/*
 * 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 ganttviewer.views;

import ganttviewer.LagTimeHandler;
import ganttviewer.util.GanttViewerProperties;
import ganttviewer.viewer.View;
import ganttviewer.viewer.Viewer;
import ilog.views.gantt.IlvActivity;
import ilog.views.gantt.IlvActivityFactory;
import ilog.views.gantt.IlvConstraintFactory;
import ilog.views.gantt.IlvGanttChart;
import ilog.views.gantt.IlvGanttModel;
import ilog.views.gantt.IlvHierarchyChart;
import ilog.views.gantt.IlvTimeScrollController;
import ilog.views.gantt.event.ConstraintPropertyEvent;
import ilog.views.gantt.event.SelectionEvent;
import ilog.views.gantt.event.SelectionListener;
import ilog.views.gantt.graphic.IlvConstraintGraphic;
import ilog.views.gantt.graphic.IlvConstraintGraphicLayer;
import ilog.views.gantt.graphic.IlvConstraintGraphicLayerPolicy;
import ilog.views.gantt.graphic.IlvGanttSheet;
import ilog.views.gantt.graphic.grid.IlvGanttGridRenderer;
import ilog.views.gantt.graphic.grid.IlvWeekendGrid;
import ilog.views.gantt.model.general.ConstraintUserPropertyEvent;
import ilog.views.gantt.model.general.IlvGeneralConstraint;
import ilog.views.util.styling.IlvStylingException;

import javax.swing.JComponent;

/**
 * <code>GanttView</code> is the Gantt chart view.
 */
public class GanttView extends View implements SelectionListener {

  private static final String NAME_COLUMN = "Name";

  private static final int notSatisfiedConstraintsLayerIndex = 5;

  /**
   * Activity factory.
   */
  private IlvActivityFactory activityFactory;

  /**
   * Constraint factory.
   */
  private IlvConstraintFactory constraintFactory;

  /**
   * A time scroll controller.
   */
  private IlvTimeScrollController timeScrollController;

  /**
   * Build a <code>GanttView</code>.
   * @param viewer The viewer.
   * @param iD The view's ID.
   * @param name The view's name.
   * @param timeScrollController A time scroll controller.
   * @param activityFactory An activity factory.
   * @param constraintFactory A constraint factory.
   */
  public GanttView(Viewer viewer, String iD, String name,
      IlvTimeScrollController timeScrollController,
      IlvActivityFactory activityFactory, IlvConstraintFactory constraintFactory) {
    super(viewer, iD, name);
    this.activityFactory = activityFactory;
    this.constraintFactory = constraintFactory;
    this.timeScrollController = timeScrollController;
    // set the Gantt chart.
    setMainComponent(new IlvGanttChart());
  }

  /**
   * Overridden constraint graphic layer policy. Manages two layers of constraints,
   * the default layer and one for highlighting the not satisfied constraints.
   */
  class NotSatisfiedConstraintsLayerPolicy extends
      IlvConstraintGraphicLayerPolicy {

    private IlvConstraintGraphicLayer notSatifiedConstraintsLayer;

    /**
     * Build a <code>NotSatisfiedConstraintsLayerPolicy</code>.
     * @param notSatifiedConstraintsLayer The layer.
     */
    public NotSatisfiedConstraintsLayerPolicy(
        IlvConstraintGraphicLayer notSatifiedConstraintsLayer) {
      super(getGanttChart().getGanttSheet());
      this.notSatifiedConstraintsLayer = notSatifiedConstraintsLayer;
    }

    /**
     * Adds the constraint graphic.
     * @param graphic The constraint graphic.
     */
    Override
    public void addConstraintGraphic(IlvConstraintGraphic graphic) {
      IlvGeneralConstraint constraint = (IlvGeneralConstraint) graphic
          .getConstraint();
      if (!constraint.getPropertyNames().contains(
          LagTimeHandler.CONSTRAINT_SATISFIED_PROPERTY)) {
        super.addConstraintGraphic(graphic);
        return;
      }
      if (!(Boolean) constraint
          .getProperty(LagTimeHandler.CONSTRAINT_SATISFIED_PROPERTY)) {
        notSatifiedConstraintsLayer.addConstraintGraphic(graphic);
      } else {
        super.addConstraintGraphic(graphic);
      }
    }

    /**
     * Removes all the constraint graphics.
     */
    Override
    public void removeAllConstraintGraphics() {
      super.removeAllConstraintGraphics();
      notSatifiedConstraintsLayer.removeAllConstraintGraphics();
    }

    /**
     * Updates the layer of the constraint graphic if its satisfied property has changed.
     * @param graphic The constraint graphic.
     * @param evt The event that implies the update.
     */
    Override
    public void updateConstraintGraphic(IlvConstraintGraphic graphic,
        ConstraintPropertyEvent evt) {
      IlvGanttSheet ganttSheet = getGanttChart().getGanttSheet();
      if (evt instanceof ConstraintUserPropertyEvent) {
        ConstraintUserPropertyEvent cupEvt = (ConstraintUserPropertyEvent) evt;
        if (LagTimeHandler.CONSTRAINT_SATISFIED_PROPERTY.equals(cupEvt
            .getPropertyName())) {
          if (!(Boolean) cupEvt.getNewValue()) {
            // we put the constraint on an above layer if it is satisfied
            ganttSheet.getManager().setLayer(graphic,
                notSatifiedConstraintsLayer.getIndex(), true);
          } else {
            // we put the constraint on the default layer if it now satisfied
            ganttSheet.getManager().setLayer(graphic,
                ganttSheet.getDefaultConstraintGraphicLayerIndex(), true);
          }
        }
      }
    }

  }

  /**
   * Customizes the Gantt chart before it is added.
   * @param component The chart to be added.
   * @return The chart.
   */
  Override
  protected JComponent mainComponentAboutToBeAdded(JComponent component) {
    IlvGanttChart chart = (IlvGanttChart) component;
    // add a constraint layer to the Gantt sheet
    IlvConstraintGraphicLayer notSatisfiedConstraintsLayer = new IlvConstraintGraphicLayer();
    chart.getGanttSheet().getManager().addLayer(notSatisfiedConstraintsLayer,
        GanttView.notSatisfiedConstraintsLayerIndex);
    chart.getGanttSheet().setConstraintGraphicLayerPolicy(
        new NotSatisfiedConstraintsLayerPolicy(notSatisfiedConstraintsLayer));
    // factories init.
    chart.setActivityFactory(activityFactory);
    chart.setConstraintFactory(constraintFactory);
    // time interval synchronization.
    timeScrollController.addTimeScrollable(chart);
    // listen to selection changes.
    chart.addSelectionListener(this);
    // customize the table.
    int nameWidth = Integer.parseInt(GanttViewerProperties
        .getString("Table.Column.Name.Width"));
    chart.getTable().getColumn(GanttView.NAME_COLUMN).setPreferredWidth(
        nameWidth);
    chart.setDividerLocation(nameWidth);
    // set hover highlighting
    chart.getGanttSheet().setHoverHighlightingMode(IlvGanttSheet.HH_GRAYSCALE);
    // force the weekend grid to print opaque. This reduces the print job
    // size from >60Mb to approx. 150Kb.
    IlvGanttGridRenderer verticalGrid = chart.getGanttSheet().getVerticalGrid();
    if (verticalGrid instanceof IlvWeekendGrid) {
      ((IlvWeekendGrid) verticalGrid).setPrintWeekendsOpaque(true);
    }
    // enable animation of changes to the visible time interval.
    chart.setVisibleIntervalAnimationSteps(4);
    return component;
  }

  /**
   * Attaches the Gantt data model to the view.
   * @param model The Gantt data model.
   */
  Override
  protected void attachModel(Object model) {
    IlvGanttChart chart = getGanttChart();
    chart.setGanttModel((IlvGanttModel) model);
    if (model == null) {
      return;
    }
    // expand all rows
    chart.expandAllRows();
  }

  /**
   * Returns the Gantt chart.
   * @return The Gantt chart.
   */
  public IlvGanttChart getGanttChart() {
    return (IlvGanttChart) getMainComponent();
  }

  /**
   * Indicates whether this view is stylable or not.
   * @return <code>true</code>.
   */
  Override
  public boolean isStylable() {
    return true;
  }

  /**
   * Applies the style to this view.
   * @param css The CSS.
   */
  Override
  public void applyStyle(String css) throws IlvStylingException {
    IlvHierarchyChart chart = getGanttChart();
    chart.setStyleSheet(css);
  }

  /**
   * Activity selection implementation.
   * @param obj The object to (de)select.
   * @param selected The selection status.
   */
  Override
  protected void setSelectedImpl(Object obj, boolean selected) {
    if (obj instanceof IlvActivity) {
      getGanttChart().select((IlvActivity) obj, selected);
    }
  }

  /**
   * Deselects all.
   */
  Override
  protected void deSelectAllImpl() {
    getGanttChart().deSelectAllRows();
  }

  /**
   * Listens for selection changes.
   * @param event The selection event.
   */
  Override
  public void selectionChanged(SelectionEvent event) {
    super.selectionChanged(event.getSource(), event.isObjectSelected());
  }

}