/*
 * Licensed Materials - Property of Rogue Wave Software, Inc. 
 * © Copyright Rogue Wave Software, Inc. 2014, 2015 
 * © 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.IlvManager;
import ilog.views.IlvManagerLayer;
import ilog.views.IlvGraphic;
import ilog.views.IlvGraphicEnumeration;
import ilog.views.IlvHandlesSelection;
import ilog.views.IlvManagerView;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.IlvApplyObject;
import ilog.views.IlvTransformer;

import ilog.views.graphic.IlvZoomableLabel;

import ilog.views.swing.IlvPopupMenuManager;
import ilog.views.swing.IlvPopupMenuContext;
import ilog.views.swing.IlvSimplePopupMenu;

import ilog.views.interactor.IlvSelectInteractor;
import ilog.views.interactor.IlvZoomViewInteractor;

import ilog.views.swing.IlvJScrollManagerView;
import ilog.views.swing.IlvJManagerViewControlBar;

import ilog.views.util.swing.IlvSwingUtil;

//import ilog.views.util.IlvProductUtil;
import ilog.views.util.IlvResourceUtil;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.net.*;
import java.io.*;

/**
 * This is a very simple applet/application that shows how to use
 * Z-ordering with or without the quadtree.
 */
public class ZOrderingApplet extends JApplet
{
  IlvManager manager;
  IlvManagerView mgrview;

  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");
  }

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

  /**
   * Initializes the applet/application.
   */
  public void init()
  {
    super.init();

    // create a manager
    manager = new IlvManager();

    // create a manager view
    mgrview = new IlvManagerView(manager);
    mgrview.setSelectedWhenPopupPreferred(true);

    // 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.black;
    IlvHandlesSelection.defaultHandleBackgroundColor = Color.white;
    IlvHandlesSelection.defaultHandleShape = IlvHandlesSelection.SQUARE_SHAPE;

    // create the standard control bar
    IlvJManagerViewControlBar controlBar = new IlvJManagerViewControlBar();
    controlBar.setView(mgrview);

    // modify the interactors such that the demo looks better
    ((IlvSelectInteractor)controlBar.getSelectInteractor()).setOpaqueMove(true);
    ((IlvZoomViewInteractor)controlBar.getZoomViewInteractor()).setPermanent(true);

    // set the initial interactor
    mgrview.setInteractor(controlBar.getSelectInteractor());

    // -----------------------------------------------------------------------
    // Now, fill the manager and set up the pop-up menus

    // register view view to the pop-up manager
    IlvPopupMenuManager.registerView(mgrview);

    // register the menus under different names
    IlvPopupMenuManager.registerMenu("MENU1", createMenu());

    // fill the manager with graphic objects that use menu1
    Random rand = new Random(0);
    for (int i = 0; i < 5; i++) {
      for (int j = 0; j < 28; j++) {
        // float x = rand.nextFloat() * 400;
        // float y = rand.nextFloat() * 400;
        float x = 5 + i * 100 + (j%2) * 45;
        float y = 5 + j * 15;
        IlvZoomableLabel graphic = new IlvZoomableLabel(new IlvPoint(x,y),
                                                        "Z-Order: " + i,
                                                        false);
        graphic.setAntialiasing(true);
        graphic.setLeftMargin(6);
        graphic.setRightMargin(6);
        graphic.setTopMargin(4);
        graphic.setBottomMargin(4);
        graphic.setBorderOn(true);
        graphic.setBackgroundOn(true);
        int r = (int)(80 + rand.nextFloat() * (255-80));
        int g = (int)(80 + rand.nextFloat() * (255-80));
        int b = (int)(80 + rand.nextFloat() * (255-80));
        r = Math.max(0, Math.min(r, 255));
        g = Math.max(0, Math.min(g, 255));
        b = Math.max(0, Math.min(b, 255));
        graphic.setBackgroundPaint(new Color(r, g, b));

        // set the pop-up menu or the graphic
        graphic.setPopupMenuName("MENU1");

        // add the graphic to the manager
        manager.addObject(graphic, 0, true);
      }
    }

    // enable Z-Ordering
    final IlvManagerLayer layer = manager.getManagerLayer(0);
    layer.setZOrdering(true);

    // create a checkbox that allows to disable the Z-ordering in the layer
    JCheckBox useZOrder = new JCheckBox("Z-Order", true);
    useZOrder.setToolTipText(
        "Enable or disable the Z-ordering");
    useZOrder.addItemListener(new ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED)
          layer.setZOrdering(true);
        else if (e.getStateChange() == ItemEvent.DESELECTED)
          layer.setZOrdering(false);
        manager.reDraw();
      }
    });

    // create a checkbox that allows to disable the quadtree in the layer
    JCheckBox useQuadTree = new JCheckBox("Quadtree", true);
    useQuadTree.setToolTipText(
        "Enable or disable the quad tree");
    useQuadTree.addItemListener(new ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED)
          layer.setQuadtreeEnabled(true);
        else if (e.getStateChange() == ItemEvent.DESELECTED)
          layer.setQuadtreeEnabled(false);
        manager.reDraw();
      }
    });

    /**
     * To enable benchmarking
     *
    JButton bench = new JButton("Bench");
    bench.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        System.out.println("Quadtree " + layer.isQuadtreeEnabled());
        System.out.println("Z-Ordering " + layer.isZOrdering());
        long s = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++)
          manager.getObject(new IlvPoint(i,i), mgrview);
        long t = System.currentTimeMillis();
        System.out.println("Time for 1000 hittest: " + (t-s) + " ms");
        s = System.currentTimeMillis();
        IlvApplyObject f = new IlvApplyObject() {
          public void apply(IlvGraphic g, Object arg) {}
        };
        for (int i = 0; i < 1000; i++)
          manager.mapIntersects(f, null, new IlvRect(i,i,100,100), new IlvTransformer());
        t = System.currentTimeMillis();
        System.out.println("Time for 1000 empty mapIntersects: " + (t-s) + " ms");
        int n = layer.getCardinal();
        s = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++)
          layer.setIndex(layer.getObject(n-1), i);
        t = System.currentTimeMillis();
        System.out.println("Time for 1000 setIndex: " + (t-s) + " ms");
        s = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++)
          layer.getIndex(layer.getObject(n-1));
        t = System.currentTimeMillis();
        System.out.println("Time for 1000 getIndex: " + (t-s) + " ms");
      }
    });
     */

    // create the top panel with the toolbar and the pop-up checkbox
    JPanel topPanel = new JPanel();
    topPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
    topPanel.add(controlBar);
    topPanel.add(useZOrder);
    topPanel.add(useQuadTree);
    //topPanel.add(bench);

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

    updateLabelTexts();
  }

  /**
   * Called when this applet is being reclaimed in order to destroy
   * any resources that it has allocated.
   */
  public void destroy()
  {
    super.destroy();

    // This method is intended to workaround memory management issues
    // in the Sun JRE. Please refer to the method documentation for more
    // details and a description of the known issues.
    IlvSwingUtil.cleanupApplet();
  }

  /**
   * Update the text in the label to display the current Z-order.
   */
  private void updateLabelTexts()
  {
    manager.applyToObjects(manager.getObjects(),
      new IlvApplyObject() {
        public void apply(IlvGraphic g, Object arg) {
          if (g instanceof IlvZoomableLabel) {
            IlvManagerLayer layer = manager.getManagerLayer(g);
            int zOrder = layer.getIndex(g);
            String s;
            if (zOrder < 10) s = "00" + zOrder;
            else if (zOrder < 100) s = "0" + zOrder;
            else s = "" + zOrder;
            ((IlvZoomableLabel)g).setLabel("Z-Order: " + s);
          }
        }
      }, null, true);
  }

  /**
   * Creates a very simple pop-up menu.
   * In principle, you can use any JPopupMenu at the graphic object.
   * However, the IlvSimplePopupMenu is very conveniant to configure, hence
   * we use it in this sample.
   */
  private JPopupMenu createMenu()
  {
    // create the action listener for the pop-up menu
    ActionListener actionListener = new ActionListener() {
      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);
        IlvGraphic graphic = context.getGraphic();

        // do the action
        if (IlvSimplePopupMenu.getMenuItemText(m).equals("To front"))
          moveToFront(graphic);
        else if (IlvSimplePopupMenu.getMenuItemText(m).equals("Forward"))
          moveForward(graphic);
        else if (IlvSimplePopupMenu.getMenuItemText(m).equals("Backward"))
          moveBackward(graphic);
        else if (IlvSimplePopupMenu.getMenuItemText(m).equals("To back"))
          moveToBack(graphic);

        // select the object (just to have a visual feedback which object has
        // changed)
        manager.deSelectAll(true);
        manager.setSelected(graphic, true, true);
      }
    }; 

    // create a simple pop-up menu
    IlvSimplePopupMenu menu =
      new IlvSimplePopupMenu(
              "Menu1",
              "To front | Forward | Backward | To back",
              null,
              actionListener);

    return menu;
  }

  /**
   * Move a graphic to the top of the Z-order.
   */
  private void moveToFront(IlvGraphic g)
  {
    IlvManager manager = (IlvManager)g.getGraphicBag();
    IlvManagerLayer layer = manager.getManagerLayer(g);
    layer.setIndex(g, layer.getCardinal() - 1);
    // this also redraws
    updateLabelTexts();
  }

  /**
   * Move a graphic to the bottom of the Z-order.
   */
  private void moveToBack(IlvGraphic g)
  {
    IlvManager manager = (IlvManager)g.getGraphicBag();
    IlvManagerLayer layer = manager.getManagerLayer(g);
    layer.setIndex(g, 0);
    // this also redraws
    updateLabelTexts();
  }

  /**
   * Move a graphic one step forward in the Z-order
   * Well, not quite: if we have 1000 objects, then we would need up to
   * 1000 times move forward operations to move the object visible forward.
   * But visibly, only something happens at the Z-order of the overlapping
   * objects, so we shorten this by moving forward with respect to the
   * current overlapping. That is, we move it to the Z-order so that it
   * appears on top of the lowest overlapping object that is currently on top
   * of the input object
   */
  private void moveForward(IlvGraphic g)
  {
    IlvManager manager = (IlvManager)g.getGraphicBag();
    IlvManagerLayer layer = manager.getManagerLayer(g);
    IlvTransformer t = mgrview.getTransformer();
    IlvRect trect = g.boundingBox(t);
    IlvRect rect = new IlvRect(trect);
    t.boundingBox(rect, true);
    IntersectionCollector f = new IntersectionCollector();
    layer.mapIntersects(rect, trect, f, null, t);
    int zOrder = layer.getIndex(g);
    int bestZOrder;
    if (f.result.size() <= 1)
      // just move one up. Nothing overlaps, so there will not be any
      // immediate visual effect
      bestZOrder = zOrder + 1;
    else {
      // search in the result the closest object with higher Z-Order.
      // Move to that Z-Order, because this has an immediate visual effect.
      bestZOrder = layer.getCardinal();
      for (int i = 0; i < f.result.size(); i++) {
        IlvGraphic obj = (IlvGraphic)f.result.get(i);
        int otherZOrder = layer.getIndex(obj);
        if (zOrder < otherZOrder && otherZOrder < bestZOrder)
          bestZOrder = otherZOrder;
      }
    }
    layer.setIndex(g, bestZOrder);
    // this also redraws
    updateLabelTexts();
  }

  /**
   * Move a graphic one step backward in the Z-order
   * Well, not quite: if we have 1000 objects, then we would need up to
   * 1000 times move backward operations to move the object visible backward.
   * But visibly, only something happens at the Z-order of the overlapping
   * objects, so we shorten this by moving backward with respect to the
   * current overlapping. That is, we move it to the Z-order so that it
   * appears below of the highest overlapping object that is currently below
   * of the input object
   */
  private void moveBackward(IlvGraphic g)
  {
    IlvManager manager = (IlvManager)g.getGraphicBag();
    IlvManagerLayer layer = manager.getManagerLayer(g);
    IlvTransformer t = mgrview.getTransformer();
    IlvRect trect = g.boundingBox(t);
    IlvRect rect = new IlvRect(trect);
    t.boundingBox(rect, true);
    IntersectionCollector f = new IntersectionCollector();
    layer.mapIntersects(rect, trect, f, null, t);
    int zOrder = layer.getIndex(g);
    int bestZOrder;
    if (f.result.size() <= 1)
      // just move one down. Nothing overlaps, so there will not be any
      // immediate visual effect
      bestZOrder = zOrder - 1;
    else {
      // search in the result the closest object with lower Z-Order.
      // Move to that Z-Order, because this has an immediate visual effect.
      bestZOrder = -1;
      for (int i = 0; i < f.result.size(); i++) {
        IlvGraphic obj = (IlvGraphic)f.result.get(i);
        int otherZOrder = layer.getIndex(obj);
        if (bestZOrder < otherZOrder && otherZOrder < zOrder)
          bestZOrder = otherZOrder;
      }
    }
    layer.setIndex(g, bestZOrder);
    // this also redraws
    updateLabelTexts();
  }

  /**
   * A collector apply opject.
   */
  class IntersectionCollector implements IlvApplyObject
  {
    ArrayList result = new ArrayList();

    public void apply(IlvGraphic g, Object arg)
    {
      result.add(g);
    }
  }

  /**
   * 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() {
        public void run() {
          ZOrderingApplet applet = new ZOrderingApplet();
          applet.init();

          JFrame frame = new JFrame("Z-Order Example");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.setSize(500, 500);
          frame.getContentPane().add(applet);
          frame.setVisible(true);
        }
      });
  }
}