/*
 * 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.
 */
import ilog.views.IlvGraphic;
import ilog.views.IlvPoint;
import ilog.views.IlvTransformer;
import ilog.views.gantt.*;
import ilog.views.gantt.action.IlvScrollToTimeIndicatorAction;
import ilog.views.gantt.action.IlvZoomToFitAction;
import ilog.views.gantt.graphic.IlvGanttSheet;
import ilog.views.gantt.graphic.IlvTimeIndicator;
import ilog.views.gantt.graphic.timeindicator.IlvCurrentTimeIndicator;
import ilog.views.gantt.graphic.timeindicator.IlvFixedTimeIndicator;
import ilog.views.graphic.IlvGeneralPath;
import ilog.views.graphic.IlvLine;
import ilog.views.graphic.IlvSpline;
import ilog.views.java2d.IlvLinearGradientPaint;
import ilog.views.util.IlvProductUtil;
import ilog.views.util.styling.IlvStylingException;

import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.Iterator;
import javax.swing.*;


/**
 * Time Indicator sample.
 * This sample code shows how to add time indicators to a Gantt sheet.
 */
public class TimeIndicator extends JPanel {

  // A simple project configuration obtained using the Gantt Designer, to load
  // some data.
  static final String styleURL = "data/basic.css";

  // Toolbar's buttons size
  static final int BUTTON_SIZE =  40;

  public IlvHierarchyChart chart;

  // The zoom-to-fit action.
  private IlvZoomToFitAction zoomToFitAction;

  public TimeIndicator(JFrame frame) {
    // Prepare the JPanel's appearance
    setLayout(new BorderLayout());

    chart = new IlvGanttChart();

    // Create a simple model
    IlvGanttModel model = new SimpleProject(chart);
    chart.setGanttModel(model);

    // Just load a CSS file
    try {
      // Set initially the sample project
      chart.setStyleSheet(getResourceURL(styleURL).toString());
    } catch (IOException e1) {
      JOptionPane.showMessageDialog(frame, e1, "Error",
          JOptionPane.ERROR_MESSAGE);
    } catch (IlvStylingException e1) {
      JOptionPane.showMessageDialog(frame, e1, "Error",
          JOptionPane.ERROR_MESSAGE);
    }

    // Add some time indicators
    createTimeIndicators();

    zoomToFitAction = new IlvZoomToFitAction(chart, "", null, null, "Zoom To Fit", "");

    add(BorderLayout.NORTH, createToolbar());
    add(BorderLayout.CENTER, chart);

    zoomToFit();

    // Make sure the full chart is visible.
    chart.expandAllRows();
    chart.revalidate();
  }

  /**
   * This method creates some time indicators.
   */
  public void createTimeIndicators() {
    IlvGanttSheet sheet = chart.getGanttSheet();

    // Enable anti-aliasing on the Gantt sheet.
    sheet.setAntialiasing(true);

    // Add a default fixed time indicator at 6 weeks from today
    // It will create a red line
    // The description "A time indicator" is given and shown as a tooltip
    Date now = new Date();
    Date time = IlvTimeUtil.add(now, IlvDuration.ONE_WEEK.multiply(6));
    IlvFixedTimeIndicator timeIndicator = new IlvFixedTimeIndicator(time,
        "A time indicator");
    sheet.addTimeIndicator(timeIndicator);

    // Add a fixed time indicator at 12 weeks from today with a customized
    // renderer, using an IlvGeneralPath with some gradient paint.
    IlvGeneralPath path = new IlvGeneralPath();
    Color fillColor = new Color(20, 50, 225);
    Color highlight = fillColor;
    for (int i = 1; i <= 5; i++) {
      highlight = highlight.brighter();
    }
    Color shadow = fillColor;
    for (int i = 1; i <= 2; i++) {
      shadow = shadow.darker();
    }
    Color[] colors = { fillColor, highlight, shadow };
    float[] stops = { 0, 0.25f, 1 };
    Point2D start = new Point2D.Double(0, 0);
    Point2D end = new Point2D.Double(4, 1);
    Paint fillPaint = new IlvLinearGradientPaint(start, end, stops, colors,
        true);
    path.setFillPaint(fillPaint);
    time = IlvTimeUtil.add(now, IlvDuration.ONE_WEEK.multiply(12));
    timeIndicator = new IlvFixedTimeIndicator(time, path, "A second time indicator");
    timeIndicator.setMaxWidth(15);
    sheet.addTimeIndicator(timeIndicator);

    // Add the current time indicator. Customize it with some dashes, a width,
    // and a color.
    IlvLine line = new IlvLine(0, 0, 0, 100);
    line.setLineWidth(6);
    float[] lineStyle = { 10, 15 };
    line.setLineStyle(lineStyle);
    IlvCurrentTimeIndicator currentTimeIndicator = new IlvCurrentTimeIndicator(
        line);
    // Current time indicators have a default description, overriding it...
    currentTimeIndicator.setDescription("A third time indicator");
    currentTimeIndicator.setForeground(Color.magenta);
    sheet.addTimeIndicator(currentTimeIndicator);

    // Another fixed time with a non rectangular object.
    IlvPoint[] points = new IlvPoint[28];
    int x = 25;
    int w = 25;
    int y = 50;
    //// Creation of an array of points .
    for (int i = 0; i <= 6; i++) {
      points[i * 4] = new IlvPoint(x, (i * 4 + 1) * y);
      points[i * 4 + 1] = new IlvPoint(x + w, (i * 4 + 2) * y);
      points[i * 4 + 2] = new IlvPoint(x, (i * 4 + 3) * y);
      points[i * 4 + 3] = new IlvPoint(x - w, (i * 4 + 4) * y);
    }
    IlvSpline spline = new IlvSpline(points);
    spline.setLineWidth(4);
    spline.setSmoothness(0.65F);
    time = IlvTimeUtil.add(now, IlvDuration.ONE_WEEK.multiply(18));
    timeIndicator = new IlvFixedTimeIndicator(time, spline, "A fourth time indicator");
    timeIndicator.setMaxWidth(15);
    timeIndicator.setStrokeOn(true);
    timeIndicator.setForeground(Color.cyan);
    sheet.addTimeIndicator(timeIndicator);
  }

  /**
   * This method creates a toolbar for this sample.
   * @return The toolbar.
   */
  protected JToolBar createToolbar() {
    JToolBar toolbar = new JToolBar();
    toolbar.setFloatable(false);
    // iterating through time indicators to add buttons
    // allowing to scroll to them
    Iterator<IlvTimeIndicator> it = chart.getGanttSheet().timeIndicatorsIterator();
    while (it.hasNext()) {
      IlvTimeIndicator timeIndicator = it.next();
      IlvScrollToTimeIndicatorAction scrollToTimeIndicatorAction = new IlvScrollToTimeIndicatorAction(
          chart.getGanttSheet(), timeIndicator, null, getIcon(timeIndicator),
          null, "Scroll To \"" + timeIndicator.getDescription() + "\"", "");
      toolbar.add(new JButton(scrollToTimeIndicatorAction));
    }
    toolbar.addSeparator();
    int margin = 15;
    Insets insets = new Insets(margin, margin, margin, margin);
    // add a zoom-in button
    JButton zoomInButton = new JButton(chart.getZoomInAction());
    zoomInButton.setText(null);
    zoomInButton.setMargin(insets);
    toolbar.add(zoomInButton);
    // add a zoom-out button
    JButton zoomOutButton = new JButton(chart.getZoomOutAction());
    zoomOutButton.setText(null);
    zoomOutButton.setMargin(insets);
    toolbar.add(zoomOutButton);
    // add a zoom-to-fit button
    JButton zoomToFitButton = new JButton(zoomToFitAction);
    zoomToFitButton.setMargin(insets);
    toolbar.add(zoomToFitButton);
    return toolbar;
  }

  /**
   * Create an overview icon from the time indicator renderer.
   * @param timeIndicator The time indicator from which we want an overview.
   * @return The icon.
   */
  private Icon getIcon(IlvTimeIndicator timeIndicator) {
    BufferedImage image = new BufferedImage(BUTTON_SIZE, BUTTON_SIZE, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = image.createGraphics();
    // set anti-aliasing
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    // fill white background
    g2d.setColor(Color.white);
    g2d.fillRect(0, 0, BUTTON_SIZE - 1, BUTTON_SIZE - 1);
    // get time indicator renderer
    IlvGraphic renderer = timeIndicator.getRenderer();
    // resize and move it to create an overview
    renderer.resize(timeIndicator.getMaxWidth(), BUTTON_SIZE - 1);
    renderer.move((BUTTON_SIZE - 1) / 2 - timeIndicator.getMaxWidth() / 2, 0);
    // render it
    renderer.draw(image.getGraphics(), new IlvTransformer());
    // draw a gray rectangle
    g2d.setColor(Color.gray);
    g2d.drawRect(0, 0, BUTTON_SIZE - 1, BUTTON_SIZE - 1);
    // create an icon from the overview
    return new ImageIcon(image);
  }

  public static void main(String[] args) {
    // 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);

    SwingUtilities.invokeLater(new Runnable() {
      Override
      public void run() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(50, 300, 800, 500);
        TimeIndicator example = new TimeIndicator(frame);
        frame.getContentPane().add(example);
        frame.setVisible(true);
      }
    });
  }

  /**
   * Zooms the chart to fit the data model's time interval.
   */
  protected void zoomToFit() {
    zoomToFitAction.perform();
  }


  // =========================================
  // Accessing Resources
  // =========================================

  /**
   * Returns the fully qualified URL of a resource file that is specified
   * relative to the working directory of the example. Note that this is
   * different than calling <code>Class.getResource()</code>, which performs
   * resource lookup relative to the example's classpath.
   *
   * @param relativePath The path of the resource file, relative to the working
   *                     directory of the example.
   * @return The resource URL.
   *
   * @throws IOException if the path cannot be resolved to a valid and existing
   *                     URL.
   */
  public URL getResourceURL(String relativePath) throws IOException {
    relativePath = relativePath.replace('\\', '/');
    URL url;

    // In an application context, we try to find an external file relative to
    // the current working directory. If an external file does not exist, then
    // the file may be bundled into the jar with the classes. In this case, we
    // prepend a '/' to relativePath so that we search relative to the
    // classloader's root and not relative to the packaging of the current
    // class.

    File file = new File(relativePath);
    // If the file exists in the external file system, we return its
    // corresponding URL.
    if (file.exists()) {
      url = file.toURI().toURL();
    }
    // Otherwise, we search for the file relative to the classloader's root.
    // This will find the file if it is packaged into a jar with the classes.
    else {
      // Prepend a '/' so that we search relative to the classloader's root and
      // not relative to the packaging of the current class.
      if (relativePath.charAt(0) != '/') {
        relativePath = '/' + relativePath;
      }
      url = getClass().getResource(relativePath);
    }

    // Verify that we have a valid URL by trying to open its associated stream.
    if (url == null) {
      throw new FileNotFoundException(relativePath);
    }
    InputStream stream = url.openStream();
    stream.close();
    return url;
  }

}