/** @author <a href="http://community.jboss.org/people/lfryc">Lukas Fryc</a> */
public class NotifyRendererUtils {

  private static final RendererUtils UTILS = RendererUtils.getInstance();

  public static String getStackId(FacesContext facesContext, UIComponent component) {
    NotifyAttributes notify = (NotifyAttributes) component;

    if (null == notify.getStack()) {
      return null;
    }

    UIComponent stack = UTILS.findComponentFor(facesContext.getViewRoot(), notify.getStack());

    if (stack instanceof AbstractNotifyStack) {
      return stack.getClientId();
    } else {
      return null;
    }
  }

  public static void addStackIdOption(
      Map<String, Object> options, FacesContext facesContext, UIComponent component) {
    String stackId = getStackId(facesContext, component);
    if (stackId != null) {
      options.put("stackId", stackId);
    }
  }

  public static void addFacetOrAttributeAsOption(
      String name, Map<String, Object> options, FacesContext facesContext, UIComponent component) {

    UIComponent facet = component.getFacet(name);
    if (facet != null && facet.isRendered()) {
      ResponseWriter originalResponseWriter = facesContext.getResponseWriter();
      try {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        PrintWriter printWriter = new PrintWriter(outputStream);
        ResponseWriter newResponseWriter =
            facesContext.getRenderKit().createResponseWriter(printWriter, null, null);
        facesContext.setResponseWriter(newResponseWriter);
        facet.encodeAll(facesContext);
        printWriter.flush();
        String value = new String(outputStream.toByteArray());
        options.put(name, value);
      } catch (IOException e) {
        throw new FacesException(
            "Can't encode facet '"
                + name
                + "' of component '"
                + component.getClass().getName()
                + "'",
            e);
      } finally {
        facesContext.setResponseWriter(originalResponseWriter);
      }
      return;
    }

    Object attribute = component.getAttributes().get(name);
    if (attribute != null) {
      options.put(name, attribute.toString());
      return;
    }
  }
}
/**
 * @author Nick Belaevski
 * @since 4.0
 */
public final class HandlersChain {
  /** */
  private static final RendererUtils RENDERER_UTILS = RendererUtils.getInstance();
  // private static final Logger LOG = RichfacesLogger.RENDERKIT.getLogger();
  private boolean hasSubmittingBehavior = false;
  private boolean includeClientId = false;
  // TODO: review for optimization
  private List<String> handlers = new ArrayList<String>(2);
  private FacesContext facesContext;
  private UIComponent component;
  private Collection<Parameter> parameters;

  public HandlersChain(FacesContext facesContext, UIComponent component) {
    this.facesContext = facesContext;
    this.component = component;
  }

  public HandlersChain(FacesContext facesContext, UIComponent component, boolean includeClientId) {
    this.facesContext = facesContext;
    this.component = component;
    this.includeClientId = includeClientId;
  }

  public HandlersChain(
      FacesContext facesContext, UIComponent component, Collection<Parameter> parameters) {
    this.facesContext = facesContext;
    this.component = component;
    this.parameters = parameters;
  }

  public HandlersChain(
      FacesContext facesContext,
      UIComponent component,
      Collection<Parameter> parameters,
      boolean includeClientId) {
    this.facesContext = facesContext;
    this.component = component;
    this.parameters = parameters;
    this.includeClientId = includeClientId;
  }

  private static boolean isNotEmpty(String s) {
    return (s != null) && (s.length() != 0);
  }

  private List<ClientBehavior> getBehaviorsList(String behaviorName) {
    List<ClientBehavior> behaviors = null;

    if (component instanceof ClientBehaviorHolder) {
      ClientBehaviorHolder clientBehaviorHolder = (ClientBehaviorHolder) component;
      Map<String, List<ClientBehavior>> clientBehaviorsMap =
          clientBehaviorHolder.getClientBehaviors();

      if (clientBehaviorsMap != null) {
        behaviors = clientBehaviorsMap.get(behaviorName);
      }
    }

    return behaviors;
  }

  private Collection<Parameter> getParameters() {
    if (parameters == null) {
      Map<String, Object> parametersMap =
          RENDERER_UTILS.createParametersMap(facesContext, component);

      parameters = createParametersList(parametersMap);
    }

    return parameters;
  }

  public boolean hasSubmittingBehavior() {
    return hasSubmittingBehavior;
  }

  public void addInlineHandlerAsValue(String handlerValue) {
    if (isNotEmpty(handlerValue)) {
      handlers.add(handlerValue);
    }
  }

  public void addInlineHandlerFromAttribute(String attributeName) {
    addInlineHandlerAsValue((String) component.getAttributes().get(attributeName));
  }

  public void addBehaviors(String domEventName) {
    addBehaviors(domEventName, null);
  }

  public void addBehaviors(String domEventName, String logicalEventName) {
    String name = domEventName;
    List<ClientBehavior> behaviorsList = getBehaviorsList(domEventName);

    if ((behaviorsList == null) && (logicalEventName != null)) {
      behaviorsList = getBehaviorsList(logicalEventName);
      name = logicalEventName;
    }

    if (behaviorsList == null) {
      return;
    }

    ClientBehaviorContext behaviorContext =
        ClientBehaviorContext.createClientBehaviorContext(
            facesContext,
            component,
            name,
            includeClientId ? component.getClientId(facesContext) : null,
            getParameters());

    for (ClientBehavior clientBehavior : behaviorsList) {
      String behaviorScript = clientBehavior.getScript(behaviorContext);

      if (isNotEmpty(behaviorScript)) {
        if (clientBehavior.getHints().contains(ClientBehaviorHint.SUBMITTING)) {
          hasSubmittingBehavior = true;
        }

        handlers.add(behaviorScript);
      }
    }
  }

  public void addAjaxSubmitFunction() {
    if (!this.hasSubmittingBehavior()) {
      hasSubmittingBehavior = true;

      ScriptString ajaxFunction = buildAjaxFunction(facesContext, component);
      this.addInlineHandlerAsValue(ajaxFunction.toScript());
    }
  }

  public String toScript() {
    String result = null;

    if (!handlers.isEmpty()) {
      if (handlers.size() == 1) {
        result = handlers.get(0);
      } else {
        JSFunction jsFunction =
            new JSFunction("jsf.util.chain", JSReference.THIS, JSReference.EVENT);

        for (String handler : handlers) {
          jsFunction.addParameter(handler);
        }

        result = jsFunction.toScript();
      }
    }

    return result;
  }

  public static List<Parameter> createParametersList(Map<String, Object> parametersMap) {
    List<Parameter> parameters = new ArrayList<Parameter>(parametersMap.size());

    for (Entry<String, Object> entry : parametersMap.entrySet()) {
      parameters.add(new Parameter(entry.getKey(), entry.getValue()));
    }

    return parameters;
  }
}