/////////////////////////////////////////////////////////////////////////////
//
// 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.plugins.teamcal.dialog;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.wicket.AttributeModifier;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
import org.apache.wicket.extensions.markup.html.form.select.IOptionRenderer;
import org.apache.wicket.extensions.markup.html.form.select.Select;
import org.apache.wicket.extensions.markup.html.form.select.SelectOptions;
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptReferenceHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.projectforge.plugins.teamcal.admin.TeamCalDO;
import org.projectforge.plugins.teamcal.admin.TeamCalDao;
import org.projectforge.plugins.teamcal.event.TeamEventDao;
import org.projectforge.plugins.teamcal.event.TeamEventRight;
import org.projectforge.plugins.teamcal.integration.TeamCalCalendarFilter;
import org.projectforge.plugins.teamcal.integration.TemplateEntry;
import org.projectforge.registry.Registry;
import org.projectforge.user.PFUserContext;
import org.projectforge.user.PFUserDO;
import org.projectforge.user.UserRights;
import org.projectforge.web.calendar.CalendarPageSupport;
import org.projectforge.web.dialog.ModalDialog;
import org.projectforge.web.fibu.ISelectCallerPage;
import org.projectforge.web.user.UserSelectPanel;
import org.projectforge.web.wicket.WicketUtils;
import org.projectforge.web.wicket.bootstrap.GridSize;
import org.projectforge.web.wicket.components.AjaxMaxLengthEditableLabel;
import org.projectforge.web.wicket.components.SingleButtonPanel;
import org.projectforge.web.wicket.flowlayout.DivPanel;
import org.projectforge.web.wicket.flowlayout.FieldsetPanel;
import org.projectforge.web.wicket.flowlayout.Heading3Panel;
import org.projectforge.web.wicket.flowlayout.SelectPanel;

import com.vaynberg.wicket.select2.Select2MultiChoice;

import de.micromata.wicket.ajax.AjaxCallback;

/**
 * @author M. Lauterbach ([email protected])
 * @author K. Reinhard ([email protected])
 * 
 */
public class TeamCalFilterDialog extends ModalDialog
{
  private static final long serialVersionUID = 8687197318833240410L;

  private final List<TeamCalDO> selectedCalendars;

  private AjaxMaxLengthEditableLabel templateName;

  // Adaption (fake) to display "Time Sheets" as selection option
  private final TeamCalDO timesheetsCalendar = new TeamCalDO();

  private final TeamCalCalendarFilter filter;

  private TeamCalCalendarFilter backupFilter;

  private TeamCalFilterDialogCalendarColorPanel calendarColorPanel;

  private Select<Integer> defaultCalendarSelect;

  private Select2MultiChoice<TeamCalDO> teamCalsChoice;

  private WebMarkupContainer optionsControls;

  private FieldsetPanel optionsFieldset;

  private WebMarkupContainer timesheetUserControls;

  private FieldsetPanel timesheetUserFieldset;

  public static final Integer TIMESHEET_CALENDAR_ID = -1;

  @SpringBean
  private TeamCalDao teamCalDao;

  private final TeamEventRight teamEventRight;

  private CalendarPageSupport calendarPageSupport;

  /**
   * @param id
   * @param titleModel
   * @param filter
   */
  public TeamCalFilterDialog(final String id, final TeamCalCalendarFilter filter)
  {
    super(id);
    this.filter = filter;
    setTitle(new ResourceModel("plugins.teamcal.calendar.filterDialog.title"));
    setBigWindow().setShowCancelButton().wantsNotificationOnClose().setEscapeKeyEnabled(false);
    selectedCalendars = new LinkedList<TeamCalDO>();
    teamEventRight = (TeamEventRight) UserRights.instance().getRight(TeamEventDao.USER_RIGHT_ID);
  }

  /**
   * @see org.apache.wicket.Component#renderHead(org.apache.wicket.markup.html.IHeaderResponse)
   */
  @Override
  public void renderHead(final IHeaderResponse response)
  {
    super.renderHead(response);
    response.render(CssHeaderItem.forUrl("scripts/spectrum/spectrum.css"));
    response.render(JavaScriptReferenceHeaderItem.forUrl("scripts/spectrum/spectrum.js"));
  }

  /**
   * @see org.projectforge.web.dialog.ModalDialog#handleCloseEvent(org.apache.wicket.ajax.AjaxRequestTarget)
   */
  @Override
  protected void handleCloseEvent(final AjaxRequestTarget target)
  {
    myClose(target);
  }

  /**
   * @see org.projectforge.web.dialog.ModalDialog#onCancelButtonSubmit(org.apache.wicket.ajax.AjaxRequestTarget)
   */
  @Override
  protected void onCancelButtonSubmit(final AjaxRequestTarget target)
  {
    // Restore values (valid at opening time of this dialog):
    filter.copyValuesFrom(backupFilter);
    close(target);
    onClose(target);
  }

  /**
   * @see org.projectforge.web.dialog.ModalDialog#onCloseButtonSubmit(org.apache.wicket.ajax.AjaxRequestTarget)
   */
  @Override
  protected boolean onCloseButtonSubmit(final AjaxRequestTarget target)
  {
    myClose(target);
    onClose(target);
    return true;
  }

  protected void onClose(final AjaxRequestTarget target)
  {
  }

  /**
   * @see org.projectforge.web.dialog.ModalDialog#init()
   */
  @SuppressWarnings("serial")
  @Override
  public void init()
  {
    init(new Form<String>(getFormId()));
    calendarPageSupport = new CalendarPageSupport((ISelectCallerPage) getPage());
    timesheetsCalendar.setTitle(getString("plugins.teamcal.timeSheetCalendar"));
    timesheetsCalendar.setId(TIMESHEET_CALENDAR_ID);
    // confirm
    setCloseButtonTooltip(null, new ResourceModel("plugins.teamcal.calendar.filterDialog.closeButton.tooltip"));
    insertNewAjaxActionButton(new AjaxCallback() {
      @Override
      public void callback(final AjaxRequestTarget target)
      {
        filter.remove(filter.getActiveTemplateEntry());
        close(target);
        onClose(target);
      }
    }, 1, getString("delete"), SingleButtonPanel.DELETE);
  }

  public TeamCalFilterDialog redraw()
  {
    clearContent();
    gridBuilder.newSplitPanel(GridSize.COL50);
    filter.sort();
    addFilterFieldset();
    gridBuilder.newSplitPanel(GridSize.COL50);
    addDefaultCalenderSelection();
    if (calendarPageSupport.isOtherTimesheetsUsersAllowed() == true) {
      gridBuilder.newSplitPanel(GridSize.COL50);
      timesheetUserFieldset = gridBuilder.newFieldset(getString("timesheet.timesheets"));
      timesheetUserControls = timesheetUserFieldset.getControlsDiv();
      timesheetUserControls.setOutputMarkupId(true);
      redrawTimesheetsUserControls();
      gridBuilder.newSplitPanel(GridSize.COL50);
    } else {
      gridBuilder.newGridPanel();
    }
    optionsFieldset = gridBuilder.newFieldset(getString("label.options")).suppressLabelForWarning();
    optionsControls = optionsFieldset.getControlsDiv();
    optionsControls.setOutputMarkupId(true);
    redrawOptionControls();
    gridBuilder.newGridPanel();
    addTeamCalsChoiceFieldset();
    final DivPanel panel = gridBuilder.getPanel();
    panel.add(new Heading3Panel(panel.newChildId(), getString("plugins.teamcal.selectColor")));
    panel.add(calendarColorPanel = new TeamCalFilterDialogCalendarColorPanel(panel.newChildId()));
    calendarColorPanel.redraw(filter.getActiveTemplateEntry(), selectedCalendars);
    return this;
  }

  @SuppressWarnings("serial")
  private void redrawTimesheetsUserControls()
  {
    if (timesheetUserFieldset != null) {
      timesheetUserFieldset.removeAllFields();
    }
    final UserSelectPanel timesheetUserSelectPanel = calendarPageSupport.addUserSelectPanel(timesheetUserFieldset,
        new PropertyModel<PFUserDO>(this, "timesheetsUser"), false);
    if (timesheetUserSelectPanel != null) {
      timesheetUserSelectPanel.getFormComponent().add(new OnChangeAjaxBehavior() {
        @Override
        protected void onUpdate(final AjaxRequestTarget target)
        {
          final PFUserDO user = (PFUserDO) timesheetUserSelectPanel.getFormComponent().getModelObject();
          setTimesheetsUser(user);
        }
      });
    }
  }

  private void redrawOptionControls()
  {
    optionsFieldset.removeAllFields();
    final DivPanel checkBoxPanel = optionsFieldset.addNewCheckBoxButtonDiv();
    calendarPageSupport.addOptions(checkBoxPanel, false, filter);
  }

  /**
   * @see org.projectforge.web.dialog.ModalDialog#open(org.apache.wicket.ajax.AjaxRequestTarget)
   */
  @Override
  public TeamCalFilterDialog open(final AjaxRequestTarget target)
  {
    backupFilter = new TeamCalCalendarFilter().copyValuesFrom(filter);
    super.open(target);
    return this;
  }

  /**
   * Opens the dialog for adding a new entry.
   */
  public TeamCalFilterDialog openNew(final AjaxRequestTarget target, final String newTemplateName)
  {
    backupFilter = new TeamCalCalendarFilter().copyValuesFrom(filter);
    final TemplateEntry newTemplate = new TemplateEntry();
    newTemplate.setName(newTemplateName);
    filter.add(newTemplate);
    super.open(target);
    return this;
  }

  private void myClose(final AjaxRequestTarget target)
  {
    if (filter.isModified(backupFilter) == false) {
      // Do nothing.
      return;
    }
    final TemplateEntry activeTemplateEntry = filter.getActiveTemplateEntry();
    if (activeTemplateEntry != null) {
      activeTemplateEntry.setDirty();
    }
    filter.setSelectedCalendar(TemplateEntry.calcCalendarStringForCalendar(activeTemplateEntry.getDefaultCalendarId()));
    setResponsePage(getPage().getClass(), getPage().getPageParameters());
  }

  @SuppressWarnings("serial")
  private void addFilterFieldset()
  {
    final FieldsetPanel fs = gridBuilder.newFieldset((String) null).setLabelSide(false).suppressLabelForWarning();
    templateName = new AjaxMaxLengthEditableLabel(fs.getAjaxEditableLabelId(), new Model<String>() {
      @Override
      public String getObject()
      {
        return filter.getActiveTemplateEntry().getName();
      }

      @Override
      public void setObject(final String value)
      {
        filter.getActiveTemplateEntry().setName(value);
      }
    }, 40) {
      /**
       * @see org.apache.wicket.Component#onInitialize()
       */
      @Override
      protected void onInitialize()
      {
        super.onInitialize();
        getEditor().add(AttributeModifier.append("style", "width: 10em;"));
        WicketUtils.setStrong(getLabel());
        WicketUtils.setFontSizeLarge(getLabel());
        WicketUtils.addEditableLabelDefaultTooltip(getLabel());
      }
    };
    templateName.setType(String.class).setOutputMarkupId(true);
    fs.add(templateName);

    final TemplateEntry activeTemplateEntry = filter.getActiveTemplateEntry();
    selectedCalendars.clear();
    if (activeTemplateEntry != null) {
      selectedCalendars.addAll(activeTemplateEntry.getCalendars());
    }
  }

  private void addTeamCalsChoiceFieldset()
  {
    final FieldsetPanel fs = gridBuilder.newFieldset(getString("plugins.teamcal.selectCalendar")).setLabelSide(false);
    // TEAMCAL CHOICE FIELD
    final TeamCalChoiceProvider teamProvider = new TeamCalChoiceProvider();
    teamCalsChoice = new Select2MultiChoice<TeamCalDO>(fs.getSelect2MultiChoiceId(), new PropertyModel<Collection<TeamCalDO>>(
        TeamCalFilterDialog.this, "selectedCalendars"), teamProvider);
    teamCalsChoice.setOutputMarkupId(true);
    teamCalsChoice.add(new AjaxFormComponentUpdatingBehavior("onChange") {
      private static final long serialVersionUID = 1L;

      @Override
      protected void onUpdate(final AjaxRequestTarget target)
      {
        final TemplateEntry activeTemplateEntry = filter.getActiveTemplateEntry();
        final Set<Integer> oldCalIds = activeTemplateEntry.getCalendarIds();
        final List<Integer> newIds = new LinkedList<Integer>();
        // add new keys
        for (final TeamCalDO calendar : selectedCalendars) {
          if (oldCalIds.contains(calendar.getId()) == false) {
            activeTemplateEntry.addNewCalendarProperties(filter, calendar.getId());
          }
          newIds.add(calendar.getId());
        }
        // delete removed keys
        for (final Integer key : oldCalIds) {
          if (newIds.contains(key) == false) {
            activeTemplateEntry.removeCalendarProperties(key);
          }
        }
        updateComponents(target);
      }
    });
    fs.add(teamCalsChoice);
  }

  @SuppressWarnings("serial")
  private void addDefaultCalenderSelection()
  {
    final FieldsetPanel fs = gridBuilder.newFieldset(getString("plugins.teamcal.defaultCalendar"));
    fs.addHelpIcon(getString("plugins.teamcal.defaultCalendar.tooltip"));
    final IOptionRenderer<Integer> renderer = new IOptionRenderer<Integer>() {

      @Override
      public String getDisplayValue(final Integer object)
      {
        if (TIMESHEET_CALENDAR_ID.equals(object)) {
          return timesheetsCalendar.getTitle();
        }
        return teamCalDao.getById(object).getTitle();
      }

      @Override
      public IModel<Integer> getModel(final Integer value)
      {
        return Model.of(value);
      }
    };

    // TEAMCAL SELECT
    defaultCalendarSelect = new Select<Integer>(fs.getSelectId(), new PropertyModel<Integer>(filter,
        "activeTemplateEntry.defaultCalendarId")) {

      /**
       * @see org.apache.wicket.Component#onBeforeRender()
       */
      @Override
      protected void onBeforeRender()
      {
        super.onBeforeRender();
        final TemplateEntry activeTemplateEntry = filter.getActiveTemplateEntry();
        final List<TeamCalDO> result = new ArrayList<TeamCalDO>();
        if (activeTemplateEntry != null) {
          for (final TeamCalDO cal : activeTemplateEntry.getCalendars()) {
            if (teamEventRight.hasUpdateAccess(PFUserContext.getUser(), cal) == true) {
              // User is allowed to insert events to this calendar:
              result.add(cal);
            }
          }
        }
        final List<Integer> filteredList = new ArrayList<Integer>();
        filteredList.add(TIMESHEET_CALENDAR_ID);
        if (result != null) {
          final Iterator<TeamCalDO> it = result.iterator();
          while (it.hasNext()) {
            final TeamCalDO teamCal = it.next();
            filteredList.add(teamCal.getId());
          }
        }
        final SelectOptions<Integer> options = new SelectOptions<Integer>(SelectPanel.OPTIONS_WICKET_ID, filteredList, renderer);
        this.addOrReplace(options);
      }
    };
    defaultCalendarSelect.setOutputMarkupId(true);
    defaultCalendarSelect.add(new OnChangeAjaxBehavior() {

      @Override
      protected void onUpdate(final AjaxRequestTarget target)
      {
        final Integer value = defaultCalendarSelect.getModelObject();
        filter.getActiveTemplateEntry().setDefaultCalendarId(value);
      }
    });
    fs.add(defaultCalendarSelect);
  }

  private void updateComponents(final AjaxRequestTarget target)
  {
    selectedCalendars.clear();
    final TemplateEntry activeTemplateEntry = filter.getActiveTemplateEntry();
    selectedCalendars.addAll(activeTemplateEntry.getCalendars());
    redrawTimesheetsUserControls();
    redrawOptionControls();
    calendarColorPanel.redraw(activeTemplateEntry, selectedCalendars);
    teamCalsChoice.modelChanged();
    target.add(teamCalsChoice, calendarColorPanel.main, templateName, defaultCalendarSelect, optionsControls);
    if (timesheetUserControls != null) {
      target.add(timesheetUserControls);
    }
  }

  public PFUserDO getTimesheetsUser()
  {
    final TemplateEntry activeTemplateEntry = filter.getActiveTemplateEntry();
    if (activeTemplateEntry == null) {
      return null;
    }
    final Integer userId = activeTemplateEntry.getTimesheetUserId();
    return userId != null ? Registry.instance().getUserGroupCache().getUser(userId) : null;
  }

  public void setTimesheetsUser(final PFUserDO user)
  {
    final TemplateEntry activeTemplateEntry = filter.getActiveTemplateEntry();
    if (activeTemplateEntry == null) {
      return;
    }
    if (user == null) {
      activeTemplateEntry.setTimesheetUserId(null);
    } else {
      activeTemplateEntry.setTimesheetUserId(user.getId());
    }
  }
}