/**
 * @author akolonitsky
 * @author <a href="http://community.jboss.org/people/bleathem">Brian Leathem</a>
 */
@ResourceDependencies({
  @ResourceDependency(library = "javax.faces", name = "jsf.js"),
  @ResourceDependency(library = "org.richfaces", name = "jquery.js"),
  @ResourceDependency(library = "org.richfaces", name = "richfaces.js"),
  @ResourceDependency(library = "org.richfaces", name = "richfaces-queue.reslib"),
  @ResourceDependency(library = "org.richfaces", name = "common/richfaces-base-component.js"),
  @ResourceDependency(library = "org.richfaces", name = "richfaces-event.js"),
  @ResourceDependency(library = "org.richfaces", name = "toggle/togglePanel/togglePanel.js"),
  @ResourceDependency(library = "org.richfaces", name = "toggle/tabPanel/tabPanel.js"),
  @ResourceDependency(library = "org.richfaces", name = "toggle/tabPanel/tabPanel.ecss")
})
@JsfRenderer(type = "org.richfaces.ui.TabPanelRenderer", family = AbstractTabPanel.COMPONENT_FAMILY)
public class TabPanelRenderer extends TogglePanelRenderer {
  private static final RenderKitUtils.Attributes HEADER_ATTRIBUTES =
      RenderKitUtils.attributes()
          .generic("onclick", "onheaderclick", "headerclick")
          .generic("ondblclick", "onheaderdblclick", "headerdblclick")
          .generic("onmousedown", "onheadermousedown", "headermousedown")
          .generic("onmousemove", "onheadermousemove", "headermousemove")
          .generic("onmouseup", "onheadermouseup", "headermouseup");
  private static final String DIV = DIV_ELEM;
  private static final String STYLE = STYLE_ATTRIBUTE;
  private static final String CLASS = CLASS_ATTRIBUTE;

  @Override
  protected boolean isSubmitted(FacesContext context, AbstractTogglePanel panel) {
    String activePanelName = panel.getSubmittedActiveItem();
    String clientId = panel.getClientIdByName(activePanelName);
    if (clientId == null) {
      return false;
    }
    Map<String, String> parameterMap = context.getExternalContext().getRequestParameterMap();
    return parameterMap.get(clientId) != null;
  }

  @Override
  protected void doEncodeBegin(ResponseWriter w, FacesContext context, UIComponent component)
      throws IOException {
    super.doEncodeBegin(w, context, component);

    AbstractTabPanel tabPanel = (AbstractTabPanel) component;
    if (tabPanel.isHeaderPositionedTop()) {
      writeTabsLine(w, context, component);
      writeTabsLineSeparator(w, component);
    }
  }

  private void writeTabsLineSeparator(ResponseWriter writer, UIComponent component)
      throws IOException {
    writer.startElement(DIV, component);
    writer.writeAttribute(CLASS_ATTRIBUTE, "rf-tab-hdr-brd", null);
    writer.endElement(DIV);
  }

  private void writeTabsLine(ResponseWriter w, FacesContext context, UIComponent comp)
      throws IOException {
    w.startElement(DIV, comp);
    String id = comp.getClientId() + AbstractTabPanel.HEADER_META_COMPONENT;
    w.writeAttribute(ID_ATTRIBUTE, id, null);
    AbstractTabPanel tabPanel = (AbstractTabPanel) comp;
    if (tabPanel.isHeaderPositionedTop()) {
      w.writeAttribute(CLASS, "rf-tab-hdr-tabline-vis rf-tab-hdr-tabline-top", null);
    } else {
      w.writeAttribute(CLASS, "rf-tab-hdr-tabline-vis rf-tab-hdr-tabline-btm", null);
    }
    w.startElement("table", comp);
    w.writeAttribute(CLASS_ATTRIBUTE, "rf-tab-hdr-tabs", null);
    w.writeAttribute("cellspacing", "0", null);
    w.startElement(TBODY_ELEMENT, comp);
    w.startElement(TR_ELEMENT, comp);

    writeTopTabFirstSpacer(w, comp);

    writeTabLine(w, context, tabPanel);

    writeTopTabLastSpacer(w, comp);

    w.endElement(TR_ELEMENT);
    w.endElement(TBODY_ELEMENT);
    w.endElement("table");

    writeTopTabsControl(w, comp, "rf-tab-hdr-scrl-lft rf-tab-hdn", "\u00AB");
    writeTopTabsControl(w, comp, "rf-tab-hdr-tablst rf-tab-hdn", "\u2193");
    writeTopTabsControl(w, comp, "rf-tab-hdr-scrl-rgh rf-tab-hdn", "\u00BB");

    w.endElement(DIV_ELEM);
  }

  private void writeTabLine(
      final ResponseWriter w, final FacesContext context, final AbstractTabPanel panel)
      throws IOException {
    panel.visitTogglePanelItems(
        panel,
        new TogglePanelVisitCallback() {
          @Override
          public VisitResult visit(FacesContext context, TogglePanelVisitState visitState) {
            AbstractTogglePanelItemInterface item = visitState.getItem();
            if (item.isRendered() && item instanceof AbstractTab) {
              try {
                AbstractTab tab = (AbstractTab) item;
                writeTopTabHeader(context, w, tab);
                writeTopTabSpacer(w, panel);
              } catch (IOException e) {
                throw new FacesException(e);
              }
            }
            return VisitResult.ACCEPT;
          }
        });
  }

  @Override
  protected String getStyle(UIComponent component) {
    return attributeAsString(component, "style");
  }

  @Override
  protected String getStyleClass(UIComponent component) {
    return HtmlUtil.concatClasses("rf-tbp", attributeAsString(component, "styleClass"));
  }

  private void writeTopTabHeader(FacesContext context, ResponseWriter writer, AbstractTab tab)
      throws IOException {
    boolean isActive = tab.isActive();
    boolean isDisabled = tab.isDisabled();
    // TODO: Ilya, review. Much HTML because we always encoding all states. Need to optimize
    // somehow.
    encodeTabHeader(context, tab, writer, inactive, !isActive && !isDisabled);
    encodeTabHeader(context, tab, writer, active, isActive && !isDisabled);
    encodeTabHeader(context, tab, writer, disabled, isDisabled);
  }

  private String positionAbbreviation(AbstractTab tab) {
    if (tab.getParentPanel().isHeaderPositionedTop()) {
      return "top";
    } else {
      return "btm";
    }
  }

  @Override
  public void encodeMetaComponent(
      FacesContext context, UIComponent component, String metaComponentId) throws IOException {
    if (AbstractTabPanel.HEADER_META_COMPONENT.equals(metaComponentId)) {
      AbstractTabPanel panel = (AbstractTabPanel) component;
      PartialResponseWriter w = context.getPartialViewContext().getPartialResponseWriter();
      String id = component.getClientId() + AbstractTabPanel.HEADER_META_COMPONENT;
      w.startUpdate(id);
      writeTabsLine(w, context, panel);
      w.endUpdate();
    } else {
      super.encodeMetaComponent(context, component, metaComponentId);
    }
  }

  private void encodeTabHeader(
      FacesContext context,
      AbstractTab tab,
      ResponseWriter writer,
      AbstractTogglePanelTitledItem.HeaderStates state,
      Boolean isDisplay)
      throws IOException {

    String headerStateClass = "rf-tab-hdr-" + state.abbreviation();
    String headerPositionClass = "rf-tab-hdr-" + positionAbbreviation(tab);

    writer.startElement(TD_ELEM, tab);
    writer.writeAttribute(
        ID_ATTRIBUTE, tab.getClientId(context) + ":header:" + state.toString(), null);
    renderPassThroughAttributes(context, tab, HEADER_ATTRIBUTES);
    writer.writeAttribute(
        CLASS_ATTRIBUTE,
        concatClasses(
            "rf-tab-hdr",
            headerStateClass,
            headerPositionClass,
            attributeAsString(tab, "headerClass"),
            attributeAsString(tab, state.headerClass())),
        null);
    writer.writeAttribute(
        STYLE_ATTRIBUTE,
        concatStyles(isDisplay ? "" : "display : none", attributeAsString(tab, "headerStyle")),
        null);

    writer.startElement(SPAN_ELEM, tab);
    writer.writeAttribute(CLASS_ATTRIBUTE, "rf-tab-lbl", null);

    UIComponent headerFacet = tab.getHeaderFacet(state);
    if (headerFacet != null && headerFacet.isRendered()) {
      headerFacet.encodeAll(context);
    } else {
      Object headerText = tab.getAttributes().get("header");
      if (headerText != null && !headerText.equals("")) {
        writer.writeText(headerText, null);
      }
    }

    writer.endElement(SPAN_ELEM);

    writer.endElement(TD_ELEM);
  }

  private void writeTopTabsControl(ResponseWriter w, UIComponent comp, String styles, String text)
      throws IOException {
    w.startElement(DIV_ELEM, comp);
    w.writeAttribute(CLASS_ATTRIBUTE, styles, null);
    w.writeText(text, null);
    w.endElement(DIV_ELEM);
  }

  private void writeTopTabFirstSpacer(ResponseWriter w, UIComponent comp) throws IOException {
    AbstractTabPanel tabPanel = (AbstractTabPanel) comp;
    if (tabPanel.isHeaderAlignedLeft()) {
      writeTopTabSpacer(w, comp, "padding-left: 5px;", "rf-tab-hdr-spcr");
    } else {
      writeTopTabSpacer(w, comp, "padding-left: 5px; width:100%", "rf-tab-hdr-spcr");
    }
  }

  private void writeTopTabSpacer(ResponseWriter w, UIComponent comp) throws IOException {
    writeTopTabSpacer(w, comp, "", "rf-tab-hdr-spcr rf-tab-hortab-tabspcr-wdh");
  }

  private void writeTopTabLastSpacer(ResponseWriter w, UIComponent comp) throws IOException {
    AbstractTabPanel tabPanel = (AbstractTabPanel) comp;
    if (tabPanel.isHeaderAlignedLeft()) {
      writeTopTabSpacer(w, comp, "padding-right: 5px; width: 100%;", "rf-tab-hdr-spcr");
    } else {
      writeTopTabSpacer(w, comp, "padding-right: 5px;", "rf-tab-hdr-spcr");
    }
  }

  private void writeTopTabSpacer(
      ResponseWriter w, UIComponent comp, String style, String styleClass) throws IOException {
    w.startElement(TD_ELEM, comp);
    w.writeAttribute(STYLE, style, null);
    w.writeAttribute(CLASS, styleClass, null);
    w.write("<br />");
    w.endElement(TD_ELEM);
  }

  @Override
  protected void doEncodeEnd(
      final ResponseWriter writer, FacesContext context, UIComponent component) throws IOException {
    AbstractTabPanel panel = (AbstractTabPanel) component;
    if (!panel.isHeaderPositionedTop()) {
      writeTabsLineSeparator(writer, component);
      writeTabsLine(writer, context, component);
    }
    writer.endElement(HtmlConstants.DIV_ELEM);
  }

  @Override
  protected JSObject getScriptObject(FacesContext context, UIComponent component) {
    return new JSObject(
        "RichFaces.ui.TabPanel",
        component.getClientId(context),
        getScriptObjectOptions(context, component));
  }

  @Override
  protected Map<String, Object> getScriptObjectOptions(
      FacesContext context, UIComponent component) {
    Map<String, Object> options = super.getScriptObjectOptions(context, component);

    options.put("isKeepHeight", attributeAsString(component, "height").length() > 0);

    return options;
  }

  @Override
  protected Class<? extends UIComponent> getComponentClass() {
    return AbstractTabPanel.class;
  }
}