/*
 * 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.FlowLayout;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

import ilog.views.IlvGrapher;
import ilog.views.IlvGraphic;
import ilog.views.IlvGraphicEnumeration;
import ilog.views.IlvHandlesSelection;
import ilog.views.IlvLinkImage;
import ilog.views.IlvManager;
import ilog.views.IlvManagerView;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.IlvTransformer;
import ilog.views.graphic.IlvZoomableLabel;
import ilog.views.linkconnector.IlvClippingLinkConnector;
import ilog.views.swing.IlvJScrollManagerView;
//import ilog.views.util.IlvProductUtil;
import ilog.views.util.IlvResourceUtil;

/**
 * This is a very simple application that shows how to implement
 * facilities to find nodes and to center them in the view.
 */
SuppressWarnings("serial")
public class FindInViewApp extends JRootPane
{
  IlvGrapher grapher;
  IlvManagerView mgrview;
  IlvZoomableLabel node;
  boolean animate = true;

  {
    // 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);
  }

  /**
   * Initializes the application.
   */
  public void init()
  {
    // create a grapher
    grapher = new IlvGrapher();

    // create a manager view
    mgrview = new IlvManagerView(grapher);

    // create the scroll manager view 
    IlvJScrollManagerView scrollManView = new IlvJScrollManagerView(mgrview);

    // Some settings on the manager view and on the scroll manager view
    mgrview.setAntialiasing(true);
    mgrview.setKeepingAspectRatio(true);
    mgrview.setBackground(Color.white);
    mgrview.setForeground(SystemColor.windowText);   
    Color xc = SystemColor.windowText;
    xc = new Color(255-xc.getRed(), 255-xc.getGreen(), 255-xc.getBlue());
    mgrview.setDefaultXORColor(xc);
    mgrview.setDefaultGhostColor(SystemColor.windowText);
    mgrview.setZoomFactorRange(0.02, 10.0);

    // Settings parameters for selection handles
    IlvHandlesSelection.defaultHandleColor = Color.red;
    IlvHandlesSelection.defaultHandleBackgroundColor = Color.green;
    IlvHandlesSelection.defaultHandleShape = IlvHandlesSelection.SQUARE_SHAPE;

    // Now, fill the grapher
    generateBinTree(10, 11, 1);

    // create a textfield that allows to search for nodes
    JTextField searchField = new JTextField("", 8);
    searchField.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        JTextField source = (JTextField)e.getSource();
        try {
          int num = Integer.valueOf(source.getText().trim()).intValue();
          if (!find(num))
            JOptionPane.showMessageDialog(FindInViewApp.this,
                                          "Node " + num + " not found!");
        } catch (Exception ex) {
          source.setText("");
        }
      }
    });

    // create a checkbox that allows to disable the animation
    JCheckBox useAnimation = new JCheckBox("Animation", true);
    useAnimation.setToolTipText(
        "Enable or disable the animation during search");
    useAnimation.addItemListener(new ItemListener() {
      Override
      public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED)
          animate = true;
        else if (e.getStateChange() == ItemEvent.DESELECTED)
          animate = false;
      }
    });


    // create the top panel with the toolbar and the pop-up checkbox
    JPanel topPanel = new JPanel();
    topPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
    topPanel.add(new JLabel("Search for: "));
    topPanel.add(searchField);
    topPanel.add(new JLabel("   "));
    topPanel.add(useAnimation);

    // put manager view, top panel and bottom panel together
    getContentPane().setLayout(new BorderLayout(0, 0));
    getContentPane().add(scrollManView, BorderLayout.CENTER);
    getContentPane().add(topPanel, BorderLayout.NORTH);

    mgrview.setDoubleBuffering(true);
    mgrview.setTransformer(new IlvTransformer(1.5, 0, 0, 1.2, 0, 0));
  }

  /**
   * Creates a balanced binary tree.
   * @param leftPos The leftmost position allowed for the tree.
   * @param depth The depth of the tree.
   * @param i The number of the root of the tree.
   * @return The rightmost position of the tree.
   */
  private double generateBinTree(double leftPos, int depth, int i)
  {
    if (depth == 0) {
      double x = leftPos;
      double y = 300-depth * 80;
      node = new IlvZoomableLabel(new IlvPoint(x,y), "" + i, false);
      setNodeAttributes(node);
      grapher.addNode(node, true);

      IlvRect bbox = node.boundingBox();
      return bbox.x + bbox.width;

    } else {

      int r = 1;
      for (int k = 0; k < depth; k++)
        r = 2 * r;
      double maxLeft = generateBinTree(leftPos, depth-1, i + 1);
      IlvGraphic leftRoot = node;
      double maxRight = generateBinTree(maxLeft + 25, depth-1, i + r);
      IlvGraphic rightRoot = node;

      double x = (leftPos + maxRight) / 2;
      double y = 300-depth * 80;

      node = new IlvZoomableLabel(new IlvPoint(x,y), "" + i, false);
      IlvRect bbox = node.boundingBox();
      node.translate(-bbox.width/2, 0);
      setNodeAttributes(node);
      grapher.addNode(node, true);

      IlvLinkImage link1 = new IlvLinkImage(node, leftRoot, true);
      IlvLinkImage link2 = new IlvLinkImage(node, rightRoot, true);
      grapher.addLink(link1, true);
      grapher.addLink(link2, true);

      return maxRight;
    }
  }

  /**
   * Sets the attributes of a node.
   */
  private void setNodeAttributes(IlvZoomableLabel node)
  {
    node.setAntialiasing(true);
    node.setLeftMargin(6);
    node.setRightMargin(6);
    node.setTopMargin(4);
    node.setBottomMargin(4);
    node.setBorderOn(true);
    node.setBackgroundOn(true);
    node.setBackgroundPaint(Color.yellow);
    new IlvClippingLinkConnector(node);
  }

  /**
   * Find an object and center it in the view.
   * We search for the number.
   */
  private boolean find(int number)
  {
    String name = "" + number;
    IlvGraphicEnumeration e = grapher.getObjects(true);
    while (e.hasMoreElements()) {
      IlvGraphic g = e.nextElement();
      // we only search of IlvZoomableLabel
      if (g instanceof IlvZoomableLabel) {
        IlvZoomableLabel label = (IlvZoomableLabel)g;
        if (name.equals(label.getLabel())) {
          // found. Center it in the view.
          IlvManager manager = (IlvManager)label.getGraphicBag();
          IlvTransformer t = manager.getDrawingTransformer(mgrview);
          IlvRect bbox = label.boundingBox(t);
          int w = mgrview.getWidth();
          int h = mgrview.getHeight();
          grapher.setSelected(label, true, true); 
          translateView((w - bbox.width)/2 - bbox.x,
                        (h - bbox.height)/2 - bbox.y, animate);
          grapher.deSelectAll(true, true);
          grapher.setSelected(label, true, true); 
          return true;
        }
      }
    }
    return false;
  }

  /**
   * Translates the view by (dx, dy).
   * If animation is switched off, it does the translation immediately.
   * If animation is switched on, it shows a smooth animation from the
   * old position to the new position.
   * This should be called in the swing thread.
   * @param dx The x shift value.
   * @param dy The y shift value.
   * @param animate Whether animation is enabled.
   */
  private void translateView(double dx, double dy, boolean animate)
  {
    if (!animate) 
      mgrview.translate(dx, dy, true);
    else {
      Repainter repainter = new Repainter();
      mgrview.setDoubleBufferFrozen(true);
      repainter.oldT = mgrview.getTransformer();
      mgrview.translate(dx, dy, false);
      repainter.newT = mgrview.getTransformer();
      mgrview.fitTransformerToArea(null, fcalc, 10);
      repainter.fitT = mgrview.getTransformer();
      mgrview.setTransformer(repainter.oldT);
      mgrview.setDoubleBufferFrozen(false);

      repainter.mgrview = mgrview;
      repainter.count = 0;
      repainter.maxcount = 10;
      repainter.timer = new javax.swing.Timer(30, repainter);
      repainter.timer.start();
    }
  }

  /**
   * The fit area calculator.
   * It calculates the area of all selected objects.
   */
  IlvManagerView.FitAreaCalculator fcalc =
    new IlvManagerView.FitAreaCalculator() {
       Override
      public IlvRect getAreaToFit(IlvManagerView view) {
         IlvGraphicEnumeration e = view.getManager().getSelectedObjects(true);
         IlvRect rect = null;
         while (e.hasMoreElements()) {
            IlvGraphic g = e.nextElement();
            IlvManager m = (IlvManager)g.getGraphicBag();
            IlvRect bbox = g.boundingBox(m.getDrawingTransformer(view));
            if (rect == null)
              rect = bbox;
            else
              rect.add(bbox);
          }
  
         if (rect == null)
           // nothing selected, fallback: fit to all
           return view.computeBBox();
         else
           return rect;
       }
     };


  /**
   * The animated repainter.
   * This passed to a swing timer does an animation in two phases:
   * First is scrolls and zooms from oldT to fitT, then it scrolls and
   * zooms from fitT to newT.
   */
  class Repainter implements ActionListener {
    IlvManagerView mgrview;
    IlvTransformer oldT;
    IlvTransformer newT;
    IlvTransformer fitT;
    int count;
    int maxcount;
    javax.swing.Timer timer;
    boolean phase2;

    Override
    public void actionPerformed(ActionEvent evt) {
      count++;
      if (count == maxcount) {
       mgrview.setTransformer(fitT);
       mgrview.repaint(); 
       if (!phase2) {
         count = 0;
         phase2 = true;
         oldT = fitT;
         fitT = newT;
       } else {
         timer.stop();
       }
      } else {
        IlvTransformer t =
          new IlvTransformer(oldT.getx11() + count *
                               (fitT.getx11() - oldT.getx11()) / maxcount, 
                             oldT.getx12() + count *
                               (fitT.getx12() - oldT.getx12()) / maxcount, 
                             oldT.getx21() + count *
                               (fitT.getx21() - oldT.getx21()) / maxcount, 
                             oldT.getx22() + count * 
                               (fitT.getx22() - oldT.getx22()) / maxcount, 
                             oldT.getx0() + count * 
                               (fitT.getx0() - oldT.getx0()) / maxcount, 
                             oldT.gety0() + count * 
                               (fitT.gety0() - oldT.gety0()) / maxcount); 
        mgrview.setTransformer(t);
        mgrview.repaint(); 
      }
    }
  }

  /**
   * Allows you to run the demo as a standalone application.
   */
  public static void main(String[] arg)
  {
    // Sun recommends that to put the entire GUI initialization into the
    // AWT thread
    SwingUtilities.invokeLater(
      new Runnable() {
        Override
        public void run() {
          FindInViewApp app = new FindInViewApp();
          app.init();

          JFrame frame = new JFrame("Find In View Example");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.setSize(500, 500);
          frame.getContentPane().add(app);
          frame.setVisible(true);
        }
      });
  }
}