
package de.ivu.elect.jee.presentation.dynamicform.renderer;

import java.io.IOException;
import java.util.List;
import java.util.Objects;

import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.FacesRenderer;

import org.primefaces.extensions.component.dynaform.DynaForm;
import org.primefaces.extensions.component.dynaform.DynaFormRenderer;
import org.primefaces.extensions.component.dynaform.UIDynaFormControl;
import org.primefaces.extensions.model.dynaform.AbstractDynaFormElement;
import org.primefaces.extensions.model.dynaform.DynaFormControl;
import org.primefaces.extensions.model.dynaform.DynaFormLabel;
import org.primefaces.extensions.model.dynaform.DynaFormModel;
import org.primefaces.extensions.model.dynaform.DynaFormRow;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * This overrides the default {@link org.primefaces.extensions.component.dynaform.DynaFormRenderer} to render accessible form rows as specified by the static
 * <code>/elect-common-war/src/main/webapp/WEB-INF/templates/edit.xhtml</code>.
 * </p>
 * <p>
 * NOTE that this MUST be declared in the <code>faces-config.xml</code> to take effect, cf. <a href="http://java.net/jira/browse/JAVASERVERFACES-1750">http
 * ://java.net/jira/browse/JAVASERVERFACES-1750</a>!
 * </p>
 * <p>
 * NOTE that this does NOT support facets or the expandable extended view areas, cf.
 * <a href="http://fractalsoft.net/primeext-showcase-mojarra/views/dynaForm.jsf" >http://fractalsoft.net/primeext-showcase-mojarra/views/dynaForm.jsf</a>!
 * </p>
 */
@FacesRenderer(componentFamily = AccessibleDynaFormRenderer.COMPONENT_FAMILY, rendererType = AccessibleDynaFormRenderer.RENDERER_TYPE)
public class AccessibleDynaFormRenderer extends DynaFormRenderer {

  private static final Logger LOG = LoggerFactory.getLogger(AccessibleDynaFormRenderer.class);

  public static final String COMPONENT_FAMILY = "org.primefaces.extensions.component";

  public static final String RENDERER_TYPE = "org.primefaces.extensions.component.DynaFormRenderer";

  private static final String DYNA_FORM_ELEMENT = "div";

  private static final String HTML_ATTRIBUTE_ID = "id";

  private static final String HTML_ATTRIBUTE_CLASS = "class";

  private static final String HTML_ATTRIBUTE_STYLE = "style";

  @Override
  protected void encodeMarkup(FacesContext ctx, DynaForm dynaForm, DynaFormModel dynaFormModel, boolean encodeFacet) throws IOException {
    // Prepare labels with information about corresponding control components.
    preRenderLabel(ctx, dynaForm, dynaFormModel);

    encodeDynaForm(ctx, dynaForm, dynaFormModel);
  }

  private void encodeDynaForm(FacesContext ctx, DynaForm dynaForm, DynaFormModel dynaFormModel) throws IOException {
    ResponseWriter writer = ctx.getResponseWriter();

    writer.startElement(DYNA_FORM_ELEMENT, dynaForm);
    {
      writer.writeAttribute(HTML_ATTRIBUTE_ID, dynaForm.getClientId(ctx), "id");
      writer.writeAttribute(HTML_ATTRIBUTE_STYLE, "margin: 0; padding: 0;", null);

      if (dynaForm.getStyleClass() != null) {
        writer.writeAttribute(HTML_ATTRIBUTE_CLASS, dynaForm.getStyleClass(), "styleClass");
      }

      if (dynaForm.getStyle() != null) {
        writer.writeAttribute(HTML_ATTRIBUTE_STYLE, dynaForm.getStyle(), "style");
      }

      encodeDynaFormRows(ctx, dynaForm, dynaFormModel.getRegularRows());
      encodeDynaFormRows(ctx, dynaForm, dynaFormModel.getExtendedRows());
    }

    writer.endElement(DYNA_FORM_ELEMENT);
  }

  private void encodeDynaFormRows(FacesContext ctx, DynaForm dynaForm, List<DynaFormRow> dynaFormRows) throws IOException {
    if (dynaFormRows == null || dynaFormRows.isEmpty()) {
      return;
    }

    for (DynaFormRow dynaFormRow : dynaFormRows) {
      encodeDynaFormRow(ctx, dynaForm, dynaFormRow);
    }

    dynaForm.resetData();
  }

  private void encodeDynaFormRow(FacesContext ctx, DynaForm dynaForm, DynaFormRow dynaFormRow) throws IOException {
    for (AbstractDynaFormElement element : dynaFormRow.getElements()) {
      if (!(element instanceof DynaFormLabel)) { // NOTE that the <label> element is auto-generated by the XHTML-templates!
        encodeFormControl(ctx, dynaForm, (DynaFormControl) element);
      }
    }
  }

  private void encodeFormControl(FacesContext ctx, DynaForm dynaForm, DynaFormControl control) throws IOException {
    if (existsFormControlCell(dynaForm, control)) {
      dynaForm.setData(control);
      dynaForm.getControlCell(control.getType()).encodeAll(ctx);
    }
  }

  // ELECT-12271
  private boolean existsFormControlCell(DynaForm dynaForm, DynaFormControl control) {
    if (control != null) {
      String type = control.getType();

      boolean result = type != null && dynaForm.getChildren().stream().anyMatch(child -> child instanceof UIDynaFormControl && Objects.equals(((UIDynaFormControl) child).getType(), type));

      if (!result) {
        LOG.error("ELECT-12271 - UIDynaFormControl to type {} was not found", type);
      }

      return result;
    }

    return false;
  }
}
