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

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.ResourceBundle;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

import ilog.views.IlvPoint;
import ilog.views.gantt.IlvDuration;
import ilog.views.gantt.IlvGanttChart;
import ilog.views.gantt.IlvGanttModel;
import ilog.views.gantt.IlvTimeUtil;
import ilog.views.gantt.action.IlvAction;
import ilog.views.gantt.graphic.IlvGanttSheet;
import ilog.views.gantt.model.general.IlvGeneralActivity;
import ilog.views.gantt.model.general.IlvGeneralConstraint;
import ilog.views.gantt.swing.IlvPopupMenuActivityContext;
import ilog.views.gantt.swing.IlvPopupMenuConstraintContext;
import ilog.views.swing.IlvPopupMenuContext;
import ilog.views.swing.IlvPopupMenuManager;
import ilog.views.swing.IlvSimplePopupMenu;
import ilog.views.util.IlvLocaleUtil;
import ilog.views.util.IlvProductUtil;
import ilog.views.util.IlvResourceUtil;
import ilog.views.util.styling.IlvStylingException;
import ilog.views.util.swing.IlvSwingUtil;

/**
 * This example shows how to extend a Gantt project using the SDK.
 */
public class GanttExtensionExample extends JFrame {

  static {
    // This applet is designed to run only with default resource bundle
    // and various selected other resource bundles.
    // Setting the available resource suffixes avoids that the applet
    // tries to load resource bundles for other locales over the net,
    // even if the current locale of the browser is different.
    if (IlvResourceUtil.isInApplet()) {
      IlvResourceUtil.setAvailableResourceSuffixes("", "_ja");
    }
  }

  private static final String projectFile = "data/completionGantt.igpr";

  private ResourceBundle bundle;

  // *****************************************
  // Creation and initialization of the demo:
  // *****************************************

  /**
   * The constructor: initializes the resource bundle used to get the strings.
   */
  public GanttExtensionExample() {
    // 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);

    bundle = IlvResourceUtil.getBundle("extension.ganttExample", IlvLocaleUtil.getCurrentLocale(),
        getClass().getClassLoader());
  }

  /**
   * The main method called when the sample is run as an application.
   */
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      Override
      public void run() {
        // Create an instance of the demo class.
        //
        GanttExtensionExample demo = new GanttExtensionExample();

        // Create the frame.
        //
        JFrame frame = new JFrame(demo.getString("FrameTitle"));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        try {
          // Initialize the demo's GUI inside the frame.
          //
          demo.init(frame.getContentPane(), new URL("file:./"), false);
        } catch (MalformedURLException e) {
          e.printStackTrace();
          System.exit(1);
        }

        // Show the frame.
        //
        frame.setLocation(100, 100);
        frame.pack();
        frame.setVisible(true);
      }
    });
  }

  /**
   * This is the common code between the application and applet cases that
   * creates all the GUI elements and initializes the extension code.
   */
  public void init(Container contentPane, URL baseURL, boolean applet) {
    // Create a simple menu bar
    JMenuBar menubar = new JMenuBar();
    if (!applet) {
      JMenu fileMenu = new JMenu(getString("FileMenu"));
      IlvAction exitAction = new IlvAction(getString("Exit.Name"), null,
          KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.ALT_DOWN_MASK), getString("Exit.ShortDescription"),
          getString("Exit.LongDescription")) {

        /**
         * Performs the exit action.
         */
        Override
        public void actionPerformed(ActionEvent event) {
          System.exit(0);
        }
      };
      JMenuItem exitMenuItem = new JMenuItem(exitAction);
      fileMenu.add(exitMenuItem);
      menubar.add(fileMenu);
    }
    ((JComponent) contentPane).getRootPane().setJMenuBar(menubar);

    // Create the IlvGanttChart component:
    //
    IlvGanttChart ganttChart = new IlvGanttChart();

    contentPane.add(ganttChart, BorderLayout.CENTER);

    // Initialize the pop-up menus.
    //
    initPopupMenus(ganttChart);

    // Load the project.
    //
    try {
      ganttChart.setProject(new URL(baseURL, projectFile));
    } catch (IOException e1) {
      IlvSwingUtil.showErrorDialog(contentPane.getParent(), getString("Error"), e1);
    } catch (IlvStylingException e1) {
      IlvSwingUtil.showErrorDialog(contentPane.getParent(), getString("Error"), e1);
    }

    // Make sure the full chart is visible.
    ganttChart.expandAllRows();
    // Set the initially displayed time span to start 1 day before the beginning
    // of the chart and extend for 3 weeks.
    IlvGanttModel model = ganttChart.getGanttModel();
    if (model != null && model.getRootActivity() != null) {
      Date visibleStartTime = IlvTimeUtil.subtract(model.getRootActivity().getStartTime(), IlvDuration.ONE_DAY);
      ganttChart.setVisibleTime(visibleStartTime);
      ganttChart.setVisibleDuration(IlvDuration.ONE_WEEK.multiply(3));
    }

  }

  // *****************************************
  // Initialize of the pop-up menu:
  // *****************************************

  /**
   * This method creates the pop-up menus and registers them.
   */
  private void initPopupMenus(IlvGanttChart ganttChart) {
    // Enable pop-up menus on the Gantt sheet view.
    IlvGanttSheet ganttSheet = ganttChart.getGanttSheet();
    ganttSheet.setPopupMenusEnabled(true);

    // Create the activity pop-up menu for parent activities. The activity
    // pop-up menu displays the activity name, and let the user display
    // the properties of this activity
    JPopupMenu parentActivityMenu = createActivityPopupMenu(ganttChart, true);

    // In the CSS, the parent activity pop-up menu is specified in the
    // following way
    // activity:parent {
    // ...
    // popupMenuName: "ParentActivityPopupMenu";
    // }

    // Hence we must register a pop-up menu with the name
    // "ParentActivityPopupMenu"
    IlvPopupMenuManager.registerMenu("ParentActivityPopupMenu", parentActivityMenu);

    // Create the activity pop-up menu for leaves. The activity pop-up menu
    // displays the activity name, let the user change the completion state,
    // and let the user display the properties of this activity
    JPopupMenu activityMenu = createActivityPopupMenu(ganttChart, false);

    // In the CSS, the activity pop-up menu is specified in the following way
    // activity:leaf {
    // ...
    // popupMenuName: "ActivityPopupMenu";
    // }

    // Hence we must register a pop-up menu with the name "ActivityPopupMenu"
    IlvPopupMenuManager.registerMenu("ActivityPopupMenu", activityMenu);

    // Create the constraint pop-up menu. The constraint pop-up menu displays
    // the from-to labels of the linked activities, and let the user display
    // the properties of this constraint.
    JPopupMenu constraintMenu = createConstraintPopupMenu(ganttChart);

    // In the CSS, the constraint pop-up menu is specified in the following way
    // constraint {
    // ...
    // popupMenuName: "ConstraintPopupMenu";
    // }

    // Hence we must register a pop-up menu with the name "ConstraintPopupMenu"
    IlvPopupMenuManager.registerMenu("ConstraintPopupMenu", constraintMenu);

    // Register the white space menu for the main Gantt sheet.
    // Reason: when clicking in the white space, you are in fact clicking
    // on the main Gantt sheet
    JPopupMenu whitespaceMenu = createWhiteSpacePopupMenu();
    ganttSheet.getManager().setPopupMenu(whitespaceMenu);
  }

  /**
   * This method creates the pop-up menu for the activities.
   */
  private JPopupMenu createActivityPopupMenu(final IlvGanttChart ganttChart, final boolean isParent) {
    IlvSimplePopupMenu popupMenu = new IlvSimplePopupMenu() {
      Override
      protected void beforeDisplay(IlvPopupMenuContext context) {
        // always call super.beforeDisplay first
        super.beforeDisplay(context);
        // then the remaining menu preparation
        beforeActivityPopupDisplay(this, (IlvPopupMenuActivityContext) context, isParent);
      }
    };
    // add a label to contain the title
    JLabel label = new JLabel();
    label.setBorder(BorderFactory.createEmptyBorder(2, 10, 2, 5));
    popupMenu.add(label);

    // add the remaining items
    popupMenu.addActions((isParent ? ""
        : ("- | { GanttExtension.CompletionComplete | GanttExtension.CompletionInProcess | GanttExtension.CompletionNotStarted } |"))
        + "- | GanttExtension.Properties", '|', '{', '}', bundle, new ActionListener() {
          Override
          public void actionPerformed(ActionEvent e) {
            // retrieve the selected menu item
            JMenuItem m = (JMenuItem) e.getSource();

            // retrieve the graphic that has this pop-up menu
            IlvPopupMenuContext context = IlvPopupMenuManager.getPopupMenuContext(m);
            if (context == null || !(context instanceof IlvPopupMenuActivityContext)) {
              return;
            }
            IlvPopupMenuActivityContext activityContext = (IlvPopupMenuActivityContext) context;
            // retrieve the activity of the graphic
            IlvGeneralActivity activity = (IlvGeneralActivity) activityContext.getActivity();

            if (activity == null) {
              return;
            }

            if (m.getText().equals(getString("CompletionComplete"))) {
              activity.setProperty("completion", Float.valueOf((float) 1.0));
            } else if (m.getText().equals(getString("CompletionInProcess"))) {
              activity.setProperty("completion", Float.valueOf((float) 0.5));
            } else if (m.getText().equals(getString("CompletionNotStarted"))) {
              activity.setProperty("completion", Float.valueOf((float) 0.0));
            } else if (m.getText().equals(getString("Properties"))) {
              // retrieve the position of the pop-up trigger
              IlvPoint p = context.getPoint();
              // show the properties
              showProperties(ganttChart, activity, (int) p.x, (int) p.y);
            }
          }
        });

    return popupMenu;
  }

  /**
   * The actions to be performed before displaying the pop-up menu for
   * activities. This configures the pop-up menu to display the actual state.
   */
  private void beforeActivityPopupDisplay(IlvSimplePopupMenu popupMenu, IlvPopupMenuActivityContext context,
      boolean isParent) {
    // retrieve the activity from the context
    IlvGeneralActivity activity = (IlvGeneralActivity) context.getActivity();

    // the title label of the menu
    String title = activity.getName();

    // set the title label in the first entry of the menu
    JLabel label = (JLabel) popupMenu.getComponent(0);
    label.setText(title);

    // update the status for the leaves
    if (!isParent) {
      float completion = 0.0f;
      if (activity.getProperty("completion") != null) {
        completion = (Float) activity.getProperty(("completion"));
      }
      if (completion == 1.0f) {
        popupMenu.getItemForText(getString("CompletionComplete")).setSelected(true);
      } else if (completion == 0.0f) {
        popupMenu.getItemForText(getString("CompletionNotStarted")).setSelected(true);
      } else {
        popupMenu.getItemForText(getString("CompletionInProcess")).setSelected(true);
      }
    }
  }

  /**
   * This method creates the pop-up menu for the constraints.
   */
  private JPopupMenu createConstraintPopupMenu(final IlvGanttChart ganttChart) {
    IlvSimplePopupMenu popupMenu = new IlvSimplePopupMenu() {
      Override
      protected void beforeDisplay(IlvPopupMenuContext context) {
        // always call super.beforeDisplay first
        super.beforeDisplay(context);
        // then the remaining menu preparation
        beforeConstraintPopupDisplay(this, (IlvPopupMenuConstraintContext) context);
      }
    };
    // add a label to contain the title
    JLabel label = new JLabel();
    label.setBorder(BorderFactory.createEmptyBorder(2, 10, 2, 5));
    popupMenu.add(label);

    // add the remaining items
    popupMenu.addActions("- | GanttExtension.Properties", '|', '{', '}', bundle, new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        // retrieve the selected menu item
        JMenuItem m = (JMenuItem) e.getSource();

        // retrieve the graphic that has this pop-up menu
        IlvPopupMenuContext context = IlvPopupMenuManager.getPopupMenuContext(m);
        if (context == null || !(context instanceof IlvPopupMenuConstraintContext)) {
          return;
        }
        IlvPopupMenuConstraintContext constraintContext = (IlvPopupMenuConstraintContext) context;
        // retrieve the constraint of the graphic
        IlvGeneralConstraint constraint = (IlvGeneralConstraint) constraintContext.getConstraint();

        if (constraint == null) {
          return;
        }

        if (m.getText().equals(getString("Properties"))) {
          // retrieve the position of the pop-up trigger
          IlvPoint p = context.getPoint();
          // show the properties
          showProperties(ganttChart, constraint, (int) p.x, (int) p.y);
        }
      }
    });

    return popupMenu;
  }

  /**
   * The actions to be performed before displaying the pop-up menu for
   * constraints. This configures the pop-up menu to display the actual state.
   */
  private void beforeConstraintPopupDisplay(IlvSimplePopupMenu popupMenu, IlvPopupMenuConstraintContext context) {
    // retrieve the constraint from the context
    IlvGeneralConstraint constraint = (IlvGeneralConstraint) context.getConstraint();

    // the title label of the menu. Concatenate the from-to activity labels
    String title = constraint.getFromActivity().getName() + " -> " + constraint.getToActivity().getName();

    // set the title label in the first entry of the menu
    JLabel label = (JLabel) popupMenu.getComponent(0);
    label.setText(title);
  }

  /**
   * This method creates the pop-up menu for the white space.
   */
  private JPopupMenu createWhiteSpacePopupMenu() {
    // Create the pop-up menu for the white space. This gets associated to
    // the Gantt sheet manager. No actions associated with this pop-up menu.
    // It contains only a text.
    IlvSimplePopupMenu popupMenu = new IlvSimplePopupMenu() {
      Override
      protected void beforeDisplay(IlvPopupMenuContext context) {
        // always call super.beforeDisplay first
        super.beforeDisplay(context);
      }
    };
    JLabel label = new JLabel(getString("NoObjectSelected"));
    label.setBorder(BorderFactory.createEmptyBorder(2, 10, 2, 5));
    popupMenu.add(label);

    return popupMenu;
  }

  private PropertySheet propertySheet;
  private JDialog propertySheetDialog;

  /**
   * Displays a property sheet with all the properties of the selected object.
   */
  private void showProperties(IlvGanttChart gantt, Object object, int x, int y) {
    if (propertySheet == null) {
      Window window = SwingUtilities.getWindowAncestor(gantt);
      if (window instanceof Frame) {
        propertySheetDialog = new JDialog((Frame) window);
      } else {
        propertySheetDialog = new JDialog();
      }
      propertySheetDialog.setTitle(getString("PropertiesTitle"));
      propertySheetDialog.setModal(true);

      propertySheet = new PropertySheet();
      propertySheetDialog.getContentPane().add(propertySheet);

      JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));
      JButton close = new JButton(getString("Close"));
      close.addActionListener(new ActionListener() {
        Override
        public void actionPerformed(ActionEvent e) {
          propertySheetDialog.setVisible(false);
        }
      });
      buttonPanel.add(close);
      propertySheetDialog.getContentPane().add(buttonPanel, BorderLayout.AFTER_LAST_LINE);

      propertySheetDialog.setSize(300, 300);
    }
    // set the object to show the properties for
    propertySheet.setTarget(object);
    // position the property sheet on the screen
    Point pos = gantt.getLocationOnScreen();
    propertySheetDialog.setLocation(pos.x + x + 20, pos.y + y + 20);
    propertySheetDialog.setVisible(true);
  }

  // *****************
  // Utility methods:
  // *****************

  // Gets a string from the property file.
  //
  private String getString(String key) {
    return bundle.getString("GanttExtension." + key);
  }

}