/*
 * 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 java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.LookupOp;
import java.awt.image.RescaleOp;
import java.awt.image.ShortLookupTable;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import ilog.views.IlvDefaultManagerFrame;
import ilog.views.IlvGrapher;
import ilog.views.IlvGraphic;
import ilog.views.IlvGraphicEnumeration;
import ilog.views.IlvHandlesSelection;
import ilog.views.IlvHoverHighlightingImageOperation;
import ilog.views.IlvManager;
import ilog.views.IlvManagerView;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.graphic.IlvIcon;
import ilog.views.graphic.IlvPolylineLinkImage;
import ilog.views.graphic.IlvRectangle;
import ilog.views.graphic.IlvShadowRectangle;
import ilog.views.graphic.IlvText;
import ilog.views.interactor.IlvSelectInteractor;
import ilog.views.swing.IlvJComponentGraphic;
import ilog.views.swing.IlvJManagerViewControlBar;
import ilog.views.swing.IlvJScrollManagerView;

/*
 * bench result: Cost for Predefined: None=100 % Cost for Predefined: Brighten=251 % Cost for
 * Predefined: Sharpen=418 % Cost for Predefined: Blur=396 % Cost for Predefined: Grayscale=214 %
 * Cost for Predefined: Invert Colors=144 % Cost for User Defined: Blur2=766 % Cost for User
 * Defined: Emboss=1207 % Cost for User Defined: Dilation=2158 % Cost for User Defined: Erosion=2121
 * % Cost for User Defined: Red invert=988 % Cost for User Defined: Green invert=984 % Cost for User
 * Defined: Blue invert=981 % Cost for User Defined: Negative=988 % Cost for User Defined: Negative
 * 2=1706 % Cost for User Defined: Brighten 2=1677 %
 */

/**
 * A sample for the hover highlighting feature.
 */
SuppressWarnings("serial")
public class HoverHighlight extends JRootPane {

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

  /**
   * Class that defines what we know about a specific hover operation.
   * The operation combo box contains instances of this.
   */
  static public class HoverOperation {// elements in the combo box
    final private String name;// name
    final private int mode; // a predefined mode
    final private IlvHoverHighlightingImageOperation operation;// an user defined operation

    /**
     * Creates a new <code>HoverOperation</code>.
     * Applying this will set a predefined mode.
     * @param name display name of the effect
     * @param mode predefined mode
     */
    public HoverOperation(String name, int mode) {
      this.name = name;
      this.mode = mode;
      this.operation = null;
    }

    /**
     * Creates a new <code>HoverOperation</code>.
     * Applying this will use an user defined operation.
     * @param name display name of the effect
     * @param operation image operation
     */
    public HoverOperation(String name, IlvHoverHighlightingImageOperation operation) {
      this.name = name;
      this.mode = IlvManager.HH_NONE;
      this.operation = operation;
    }

    /**
     * Sets the hovering effect on the graph.
     * @param graph graph to apply the effects to.
     * This uses the new API defined in JViews 8.1
     */
    public void apply(IlvManager graph) {
      if (operation == null)
        graph.setHoverHighlightingMode(mode);
      else
        graph.setHoverHighlightingImageOperation(operation);
    }

    /**
     * What we display in the combo box.
     */
    Override
    public String toString() {
      if (operation == null)
        return "Predefined: " + name;
      return "User Defined: " + name;
    }
  }

  // demo instance fields.
  private IlvGrapher graph;
  private IlvManagerView mngView;
  private IlvJScrollManagerView scroller;
  private JSlider hoverAlpha;
  private JComboBox<HoverOperation> hoverOp;
  private JComboBox<IlvHoverHighlightingImageOperation.Filter> hoverFilter;
  // color of the subgraphs
  private Color frameColor = Color.pink;

  // various definitions for the image operations
  static float[] blur5Kernel = new float[5 * 5];
  static {
    for (int i = 0; i < blur5Kernel.length; i++) {
      blur5Kernel[i] = 1f / blur5Kernel.length;
    }
  }
  static short[] invert = new short[256];
  static short[] straight = new short[256];
  static {
    for (int i = 0; i < 256; i++) {
      invert[i] = (short) (255 - i);
      straight[i] = (short) i;
    }
  }
  static short[][] blueInvert = new short[][] {straight, straight, invert, straight};
  static BufferedImageOp blueInvertOp = new LookupOp(new ShortLookupTable(0, blueInvert), null);
  static short[][] redInvert = new short[][] {invert, straight, straight, straight};
  static BufferedImageOp redInvertOp = new LookupOp(new ShortLookupTable(0, redInvert), null);
  static short[][] greenInvert = new short[][] {straight, invert, straight, straight};
  static BufferedImageOp greenInvertOp = new LookupOp(new ShortLookupTable(0, greenInvert), null);
  static short[][] allInvert = new short[][] {invert, invert, invert, straight};
  static BufferedImageOp allInvertOp = new LookupOp(new ShortLookupTable(0, greenInvert), null);
  static BufferedImageOp negativeOp =
      new RescaleOp(new float[] {-1.0f, -1.0f, -1.0f, 1}, new float[] {255f, 255f, 255f, 0}, null);
  static BufferedImageOp brighten2Op =
      new RescaleOp(new float[] {1.5f, 1.5f, 1.5f, 1}, new float[] {0, 0, 0, 0}, null);

  // list of hover operations.
  static HoverOperation operations[] = {new HoverOperation("None", IlvManager.HH_NONE),
      new HoverOperation("Brighten", IlvManager.HH_BRIGHTEN),
      new HoverOperation("Sharpen", IlvManager.HH_SHARPEN),
      new HoverOperation("Blur", IlvManager.HH_BLUR),
      new HoverOperation("Grayscale", IlvManager.HH_GRAYSCALE),
      new HoverOperation("Invert Colors", IlvManager.HH_INVERT_COLORS),
      new HoverOperation("Blur2",
          new IlvHoverHighlightingImageOperation(new ConvolveOp(new Kernel(5, 5, blur5Kernel)), 1)),
      new HoverOperation("Emboss",
          new IlvHoverHighlightingImageOperation(new HoverEmbossOp(null, 2), 1)),
      new HoverOperation("Dilation",
          new IlvHoverHighlightingImageOperation(new MorphologyOp(2, 2, true), 1)),
      new HoverOperation("Erosion",
          new IlvHoverHighlightingImageOperation(new MorphologyOp(2, 2, false), 1)),
      new HoverOperation("Red invert", new IlvHoverHighlightingImageOperation(redInvertOp, 1)),
      new HoverOperation("Green invert", new IlvHoverHighlightingImageOperation(greenInvertOp, 1)),
      new HoverOperation("Blue invert", new IlvHoverHighlightingImageOperation(blueInvertOp, 1)),
      new HoverOperation("Negative", new IlvHoverHighlightingImageOperation(allInvertOp, 1)),
      new HoverOperation("Negative 2", new IlvHoverHighlightingImageOperation(negativeOp, 1)),
      new HoverOperation("Brighten 2", new IlvHoverHighlightingImageOperation(brighten2Op, 1))};

  /**
   * Creates a new <code>Test</code>.
   */
  public HoverHighlight() {
    // create a sample graph content.
    graph = new IlvGrapher();
    graph.setVisible(true);
    createGraph();
    // create and setup the GUI
    // main view and scroll
    mngView = new IlvManagerView(graph);
    mngView.setAntialiasing(true);
    mngView.setKeepingAspectRatio(true);
    mngView.setWheelZoomingEnabled(true);
    mngView.setName("graphView");

    // parameters for selection handles
    IlvHandlesSelection.defaultHandleColor = Color.black;
    IlvHandlesSelection.defaultHandleBackgroundColor = Color.white;
    IlvHandlesSelection.defaultHandleShape = IlvHandlesSelection.SQUARE_SHAPE;

    scroller = new IlvJScrollManagerView(mngView);
    scroller.setBackground(Color.white);
    scroller.setWheelScrollingEnabled(true);

    // control toolbar and bench button
    IlvJManagerViewControlBar controlBar = new IlvJManagerViewControlBar();
    controlBar.setView(mngView);
    controlBar.setFloatable(false);

    IlvSelectInteractor selInter = (IlvSelectInteractor) controlBar.getSelectInteractor();
    selInter.setOpaqueDragSelection(true);
    selInter.setOpaqueMove(true);
    selInter.setOpaqueResize(true);
    selInter.setOpaquePolyPointsEdition(true);

    JButton bbench = new JButton("Bench");
    bbench.setToolTipText("Start a bench to compare the various operation costs");
    bbench.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        bench(1);// increase this number to have more meaningful comparison
      }
    });
    controlBar.add(bbench);


    // Hover effect settings panel
    hoverAlpha = new JSlider(0, 100, 100);
    hoverAlpha.setMajorTickSpacing(25);
    hoverAlpha.setPaintLabels(true);
    hoverAlpha.setToolTipText("Select hover alpha");
    hoverAlpha.addChangeListener(new ChangeListener() {
      Override
      public void stateChanged(ChangeEvent event) {
        updateOperation();
      }
    });
    hoverFilter = new JComboBox<IlvHoverHighlightingImageOperation.Filter>();
    hoverFilter.addItem(new IlvHoverHighlightingImageOperation.Filter() {
      Override
      public String toString() {
        return "Every Object";
      }

      Override
      public boolean highlightObject(IlvGraphic graphic) {
        return true;
      }
    });
    hoverFilter.addItem(new IlvHoverHighlightingImageOperation.GraphicBagFilter() {
      Override
      public String toString() {
        return "Containers only";
      }
    });
    hoverFilter.addItem(new IlvHoverHighlightingImageOperation.NonGraphicBagFilter() {
      Override
      public String toString() {
        return "Simple Graphics only";
      }
    });
    hoverFilter.setSelectedIndex(0);// set an initial operation
    hoverFilter.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent event) {
        updateOperation();
      }
    });

    hoverOp = new JComboBox<HoverHighlight.HoverOperation>(operations);
    hoverOp.setToolTipText("Select hover operation");
    hoverOp.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent event) {
        SuppressWarnings("unchecked")
        JComboBox<HoverHighlight.HoverOperation> comboBox =
            (JComboBox<HoverHighlight.HoverOperation>) event.getSource();
        HoverOperation op = (HoverOperation) comboBox.getSelectedItem();
        // apply the operation
        op.apply(graph);
        // then set the alpha to be consistent
        updateOperation();
      }
    });
    hoverOp.setSelectedIndex(1);// set an initial operation

    JPanel settings = new JPanel(new GridLayout(3, 0));
    settings.add(new JLabel("Hover Operation"));
    settings.add(hoverOp);
    settings.add(new JLabel("Operation Opacity"));
    settings.add(hoverAlpha);
    settings.add(new JLabel("Operation Filter"));
    settings.add(hoverFilter);
    settings.setBorder(BorderFactory.createTitledBorder("Hover effect settings"));

    // setup the main frame
    getContentPane().add(controlBar, BorderLayout.NORTH);
    getContentPane().add(scroller, BorderLayout.CENTER);
    getContentPane().add(settings, BorderLayout.SOUTH);
  }

  /**
   * Sets the current opacity and filter for the selected hover operation.
   */
  void updateOperation() {
    float alpha = hoverAlpha.getValue() / 100f;
    IlvHoverHighlightingImageOperation op = graph.getHoverHighlightingImageOperation();
    if (op != null) {
      op.setAlpha(alpha);
      op.setHighlightFilter(
          (IlvHoverHighlightingImageOperation.Filter) hoverFilter.getSelectedItem());
    }

  }

  /**
   * Bench to display the cost of the image operations relatively to a "noop" operation.
   * @param num number of times to repeat the graph enumeration.
  */
  public void bench(int num) {
    double base = -1;// base time for operations
    // test all operations
    for (int o = 0; o < operations.length; o++) {
      long to0 = System.currentTimeMillis();
      operations[o].apply(graph);// apply the operation
      updateOperation();// apply the alpha&filter
      // get the BufferedImageOp and the alpha
      IlvHoverHighlightingImageOperation op = graph.getHoverHighlightingImageOperation();
      for (int i = 0; i < num; i++) {
        // go through all graph objects.
        for (IlvGraphicEnumeration en = graph.getObjects(); en.hasMoreElements();) {
          if (op != null) {
            // call the method that displays the highlighted area
            op.showHighlight(en.nextElement(), mngView);
          } else {
            // draw the object
            en.nextElement().draw(mngView.getGraphics(), mngView.getTransformer());
          }
        }
      }
      long to1 = System.currentTimeMillis();
      if (op == null) {// noop (is the first one)
        base = (to1 - to0);
      }
      double cost = (to1 - to0) / base;
      System.out.println("Cost for " + operations[o] + "=" + ((int) (cost * 100)) + " %");
    }
    hoverOp.setSelectedIndex(operations.length - 1);
    mngView.repaint();
  }


  /**
   * Create a sample graph that contains varied objects.
   */
  public void createGraph() {
    IlvGraphic node1;
    node1 = new IlvRectangle(new IlvRect(10, 10, 20, 20));
    node1.setFillOn(true);
    node1.setBackground(Color.green);
    graph.addNode(node1, true);
    IlvGraphic node2;
    node2 = new IlvRectangle(new IlvRect(10, 60, 20, 20));
    node2.setFillOn(true);
    node2.setBackground(Color.green);
    graph.addNode(node2, true);
    IlvGraphic node3;
    node3 = addSubgraph(graph, 60, 0, 5);
    IlvPolylineLinkImage link1 = new IlvPolylineLinkImage(node1, node2, true, null);
    link1.setLineWidth(3.0);
    graph.addLink(link1, true);
    IlvPoint[] points =
        {new IlvPoint(40, 20), new IlvPoint(40, 40), new IlvPoint(50, 40), new IlvPoint(50, 20)};
    IlvPolylineLinkImage link2 = new IlvPolylineLinkImage(node1, node3, true, points);
    link2.setLineWidth(3.0);
    link2.setForeground(Color.red);
    graph.addLink(link2, true);
    node1 = new IlvRectangle(new IlvRect(10, 120, 30, 30));
    node1.setFillOn(true);
    node1.setBackground(Color.green);
    graph.addNode(node1, true);
    node1 = new IlvRectangle(new IlvRect(20, 125, 30, 30));
    node1.setFillOn(true);
    node1.setBackground(Color.pink);
    graph.addNode(node1, true);
    node1 = new IlvRectangle(new IlvRect(15, 135, 30, 30));
    node1.setFillOn(true);
    node1.setBackground(Color.cyan);
    graph.addNode(node1, true);
    IlvText text = new IlvText(new IlvPoint(120, 180), "This is a text");
    text.setAntialiasing(true);
    text.setForeground(Color.blue);
    graph.addNode(text, true);
    IlvIcon icon =
        new IlvIcon(getClass().getResource("data/bookmark_48.gif"), new IlvRect(40, 170, 48, 48));
    graph.addObject(icon, true);
    JButton b = new JButton("Swing Button");
    IlvJComponentGraphic cg = new IlvJComponentGraphic(
        new IlvRect(120, 210, b.getPreferredSize().getWidth(), b.getPreferredSize().getHeight()),
        b);
    graph.addObject(cg, true);
  }

  /**
   * Add a sub graph.
   * @param parent
   * @param x
   * @param y
   * @param nesting
   * @return the subgraph
   */
  public IlvGraphic addSubgraph(IlvGrapher parent, float x, float y, int nesting) {
    IlvGrapher sub = new IlvGrapher();
    IlvGraphic node1;
    node1 = new IlvRectangle(new IlvRect(x + 10, y + 10, 20, 20));
    node1.setFillOn(true);
    node1.setBackground(Color.green);
    sub.addNode(node1, true);
    IlvGraphic node2;
    node2 = new IlvShadowRectangle(new IlvRect(x + 10, y + 60, 20, 20));
    // node2.setFillOn(true);
    node2.setBackground(Color.green);
    sub.addNode(node2, true);
    IlvGraphic node3;
    if (nesting == 0) {
      node3 = new IlvShadowRectangle(new IlvRect(x + 60, y + 10, 20, 20));
      // node3.setFillOn(true);
      node3.setBackground(Color.green);
      sub.addNode(node3, true);
    } else {
      node3 = addSubgraph(sub, x + 60, y + 10, nesting - 1);
    }
    IlvPolylineLinkImage link1 = new IlvPolylineLinkImage(node1, node2, true, null);
    link1.setLineWidth(3.0);
    sub.addLink(link1, true);
    IlvPolylineLinkImage link2 = new IlvPolylineLinkImage(node1, node3, true, null);
    link2.setLineWidth(3.0);
    link2.setForeground(Color.red);
    sub.addLink(link2, true);
    IlvDefaultManagerFrame frame = (IlvDefaultManagerFrame) sub.getFrame();
    if (nesting % 2 == 0) {
      frame.setOpaque(true);
      frame.setBackground(frameColor);
      frame.setForeground(Color.red);
      frameColor = new Color((frameColor.getRed() * 211 + 17) % 255,
          (frameColor.getGreen() * 211 + 113) % 255, (frameColor.getBlue() * 211 + 37) % 255);
    } else {
      frame.setOpaque(false);
    }
    parent.addNode(sub, true);
    return node1;
  }


  /**
   * Starts the hover highlighting sample
   * @param args (ignored)
   */
  public static void main(String args[]) {
    JFrame f = new JFrame();
    f.getContentPane().add(new HoverHighlight());
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setTitle("Hover Highlighting Sample");
    f.setSize(500, 500);
    f.setVisible(true);
  }
}