/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

package demo.gantt.faces;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Stack;

import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;

import demo.gantt.faces.ContextDataBean.ContextData;
import ilog.views.faces.dhtml.event.FacesViewActionEvent;
import ilog.views.gantt.IlvActivity;
import ilog.views.gantt.IlvDuration;
import ilog.views.gantt.IlvGanttModel;
import ilog.views.gantt.IlvHierarchyChart;
import ilog.views.gantt.faces.component.data.IlvFacesGanttXMLDataSource;
import ilog.views.gantt.faces.dhtml.component.IlvFacesDHTMLGanttChartView;
import ilog.views.util.servlet.IlvMenuFactory;

/**
 * The bean that contain the logic of the JSF Gantt Chart sample.
 */
public class DemoBean {
  /**
   * Stores the {@link ContextData} holding all contextual data for the current
   * request.
   */
  private ContextData _contextData;

  /**
   * The contextual pop-up menu factory.
   */
  private IlvMenuFactory _menuFactory;

  /**
   * Constructor: Retrieves the context data for the page that originated the
   * request currently being processed.
   */
  public DemoBean() {
    // Retrieves the ContextData
    _contextData = ContextDataBean.getContextData();
    // Initialize pop-up menu factory
    _menuFactory = new DemoFactory();
  }

  /**
   * Returns the gantt model displayed.
   * 
   * @return The gantt model.
   */
  private IlvGanttModel getGanttModel() {
    return _contextData.ganttView.getChart().getGanttModel();
  }

  /**
   * Builds the activity select item list from the gantt model.
   */
  private void initActivityItems() {
    _contextData.activityItems = new ArrayList<SelectItem>();
    _contextData.activityItems.add(new SelectItem("default", ""));
    // The gantt view is not initialized with the model at this state.
    IlvGanttModel model = _contextData.xmlDS.getValue();
    IlvActivity rootActivity = model.getRootActivity();
    Stack<IlvActivity> stack = new Stack<IlvActivity>();
    stack.push(rootActivity);
    _contextData.activityById = new HashMap<String, IlvActivity>();
    while (stack.size() > 0) {
      int size = stack.size();
      for (int i = 0; i < size; i++) {
        IlvActivity activity = stack.pop();
        _contextData.activityItems.add(buildSelectItemFromActivity(activity));
        _contextData.activityById.put(activity.getID(), activity);
        int count = model.getChildActivityCount(activity);
        for (int j = 0; j < count; j++)
          stack.push(model.getChildActivity(activity, count - 1 - j));
      }
    }
  }

  /**
   * Builds a select item from an activity. The value is the activity Id and the
   * label is the activity name.
   * 
   * @param activity
   *          The activity to build a select item from.
   * @return A select item initialized with activity id and name.
   */
  private SelectItem buildSelectItemFromActivity(IlvActivity activity) {
    SelectItem item = new SelectItem();
    item.setValue(activity.getID());
    item.setLabel(activity.getName());
    return item;
  }

  /**
   * Value change listener called on a modification of the select one menu. The
   * activity is selected, visible (parent nodes are expanded and the view is
   * centered on this activity) and expanded.
   * 
   * @param event
   *          The value change event.
   */
  public void onValueChanged(ValueChangeEvent event) {
    String activityId = (String) event.getNewValue();

    if (activityId != null && !activityId.equals("default")) {

      // Find the activity form its id.
      IlvActivity activity = _contextData.activityById.get(activityId);

      // Set the start and end activity date to edit.
      _contextData.startActivityDate = new Date(activity.getStartTime().getTime());
      _contextData.endActivityDate = new Date(activity.getEndTime().getTime());

      // Set the visible interval to the activity time interval plus 2 weeks.
      centerView(_contextData.startActivityDate, _contextData.endActivityDate);

      IlvGanttModel model = getGanttModel();
      IlvHierarchyChart chart = _contextData.ganttView.getChart();

      // Collapse the previous selected activity
      IlvActivity cActivity;
      if (_contextData.currentActivity != null) {
        cActivity = _contextData.currentActivity;
        while (cActivity != model.getRootActivity()) {
          cActivity = model.getParentActivity(cActivity);
          chart.collapseRow(cActivity);
        }
        chart.collapseRow(_contextData.currentActivity);
      }

      // Make the activity visible.
      cActivity = activity;
      while (cActivity != model.getRootActivity()) {
        cActivity = model.getParentActivity(cActivity);
        chart.expandRow(cActivity);
      }
      chart.expandRow(activity);

      // Selection update.
      chart.deSelectAllRows();
      chart.selectRow(activity, true);

      _contextData.currentActivity = activity;
    }
  }

  /**
   * Expands or collapses the activity passed in the event.
   * 
   * @param event
   *          The view event.
   */
  public void expandCollapse(FacesViewActionEvent event) {
    IlvActivity activity = (IlvActivity) event.getObject();
    IlvHierarchyChart chart = (IlvHierarchyChart) event.getGraphicComponent();
    if (activity != null) {
      if (chart.isRowExpanded(activity))
        chart.collapseRow(activity);
      else
        chart.expandRow(activity);
    }
  }

  /**
   * Action called by the contextual pop-up menu. The activity is selected and
   * expanded. The selectOneMenu is updated
   * 
   * @param event
   *          The view action event.
   */
  public void nodeValueChanged(FacesViewActionEvent event) {
    selectNode((IlvActivity) event.getObject());
  }

  /**
   * Value change listener called on the value modification of the node select
   * interactor. The picked activity is selected and expanded. The selectOneMenu
   * is updated.
   * 
   * @param event
   *          The value change event.
   */
  public void nodeValueChanged(ValueChangeEvent event) {
    selectNode((IlvActivity) event.getNewValue());
  }

  private void selectNode(IlvActivity activity) {
    if (activity != null) {
      // Set the start and end activity date to edit.
      _contextData.startActivityDate = new Date(activity.getStartTime().getTime());
      _contextData.endActivityDate = new Date(activity.getEndTime().getTime());

      // Set the visible interval to the activity time interval plus 2 weeks.
      centerView(_contextData.startActivityDate, _contextData.endActivityDate);

      // Select the activity
      IlvHierarchyChart chart = _contextData.ganttView.getChart();
      chart.deSelectAllRows();
      chart.selectRow(activity, true);

      // Expand activity child nodes.
      chart.expandAllRows(activity);

      // Synchronize the select one menu.
      setActivityId(activity.getID());

      _contextData.currentActivity = activity;
    }
  }

  /**
   * Action event listener on the save change button of the editing panel. Sets
   * the start and end time to the currently selected activity and recenter the
   * view on it.
   * 
   * @param event
   *          The action event.
   * @return null.
   */
  public String changeActivityInterval(ActionEvent event) {
    if (_contextData.currentActivity != null) {
      _contextData.currentActivity.setStartTime(new Date(_contextData.startActivityDate.getTime()));
      _contextData.currentActivity.setEndTime(new Date(_contextData.endActivityDate.getTime()));
      centerView(_contextData.currentActivity.getStartTime(), _contextData.currentActivity.getEndTime());
    }
    return null;
  }

  /**
   * Centers the view displayed on the specified dates zoomed out to have a week
   * before and after.
   * 
   * @param start
   *          The start time to center on.
   * @param end
   *          The end time to center on.
   */
  private void centerView(Date start, Date end) {
    centerView(start, end, IlvDuration.ONE_WEEK);
  }

  /**
   * Center the view displayed on the specified dates zoomed out to have a the
   * <code>duration</code> before and after.
   * 
   * @param start
   *          The start time to center on.
   * @param end
   *          The end to center on.
   * @param duration
   *          The duration to have before and after the date interval.
   */
  private void centerView(Date start, Date end, IlvDuration duration) {
    Date startDate = new Date(start.getTime());
    Date endDate = new Date(end.getTime());
    startDate.setTime(startDate.getTime() - duration.getMillis());
    endDate.setTime(endDate.getTime() + duration.getMillis());
    _contextData.ganttView.setStartVisibleInterval(startDate);
    _contextData.ganttView.setEndVisibleInterval(endDate);
  }

  // Getters and Setters.

  public IlvFacesGanttXMLDataSource getXmlDS() {
    return _contextData.xmlDS;
  }

  public void setXmlDS(IlvFacesGanttXMLDataSource xmlDS) {
    _contextData.xmlDS = xmlDS;
  }

  public ArrayList<SelectItem> getActivityItems() {
    if (_contextData.activityItems == null)
      initActivityItems();
    return _contextData.activityItems;
  }

  public void setActivityItems(ArrayList<SelectItem> selectItemList) {
    _contextData.activityItems = selectItemList;
  }

  public IlvFacesDHTMLGanttChartView getGanttView() {
    return (IlvFacesDHTMLGanttChartView) _contextData.ganttView;
  }

  public void setGanttView(IlvFacesDHTMLGanttChartView ganttView) {
    _contextData.ganttView = ganttView;
  }

  public IlvMenuFactory getMenuFactory() {
    return _menuFactory;
  }

  public void setMenuFactory(IlvMenuFactory menuFactory) {
    _menuFactory = menuFactory;
  }

  public String getActivityId() {
    return _contextData.currentActivity == null ? "default" : _contextData.currentActivity.getID();
  }

  public void setActivityId(String activityId) {
    // the value change listener is reponsible to update this value
  }
}