/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
//         www.projectforge.org
//
// Copyright (C) 2001-2014 Kai Reinhard ([email protected])
//
// ProjectForge is dual-licensed.
//
// This community edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 3 of the License.
//
// This community edition is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see http://www.gnu.org/licenses/.
//
/////////////////////////////////////////////////////////////////////////////

package org.projectforge.web.wicket.flowlayout;

import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.ListMultipleChoice;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;
import org.projectforge.web.CSSColor;
import org.projectforge.web.wicket.WicketUtils;
import org.projectforge.web.wicket.bootstrap.GridPanel;
import org.projectforge.web.wicket.components.JiraIssuesPanel;

/**
 * Represents a entry of a group panel. This can be a label, text field or other form components.
 * @author Kai Reinhard ([email protected])
 * 
 */
public class FieldsetPanel extends AbstractFieldsetPanel<FieldsetPanel>
{
  public static final String FIELD_SET_CLASS = "control-group";

  public static final String LABEL_SUFFIX_ID = "labelSuffix";

  public static final String DESCRIPTION_SUFFIX_ID = "descriptionSuffix";

  private static final long serialVersionUID = -6318707656650110365L;

  private final WebMarkupContainer controls;

  private RepeatingView iconContainer;

  private Label feedbackMessageLabel;

  private String feedbackMessage;

  private Component labelSuffix, descriptionSuffix;

  private boolean initialized;

  private int childCounter = 0;

  /**
   * Adds this FieldsetPanel to the parent panel.
   * @param parent
   * @param label
   */
  public FieldsetPanel(final DivPanel parent, final FieldProperties< ? > fieldProperties)
  {
    this(parent, getString(parent, fieldProperties.getLabel()), //
        getString(parent, fieldProperties.getLabelDescription(), fieldProperties.isTranslateLabelDecsription()));
  }

  private static String getString(final Component parent, final String label)
  {
    return getString(parent, label, true);
  }

  private static String getString(final Component parent, final String label, final boolean translate)
  {
    if (translate == false || label == null) {
      return label;
    }
    return parent.getString(label);
  }

  /**
   * Adds this FieldsetPanel to the parent panel.
   * @param parent
   * @param label
   */
  public FieldsetPanel(final DivPanel parent, final String label)
  {
    this(parent, label, null);
  }

  /**
   * Adds this FieldsetPanel to the parent panel.
   * @param parent
   * @param label
   * @param description Description below or beside the label of the field-set.
   */
  public FieldsetPanel(final DivPanel parent, final String labelText, final String description)
  {
    this(parent.newChildId(), labelText, description);
    parent.add(this);
  }

  /**
   * Adds this FieldsetPanel to the parent panel.
   * @param parent
   * @param label
   * @param description Description below or beside the label of the field-set.
   */
  public FieldsetPanel(final GridPanel parent, final String labelText)
  {
    this(parent.newChildId(), labelText);
    parent.add(this);
  }

  /**
   */
  public FieldsetPanel(final String id, final String labeltext)
  {
    this(id, labeltext, null);
  }

  /**
   * @param id
   * @param labeltext If null, then the label field is invisible.
   * @param description
   */
  @SuppressWarnings("serial")
  public FieldsetPanel(final String id, final String labeltext, final String description)
  {
    super(id);
    this.labelText = labeltext;
    fieldset = new WebMarkupContainer("fieldset");
    superAdd(fieldset);
    fieldset.add(AttributeModifier.append("class", FIELD_SET_CLASS));
    fieldset.add((label = new WebMarkupContainer("label")));
    if (labelText != null) {
      label.add(new Label("labeltext", new Model<String>() {
        @Override
        public String getObject()
        {
          return labelText;
        };
      }).setRenderBodyOnly(true));
      if (description != null) {
        label.add(new Label("labeldescription", description));
      } else {
        label.add(WicketUtils.getInvisibleComponent("labeldescription"));
      }
    } else {
      label.setVisible(false);
    }
    fieldset.add(controls = new WebMarkupContainer("controls"));
    controls.add(feedbackMessageLabel = new Label("feedbackMessage", new Model<String>() {
      /**
       * @see org.apache.wicket.model.Model#getObject()
       */
      @Override
      public String getObject()
      {
        return feedbackMessage;
      }
    }) {
      /**
       * @see org.apache.wicket.Component#isVisible()
       */
      @Override
      public boolean isVisible()
      {
        return feedbackMessage != null;
      }
    });
    fieldsRepeater = new RepeatingView("fields");
    controls.add(fieldsRepeater);
  }

  /**
   * Please note: only labelSide=false is supported and shouldn't be called twice. The label is placed above the input fields. Default is
   * labelSide = true.
   * @param labelSide
   */
  public FieldsetPanel setLabelSide(final boolean labelSide)
  {
    if (labelSide == false) {
      fieldset.add(AttributeModifier.append("class", "vertical"));
    }
    return this;
  }

  /**
   * @param labelSuffix the labelSuffix to set
   * @return this for chaining.
   */
  public FieldsetPanel setLabelSuffix(final Component labelSuffix)
  {
    this.labelSuffix = labelSuffix;
    label.add(labelSuffix);
    return this;
  }

  /**
   * @param descriptionSuffix the descriptionSuffix to set
   * @return this for chaining.
   */
  public FieldsetPanel setDescriptionSuffix(final Component descriptionSuffix)
  {
    this.descriptionSuffix = descriptionSuffix;
    label.add(descriptionSuffix);
    return this;
  }

  /**
   * No wrap of the multiple children.
   * @return this for chaining.
   */
  public FieldsetPanel setNowrap()
  {
    // fieldDiv.add(AttributeModifier.append("style", "white-space: nowrap;"));
    return this;
  }

  /**
   * @see org.projectforge.web.wicket.flowlayout.AbstractFieldsetPanel#modifyAddedChild(org.apache.wicket.Component)
   */
  @Override
  protected void modifyAddedChild(final Component child)
  {
    if (child instanceof InputPanel) {
      final InputPanel inputPanel = (InputPanel) child;
      if (inputPanel.getField() instanceof TextField) {
        inputPanel.getField().add(AttributeModifier.append("class", "text"));
      }
    }
  }

  /**
   * @param id
   * @param label
   * @param listChoice
   * @return The created ListMultipleChoicePanel.
   * @see ListMultipleChoicePanel#ListMultipleChoicePanel(String, String, ListMultipleChoice)
   */
  public <T> ListMultipleChoicePanel<T> add(final ListMultipleChoice<T> listChoice)
  {
    final ListMultipleChoicePanel<T> listChoicePanel = new ListMultipleChoicePanel<T>(newChildId(), listChoice);
    listChoicePanel.getListMultipleChoice().setLabel(new Model<String>(getLabel()));
    add(listChoicePanel);
    return listChoicePanel;
  }

  /**
   * @return The Wicket id of the embedded text fiel of {@link ListMultipleChoicePanel}.
   */
  public String getListChoiceId()
  {
    return ListMultipleChoicePanel.WICKET_ID;
  }

  /**
   * @param model
   * @param labelString
   * @return The created CheckBoxPanel.
   * @see CheckBoxPanel#CheckBoxPanel(String, IModel, String)
   */
  public CheckBoxPanel addCheckBox(final IModel<Boolean> model, final String labelString)
  {
    return addCheckBox(model, labelString, null);
  }

  /**
   * @param model
   * @param labelString
   * @return The created CheckBoxPanel.
   * @see CheckBoxPanel#CheckBoxPanel(String, IModel, String)
   */
  public CheckBoxPanel addCheckBox(final IModel<Boolean> model, final String labelString, final String tooltip)
  {
    final CheckBoxPanel checkBox = new CheckBoxPanel(newChildId(), model, labelString);
    if (tooltip != null) {
      checkBox.setTooltip(tooltip);
    }
    add(checkBox);
    return checkBox;
  }

  /**
   * Adds an alert icon at the top left corner of the field set label.
   * @param tooltip
   * @return this for chaining.
   */
  public FieldsetPanel addAlertIcon(final String tooltip)
  {
    final IconPanel icon = WicketUtils.getAlertTooltipIcon(this, new ResourceModel("common.attention"), Model.of(tooltip));
    add(icon, FieldSetIconPosition.TOP_LEFT);
    return this;
  }

  /**
   * Adds a help icon at the top right corner of the field set.
   * @param title
   * @param tooltip
   * @return The created IconPanel.
   */
  public IconPanel addHelpIcon(final IModel<String> title, final IModel<String> tooltip)
  {
    return addHelpIcon(title, tooltip, FieldSetIconPosition.TOP_RIGHT);
  }

  /**
   * Adds a help icon at the top right corner of the field set.
   * @param title
   * @param tooltip
   * @param iconPosition
   * @return The created IconPanel.
   */
  public IconPanel addHelpIcon(final IModel<String> title, final IModel<String> tooltip, final FieldSetIconPosition iconPosition)
  {
    final IconPanel icon = new IconPanel(newIconChildId(), IconType.HELP, title, tooltip).setColor(CSSColor.GRAY);
    add(icon, iconPosition);
    return icon;
  }

  /**
   * Adds a help icon at the top right corner of the field set.
   * @param tooltip
   * @return The created IconPanel.
   */
  public IconPanel addHelpIcon(final String tooltip)
  {
    return addHelpIcon(tooltip, FieldSetIconPosition.TOP_RIGHT);
  }

  /**
   * Adds a help icon at the top right corner of the field set.
   * @param tooltip
   * @return The created IconPanel.
   */
  public IconPanel addHelpIcon(final String tooltip, final FieldSetIconPosition iconPosition)
  {
    final IconPanel icon = new IconPanel(newIconChildId(), IconType.HELP, tooltip).setColor(CSSColor.GRAY);
    add(icon, iconPosition);
    return icon;
  }

  /**
   * Adds a help icon at the top right corner of the field set.
   * @param tooltip
   * @return The created IconPanel.
   */
  public IconPanel addHelpIcon(final IModel<String> tooltip)
  {
    final IconPanel icon = new IconPanel(newIconChildId(), IconType.HELP, tooltip).setColor(CSSColor.GRAY);
    add(icon, FieldSetIconPosition.TOP_RIGHT);
    return icon;
  }

  /**
   * Adds a keyboard icon at the bottom right corner of the field set.
   * @param tooltip
   * @return this for chaining.
   */
  public FieldsetPanel addKeyboardHelpIcon(final String tooltip)
  {
    return add(new IconPanel(newIconChildId(), IconType.KEYBOARD, tooltip).setColor(CSSColor.GRAY), FieldSetIconPosition.BOTTOM_RIGHT);
  }

  /**
   * Adds a keyboard icon at the bottom right corner of the field set.
   * @param tooltip
   * @return this for chaining.
   */
  public FieldsetPanel addKeyboardHelpIcon(final IModel<String> title, final IModel<String> tooltip)
  {
    return add(new IconPanel(newIconChildId(), IconType.KEYBOARD, title, tooltip).setColor(CSSColor.GRAY),
        FieldSetIconPosition.BOTTOM_RIGHT);
  }

  /**
   * Adds a JIRA icon at the bottom right corner of the field set (only if JIRA is configured, otherwise this method does nothing). This
   * method is automatically called by {@link #addJIRAField()}.
   * @param tooltip
   * @return this for chaining.
   */
  public FieldsetPanel addJIRASupportHelpIcon()
  {
    if (WicketUtils.isJIRAConfigured() == true) {
      return add(WicketUtils.getJIRASupportTooltipIcon(this).setColor(CSSColor.GRAY), FieldSetIconPosition.TOP_RIGHT);
    } else {
      // No JIRA configured.
      return this;
    }
  }

  /**
   * Adds a JIRA icon at the bottom right corner of the field set (only if JIRA is configured, otherwise this method does nothing).
   * @param tooltip
   * @return this for chaining.
   */
  public FieldsetPanel addJIRAField(final IModel<String> model)
  {
    if (WicketUtils.isJIRAConfigured() == true) {
      add(new JiraIssuesPanel(newChildId(), model));
      add(WicketUtils.getJIRASupportTooltipIcon(this).setColor(CSSColor.GRAY), FieldSetIconPosition.TOP_RIGHT);
    }
    return this;
  }

  public FieldsetPanel add(final IconPanel icon, final FieldSetIconPosition iconPosition)
  {
    icon.getDiv().add(AttributeModifier.append("class", iconPosition.getStyleAttrValue()));
    if (iconContainer == null) {
      throw new IllegalArgumentException("No icons container given! May-be you forget to call newIconChildId() before adding an image.");
    }
    iconContainer.add(icon).setRenderBodyOnly(true);
    return this;
  }

  public String newIconChildId()
  {
    if (iconContainer == null) {
      iconContainer = new RepeatingView("icons");
      fieldset.add(iconContainer);
    }
    return iconContainer.newChildId();
  }

  /**
   * @return the feedbackMessage
   */
  public Label getFeedbackMessageLabel()
  {
    return feedbackMessageLabel;
  }

  /**
   * @param feedbackMessage
   * @return this for chaining.
   */
  public FieldsetPanel setFeedbackMessage(final String feedbackMessage)
  {
    this.feedbackMessage = feedbackMessage;
    return this;
  }

  /**
   * Creates and add a new RepeatingView as div-child if not already exist.
   * @see RepeatingView#newChildId()
   */
  @Override
  public String newChildId()
  {
    if (childCounter++ == 1) {
      controls.add(AttributeModifier.append("class", "controls-row"));
    }
    return fieldsRepeater.newChildId();
  }

  public FieldsetPanel removeAllFields()
  {
    fieldsRepeater.removeAll();
    return this;
  }

  public FieldsetPanel setDivStyle(final DivType divType)
  {
    this.controls.add(AttributeModifier.append("class", divType.getClassAttrValue()));
    return this;
  }

  public DivPanel addNewCheckBoxButtonDiv()
  {
    final DivPanel checkBoxDiv = new DivPanel(newChildId(), DivType.BTN_GROUP);
    add(checkBoxDiv);
    return checkBoxDiv;
  }

  public DivPanel addNewRadioBoxButtonDiv()
  {
    final DivPanel radioBoxDiv = new DivPanel(newChildId(), DivType.BTN_GROUP);
    add(radioBoxDiv);
    return radioBoxDiv;
  }

  /**
   * @see org.projectforge.web.wicket.flowlayout.AbstractFieldsetPanel#onBeforeRender()
   */
  @Override
  protected void onBeforeRender()
  {
    if (initialized == false) {
      // Can't be done in onInitialize() because this component is added to its parent in constructor and onInitialize() is some times
      // called before the setter methods (e. g. for labelSide) are called.
      initialized = true;
      if (labelSuffix == null) {
        label.add(labelSuffix = WicketUtils.getInvisibleComponent("labelSuffix"));
      }
      if (descriptionSuffix == null) {
        label.add(descriptionSuffix = WicketUtils.getInvisibleComponent("descriptionSuffix"));
      }
      if (iconContainer == null) {
        fieldset.add(new WebMarkupContainer("icons").setVisible(false));
      }
    }
    super.onBeforeRender();
  }

  /**
   * @see org.projectforge.web.wicket.flowlayout.AbstractFieldsetPanel#addChild(org.apache.wicket.Component[])
   */
  @Override
  protected MarkupContainer addChild(final Component... childs)
  {
    return controls.add(childs);
  }

  public WebMarkupContainer getControlsDiv()
  {
    return controls;
  }

  /**
   * @see org.projectforge.web.wicket.flowlayout.AbstractFieldsetPanel#getThis()
   */
  @Override
  protected FieldsetPanel getThis()
  {
    return this;
  }

  public boolean hasChilds()
  {
    return childCounter > 0;
  }
}