/*
 * 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 dualChart;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.Date;

import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;

import ilog.views.gantt.IlvActivity;
import ilog.views.gantt.IlvDuration;
import ilog.views.gantt.IlvGanttChart;
import ilog.views.gantt.IlvGanttModel;
import ilog.views.gantt.IlvHierarchyChart;
import ilog.views.gantt.IlvResource;
import ilog.views.gantt.IlvScheduleChart;
import ilog.views.gantt.IlvTimeInterval;
import ilog.views.gantt.IlvTimeScrollController;
import ilog.views.gantt.IlvTimeUtil;
import ilog.views.gantt.action.IlvAction;
import ilog.views.gantt.action.IlvZoomToFitAction;
import ilog.views.gantt.event.ChartDividerEvent;
import ilog.views.gantt.event.ChartDividerListener;
import ilog.views.gantt.graphic.grid.IlvWeekendGrid;
import ilog.views.gantt.model.IlvDefaultGanttModel;
import ilog.views.gantt.swing.IlvJTable;
import ilog.views.util.IlvProductUtil;
import shared.AbstractExample;
import shared.GanttCommand;
import shared.data.GeneralActivityFactory;
import shared.data.GeneralResourceFactory;
import shared.data.SimpleEngineeringProject;
import shared.swing.ExampleFrame;
import xml.XMLGanttActions;

/**
 * An example of synchronized Gantt and Schedule charts that display the same
 * data model.
 */
public class DualChartExample extends AbstractExample {

  /**
   * The Gantt chart.
   */
  protected IlvGanttChart gantt;

  /**
   * The Schedule chart.
   */
  protected IlvScheduleChart schedule;

  /**
   * The data model used for the charts.
   */
  protected IlvGanttModel model;

  /**
   * The various menu and toolbar actions.
   */
  protected IlvAction newAction;
  protected IlvAction openAction;
  protected IlvAction saveAsAction;
  protected IlvAction exitAction;
  protected IlvAction insertActivityRowAction;
  protected IlvAction deleteActivityRowAction;
  protected IlvAction makeConstraintAction;
  protected IlvAction deleteConstraintAction;
  protected IlvAction insertResourceRowAction;
  protected IlvAction deleteResourceRowAction;
  protected IlvAction makeReservationAction;
  protected IlvAction deleteReservationAction;
  protected IlvAction deleteAllAction;
  protected IlvAction rowExpandCollapseAction;
  protected IlvZoomToFitAction zoomToFitAction;
  protected IlvAction increaseRowHeightAction;
  protected IlvAction decreaseRowHeightAction;

  {
    // This sample uses JViews Gantt features. When deploying an
    // application that includes this code, you need to be in possession
    // of a Perforce JViews Gantt Deployment license.
    IlvProductUtil.DeploymentLicenseRequired(IlvProductUtil.JViews_Gantt_Deployment);
  }

  /**
   * Initializes the user interface of the example in the specified container.
   * 
   * @param container
   *          The container that the example is running in. This will be the
   *          <code>contentPane</code> of the <code>JApplet</code> or the
   *          <code>JFrame</code>, depending on whether the example is run as an
   *          applet or as an application.
   */
  Override
  public void init(Container container) {
    super.init(container);
    container.setLayout(new BorderLayout());

    // Create the charts.
    gantt = new IlvGanttChart();
    schedule = new IlvScheduleChart();

    // Customize the data model factories and graphic renderers of the charts.
    customizeFactories();

    // Create the Gantt data model.
    model = createGanttModel();

    // Set the data model of the charts.
    gantt.setGanttModel(model);
    schedule.setGanttModel(model);

    // Create the horizontal split pane and its 2 panels.
    JPanel ganttPanel = new JPanel(new BorderLayout());
    JPanel schedulePanel = new JPanel(new BorderLayout());
    JSplitPane horizontalSplitter = new JSplitPane(JSplitPane.VERTICAL_SPLIT, ganttPanel, schedulePanel);
    horizontalSplitter.setResizeWeight(0.5);
    horizontalSplitter.setOneTouchExpandable(true);

    // If the example is running as an applet, the container will have a defined
    // fixed height already. In this case, we set the divider location to the
    // approximate mid-point, not counting the menu bar, toolbar, or status bar
    // components. If the example is running as an application, the container
    // will not have a defined size yet. In this case, the preferred sizes of
    // the gantt and schedule charts will be respected when the frame is packed.
    int containerHeight = container.getHeight();
    if (containerHeight > 0) {
      horizontalSplitter.setDividerLocation(containerHeight / 2);
    }

    // Customize the appearance of the charts.
    customizeCharts();

    // Synchronize the behavior of the charts.
    synchronizeCharts(gantt, schedule);

    // Create and install the menu bar.
    JMenuBar menuBar = createMenuBar();
    setJMenuBar(menuBar);

    // Create the status bar.
    JLabel status = createStatusBar();

    // Create the toolbars.
    JToolBar mainToolBar = createMainToolBar();
    JToolBar ganttToolBar = createGanttToolBar();
    JToolBar scheduleToolBar = createScheduleToolBar();

    ganttPanel.add(ganttToolBar, BorderLayout.LINE_START);
    ganttPanel.add(gantt, BorderLayout.CENTER);
    schedulePanel.add(scheduleToolBar, BorderLayout.LINE_START);
    schedulePanel.add(schedule, BorderLayout.CENTER);
    container.add(horizontalSplitter, BorderLayout.CENTER);
    JPanel toolbarPanel = new JPanel();
    toolbarPanel.add(mainToolBar);
    container.add(toolbarPanel, BorderLayout.NORTH);
    container.add(status, BorderLayout.SOUTH);
  }

  /**
   * Customizes the data model factories and graphic renderers of the charts.
   */
  protected void customizeFactories() {
    // Change the default activity and resource factories of the Gantt chart
    // to ones that sequentially number the activities and resources.
    gantt.setActivityFactory(new GeneralActivityFactory(gantt));
    gantt.setResourceFactory(new GeneralResourceFactory(gantt));

    // Set the Schedule chart to use the same data factories as the Gantt chart.
    schedule.setActivityFactory(gantt.getActivityFactory());
    schedule.setResourceFactory(gantt.getResourceFactory());
    schedule.setConstraintFactory(gantt.getConstraintFactory());
    schedule.setReservationFactory(gantt.getReservationFactory());
  }

  /**
   * Customizes the appearance of the Gantt and Schedule charts. The charts are
   * not yet synchronized when this method is invoked.
   */
  protected void customizeCharts() {
    // Expand several activities in the Gantt chart so their children
    // are visible.
    IlvActivity ganttRoot = model.getRootActivity();
    for (int i = 0; i <= 1; i++) {
      if (model.getChildActivityCount(ganttRoot) > i) {
        gantt.expandAllRows(model.getChildActivity(ganttRoot, i));
      }
    }

    // Expand several resources in the Schedule chart so their children
    // are visible.
    IlvResource scheduleRoot = model.getRootResource();
    for (int i = 0; i <= 1; i++) {
      if (model.getChildResourceCount(scheduleRoot) > i) {
        schedule.expandAllRows(model.getChildResource(scheduleRoot, i));
      }
    }

    // Default column widths are 75. Resize some of the columns so that the
    // text is not truncated.
    IlvJTable table;
    table = gantt.getTable();
    table.getColumn("Name").setPreferredWidth(175);
    table.getColumn("Start").setPreferredWidth(90);
    table.getColumn("End").setPreferredWidth(90);
    table = schedule.getTable();
    table.getColumn("Name").setPreferredWidth(150);
    table.getColumn("ID").setPreferredWidth(60);
    table.getColumn("Qty").setPreferredWidth(40);

    // Set the initially displayed time span to start 1 day before the beginning
    // of the schedule and extend for 3 weeks. Later, the Schedule chart will be
    // synchronized to the Gantt chart, so we only need to set the time span for
    // the Gantt chart here.
    Date visibleStartTime = IlvTimeUtil.subtract(model.getRootActivity().getStartTime(), IlvDuration.ONE_DAY);
    gantt.setVisibleTime(visibleStartTime);
    gantt.setVisibleDuration(IlvDuration.ONE_WEEK.multiply(3));

    // Use a vertical grid in both charts that colors the weekends.
    IlvWeekendGrid grid = new IlvWeekendGrid();
    gantt.getGanttSheet().setVerticalGrid(grid);
    grid = new IlvWeekendGrid();
    schedule.getGanttSheet().setVerticalGrid(grid);
  }

  /**
   * Synchronizes the behavior of the second specified chart to that of the
   * first specified chart.
   * 
   * @param chart1
   *          The primary chart.
   * @param chart2
   *          The secondary chart that will be synchronized with the primary
   *          chart.
   */
  protected void synchronizeCharts(final IlvHierarchyChart chart1, final IlvHierarchyChart chart2) {
    // Use an IlvTimeScrollController to synchronize the visible time intervals
    // of the two charts. This will only give the right effect if we also ensure
    // that both charts always have their vertical scroll bar visible.
    IlvTimeInterval visibleInterval = chart1.getVisibleInterval();
    IlvTimeScrollController timeController = new IlvTimeScrollController();
    timeController.addTimeScrollable(chart1);
    timeController.addTimeScrollable(chart2);
    timeController.setVisibleInterval(visibleInterval.getStart(), visibleInterval.getDuration());
    chart1.setVerticalScrollBarPolicy(IlvHierarchyChart.VERTICAL_SCROLLBAR_ALWAYS);
    chart2.setVerticalScrollBarPolicy(IlvHierarchyChart.VERTICAL_SCROLLBAR_ALWAYS);

    // Synchronize the vertical splitters of both charts.
    chart1.addChartDividerListener(new ChartDividerListener() {
      Override
      public void chartDividerLocationChanged(ChartDividerEvent evt) {
        int location = evt.getDividerLocation();
        if (location != chart2.getDividerLocation()) {
          chart2.setDividerLocation(location);
        }
      }
    });
    chart2.addChartDividerListener(new ChartDividerListener() {
      Override
      public void chartDividerLocationChanged(ChartDividerEvent evt) {
        int location = evt.getDividerLocation();
        if (location != chart1.getDividerLocation()) {
          chart1.setDividerLocation(location);
        }
      }
    });
  }

  /**
   * Creates the menu bar.
   *
   * @return The menu bar.
   */
  public JMenuBar createMenuBar() {
    JMenuBar menu = new JMenuBar();
    menu.add(createFileMenu());
    menu.add(createEditMenu());
    menu.add(createRowMenu());
    menu.add(createLAFMenu());
    return menu;
  }

  /**
   * Creates the "File" menu.
   *
   * @return The File menu.
   */
  public JMenu createFileMenu() {
    JMenu menu = new JMenu("File");
    menu.setMnemonic(KeyEvent.VK_F);
    setStatusText(menu, "File operations");

    // Menu item for "New".
    newAction = new GanttCommand.NewDataModelAction(gantt) {
      Override
      public void actionPerformed(ActionEvent event) {
        IlvGanttModel model = new IlvDefaultGanttModel();
        gantt.setGanttModel(model);
        schedule.setGanttModel(model);
      }
    };
    addAction(menu, newAction);

    if (isLocalFileAccessAllowed()) {
      // Menu item for "Open ...".
      openAction = new XMLGanttActions.OpenXMLAction(gantt) {
        Override
        public void actionPerformed(ActionEvent event) {
          IlvGanttModel model = readXMLFile();
          // Change the Gantt model of the Gantt chart
          if (model != null) {
            gantt.setGanttModel(model);
            schedule.setGanttModel(model);
            zoomToFit();
          }
        }
      };
      addAction(menu, openAction);

      // Menu item for "Save ...".
      saveAsAction = new XMLGanttActions.SaveAsXMLAction(gantt);
      addAction(menu, saveAsAction);
    }

    // Menu item for "Exit".
    if (isExitAllowed()) {
      menu.addSeparator();
      exitAction = new GanttCommand.ExitAction();
      addAction(menu, exitAction);
    }

    return menu;
  }

  /**
   * Creates the Edit menu.
   *
   * @return The Edit menu.
   */
  public JMenu createEditMenu() {
    JMenu menu = new JMenu("Edit");
    menu.setMnemonic(KeyEvent.VK_E);
    setStatusText(menu, "Adds, removes, or modifies activities, constraints, resources, and reservations.");

    // Menu item for "Insert new activity".
    insertActivityRowAction = new GanttCommand.InsertActivityAction(gantt);
    addAction(menu, insertActivityRowAction);

    // Menu item for "Delete selected activity".
    deleteActivityRowAction = new GanttCommand.DeleteSelectedRowsAction(gantt);
    deleteActivityRowAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
    addAction(menu, deleteActivityRowAction);

    // Menu item for "Create new constraint".
    makeConstraintAction = new GanttCommand.MakeConstraintAction(gantt);
    addAction(menu, makeConstraintAction);

    // Menu item for "Delete selected constraints".
    deleteConstraintAction = new GanttCommand.DeleteSelectedConstraintsAction(gantt);
    deleteConstraintAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK));
    addAction(menu, deleteConstraintAction);

    menu.addSeparator();

    // Menu item for "Insert new resource".
    insertResourceRowAction = new GanttCommand.InsertResourceAction(schedule);
    insertResourceRowAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, KeyEvent.SHIFT_DOWN_MASK));
    addAction(menu, insertResourceRowAction);

    // Menu item for "Delete selected resource".
    deleteResourceRowAction = new GanttCommand.DeleteSelectedRowsAction(schedule);
    deleteResourceRowAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.SHIFT_DOWN_MASK));
    addAction(menu, deleteResourceRowAction);

    // Menu item for "Create reservation".
    makeReservationAction = new GanttCommand.MakeActivityAction(schedule);
    addAction(menu, makeReservationAction);

    // Menu item for "Delete selected reservations".
    deleteReservationAction = new GanttCommand.DeleteSelectedReservationsAction(schedule);
    deleteReservationAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK));
    addAction(menu, deleteReservationAction);

    menu.addSeparator();

    // Menu item for "Delete all".
    deleteAllAction = new GanttCommand.DeleteSelectedAction(gantt, schedule);
    deleteAllAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
    addAction(menu, deleteAllAction);

    menu.addSeparator();

    // Menu item for "Expand/Collapse".
    rowExpandCollapseAction = new GanttCommand.RowExpandCollapseAction(gantt, schedule);
    addAction(menu, rowExpandCollapseAction);
    return menu;
  }

  /**
   * Creates the Row menu.
   *
   * @return The Row menu.
   */
  public JMenu createRowMenu() {
    JMenu menu = new JMenu("Row");
    menu.setMnemonic(KeyEvent.VK_R);
    setStatusText(menu, "Changes the chart's row height.");

    // Menu item for "Increase 2 pixels".
    increaseRowHeightAction = new GanttCommand.RowHeightAction(2, gantt, schedule);
    increaseRowHeightAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, KeyEvent.CTRL_DOWN_MASK));
    addAction(menu, increaseRowHeightAction,
        // Add an alternate keystroke, since Ctrl-2 is not available on some
        // national keyboards.
        new KeyStroke[] { KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD2, KeyEvent.CTRL_DOWN_MASK) });

    // Menu item for "Decrease 2 pixels".
    decreaseRowHeightAction = new GanttCommand.RowHeightAction(-2, gantt, schedule);
    decreaseRowHeightAction
        .setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK));
    addAction(menu, decreaseRowHeightAction,
        new KeyStroke[] { KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD2, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK) });

    return menu;
  }

  /**
   * Creates the main toolbar.
   *
   * @return The main application toolbar.
   */
  protected JToolBar createMainToolBar() {
    JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL);
    toolbar.setFloatable(false);
    addAction(toolbar, newAction);
    if (openAction != null) {
      addAction(toolbar, openAction);
    }
    if (saveAsAction != null) {
      addAction(toolbar, saveAsAction);
    }
    toolbar.addSeparator();
    addAction(toolbar, deleteAllAction);
    toolbar.addSeparator();
    addAction(toolbar, rowExpandCollapseAction);
    toolbar.addSeparator();
    addAction(toolbar, gantt.getZoomOutAction());
    addAction(toolbar, gantt.getZoomInAction());
    zoomToFitAction = new IlvZoomToFitAction(gantt, "Zoom To Fit",
        KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_DOWN_MASK), "Zoom To Fit",
        "Zooms the time scale to fit the data.");
    addAction(toolbar, zoomToFitAction);
    return toolbar;
  }

  /**
   * Creates the toolbar of the Gantt chart.
   *
   * @return The Gantt chart toolbar.
   */
  protected JToolBar createGanttToolBar() {
    JToolBar toolbar = new JToolBar(JToolBar.VERTICAL);
    toolbar.setFloatable(false);
    addAction(toolbar, insertActivityRowAction);
    addAction(toolbar, deleteActivityRowAction);
    addAction(toolbar, makeConstraintAction);
    addAction(toolbar, deleteConstraintAction);
    return toolbar;
  }

  /**
   * Creates the toolbar of the Schedule chart.
   *
   * @return The Schedule chart toolbar.
   */
  protected JToolBar createScheduleToolBar() {
    JToolBar toolbar = new JToolBar(JToolBar.VERTICAL);
    toolbar.setFloatable(false);
    addAction(toolbar, insertResourceRowAction);
    addAction(toolbar, deleteResourceRowAction);
    addAction(toolbar, makeReservationAction);
    addAction(toolbar, deleteReservationAction);
    return toolbar;
  }

  /**
   * Creates and returns the Gantt data model.
   *
   * @return The data model.
   */
  protected IlvGanttModel createGanttModel() {
    return new SimpleEngineeringProject(gantt);
  }

  // =========================================
  // Example Application
  // =========================================

  /**
   * Returns the title of the example.
   *
   * @return The title of the example.
   */
  Override
  public String getTitle() {
    return "Synchronized Gantt and Schedule Chart Example";
  }

  /**
   * Application mainline.
   *
   * @param args
   *          The command line arguments.
   */
  public static void main(String[] args) {
    ExampleFrame.createAndShowGUI(DualChartExample.class);
  }

}