/*
 * 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.IlvRect;
import ilog.views.diagrammer.IlvDiagrammer;
import ilog.views.diagrammer.application.IlvDiagrammerAction;
import ilog.views.diagrammer.application.IlvDiagrammerApplication;
import ilog.views.diagrammer.application.IlvDiagrammerToolBar;
import ilog.views.sdm.IlvSDMModel;
import ilog.views.sdm.modeltools.IlvContentController;
import ilog.views.sdm.modeltools.IlvContentHandler;
import ilog.views.sdm.modeltools.IlvVisibleAreaListener;
import ilog.views.util.IlvProductUtil;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.ResourceBundle;

/**
 * This example shows how to use content on demand tool.
 * @since JViews 8.0
 * 
 */ 
public class ContentDemo extends IlvDiagrammerApplication
{
  
  ////////////////////////////////////////////////////
  // The following fields are interesting to modify,
  
  /** Number of nodes */
  public int _nodeNumber = 100;   // best between 100 and 10 000
  /** Offset between nodes, in the grid */
  public long _delta = 40; 
  /** Size of the cache */
  public int _cacheSize = _nodeNumber/2; // 0 is also an interesting value
  /** Show nodes in cache. This is less realistic, slower, but 
   * it shows how the cache is working. */
  public boolean _showCachedNodes = true;
  
  ////////////////////////////////////////////////////

  private static final String CODstatus = "detailsOnDemand";
  private static final String LOCKED = new String("locked");
  private static final String LOADED = new String("loaded");
  private static final String UNLOADED = new String("unloaded");
  private static final String CONTENT = "content";  
  private IlvContentController _controller;
  
  // entry point
  public static void main(final String[] args)
  {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {        
        new ContentDemo().init(args);
      }
    });
  }
  
  // constructor
  public ContentDemo()
  {
    super(new String[] {
      "-data", "data/grid.idpr",
      "-simple"
    });

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

  // init GUI
  public void init(Container contentPane)
  {
    super.init(contentPane);

    ResourceBundle rb;
    rb = ResourceBundle.getBundle("main");

    // add actions
    IlvDiagrammerToolBar tb = getViewToolBar();
    tb.addAction(new MoveAreaAction(rb));
    
    IlvDiagrammerToolBar toolbar = new IlvDiagrammerToolBar();
      toolbar.addAction(new Lock(rb));
      toolbar.addAction(new Load(rb));
      toolbar.addAction(new Unload(rb));
      contentPane.add(toolbar, BorderLayout.NORTH);
  }

  // GUI is ready, init application
  public void ready() {
    super.ready();
    // redo the diagram
    populateDiagram(_nodeNumber);
    final IlvDiagrammer diagrammer = getDiagrammers()[0];
    
    // initialize the content controler
    _controller = new IlvContentController();
    _controller.setCacheSize(_cacheSize);
    _controller.setSDMEngine(diagrammer.getEngine());
    _controller.setContentHandler(new IlvContentHandler() {

      // callback for loading content. Could be done in a separate thread, too.
      public void loadContent(IlvContentController source, Object[] objects) {
        // prevent too much notifications 
        diagrammer.setAdjusting(true);
        // loop over objects to load
        for (int i=0; i<objects.length; i++) {
          loadObject(objects[i], true);
        }
        diagrammer.setAdjusting(false);
      }

      // callback for unloading content
      public void unloadContent(IlvContentController source, Object[] objects) {
        diagrammer.setAdjusting(true);
        for (int i=0; i<objects.length; i++) {
          loadObject(objects[i], false);
        }
        diagrammer.setAdjusting(false);
      }
    });
    
    // install view listener to lock objects in visible area. 
    diagrammer.getView().fitTransformerToContent();
    IlvVisibleAreaListener val = new IlvVisibleAreaListener(_controller) {
      // here we subclass only to check nodes in cache
      public int requestAreaToLock(IlvRect area) {
        int result = super.requestAreaToLock(area);
        // check whether some nodes goes to cache
        if (_showCachedNodes) { 
          updateStates();
        }
        return result;
      }
      
    };
    val.installListener(_controller.getSDMEngine().getReferenceView());
  }

  ///////////////////////////////
  
  // make "pop" different nodes, in a grid
  private void populateDiagram(int pop) {
    IlvDiagrammer d = getDiagrammers()[0];
    d.setAdjusting(true);
    IlvSDMModel model= d.getEngine().getModel();
    model.clear();
    long width = (long) Math.sqrt(pop)*_delta;
    long x=0;
    long y=0;
    for (int i=0; i<pop; i++) {
      Object node = model.createNode("form");
      model.setObjectProperty(node, "name", new Integer(i));
      model.setObjectProperty(node, "x", new Long(x));
      model.setObjectProperty(node, "y", new Long(y));
      x += _delta;
      if (x >= width) {
        x = 0;
        y += _delta;
      }
      // all nodes are unloaded
      model.setObjectProperty(node, CODstatus, UNLOADED);
      model.addObject(node, null, null);
    }
    d.setAdjusting(false);
  }
  
  // callback from lock/load/unload actions
  private void workOnSelection(int action) {
    IlvDiagrammer d = getDiagrammers()[0];
    // loop on selected objects
    Iterator selected = d.getSelectedObjects();
    _controller.processObjects(selected, action);
    // check whether some nodes goes to cache
    if (_showCachedNodes) { 
      updateStates();
    }
  }
  
  /**
   * Callback from RectangleInteractor
   * @param area the area to lock
   */
  void lockArea(IlvRect area) {
    _controller.lockArea(area, IlvContentController.OVERRIDE);
    // check whether some nodes goes to cache
    if (_showCachedNodes) { 
      updateStates();
    }
  }
  
  
  // the exact status of objects, including in cache. This is slow, because
  // we need to check state of all objects.
  private void updateStates() {
    getDiagrammers()[0].setAdjusting(true);
    IlvSDMModel model = getDiagrammers()[0].getEngine().getModel();
    // loop over all objects
    Enumeration e = getDiagrammers()[0].getEngine().getAllObjects();
    while (e.hasMoreElements()) {
      Object node = e.nextElement();
      String expected = statToString(_controller.getObjectStatus(node));
      if (model.getObjectProperty(node, CODstatus) != expected) { // == is ok, here
        model.setObjectProperty(node, CODstatus, expected);
      }      
    }
    getDiagrammers()[0].setAdjusting(false);
  }
  
  /**
   * Loads or unloads an object 
   * @param node the object
   * @param load the action
   */
  private void loadObject(Object node, boolean load) {
    IlvSDMModel model = getDiagrammers()[0].getEngine().getModel();
    if (load) {
      // here we simulate the loading of node contents
      // it can be also interesting to put a sleep() command here, or loading a huge value. 
      model.setObjectProperty(node, CONTENT, "content should be more complex than this string");
    } else {
      // unload values
      model.setObjectProperty(node, CONTENT, null);
    }
    // adjust state. It's use only to have an accurate visual feedback in the CSS
    model.setObjectProperty(node, CODstatus, statToString(_controller.getObjectStatus(node)));
  }
  
  /**
   * Converts content status to a string that is simpler to match in the CSS.
   * @param objectStatus the loaded state
   * @return a string
   */
  private String statToString(int objectStatus) {
    switch(objectStatus) {
    case IlvContentController.LOCK: return LOCKED;
    case IlvContentController.LOAD: return LOADED;
    case IlvContentController.UNLOAD: return UNLOADED;
    }
    return null;
  }

  /////////////////////////////////
  // inner classes
  
  /**
   *  The action that locks selected objects.
   */
  private class Lock extends IlvDiagrammerAction {
    public Lock(ResourceBundle rb) {
      super("Lock", rb);
    }
    public void perform(ActionEvent e, IlvDiagrammer source) {
      workOnSelection(_controller.LOCK);
    }
   protected boolean isEnabled(IlvDiagrammer diagrammer) throws Exception {
      return true;
    }
  }

  /**
   *  The action that load in cache selected objects. 
   */
  private class Load extends IlvDiagrammerAction {
    public Load(ResourceBundle rb) {
      super("Load", rb);
    }
    public void perform(ActionEvent e, IlvDiagrammer source) {
      workOnSelection(_controller.LOAD);
    }
   protected boolean isEnabled(IlvDiagrammer diagrammer) throws Exception {
      return true;
    }    
  }

  
  /**
   *  The action that unloads selected objects.
   */
  private class Unload extends IlvDiagrammerAction {
    public Unload(ResourceBundle rb) {
      super("Unload", rb);
    }
    protected boolean isEnabled(IlvDiagrammer diagrammer) throws Exception {
      return true;
    }
    public void perform(ActionEvent e, IlvDiagrammer source) {
      workOnSelection(_controller.UNLOAD);
    }
  }

  /**
   * An action that controls the content on demand through a visible area. 
   */
  private class MoveAreaAction extends IlvDiagrammerAction.ToggleAction {

    /**
     * @param bundle
     */
    protected MoveAreaAction(ResourceBundle bundle) {
      super("MoveArea", bundle);
    }

    /* (non-Javadoc)
     * @see ilog.views.diagrammer.application.IlvDiagrammerAction.ToggleAction#isSelected(ilog.views.diagrammer.IlvDiagrammer)
     */
    protected boolean isSelected(IlvDiagrammer diagrammer) throws Exception {
      return diagrammer != null && diagrammer.getView().getInteractor() instanceof RectangleInteractor;
    }

    /* (non-Javadoc)
     * @see ilog.views.diagrammer.application.IlvDiagrammerAction.ToggleAction#setSelected(ilog.views.diagrammer.IlvDiagrammer, boolean)
     */
    protected void setSelected(IlvDiagrammer diagrammer, boolean selected) throws Exception {
      if (selected) {
        diagrammer.getView().pushInteractor(new RectangleInteractor(ContentDemo.this));
      } else {
        diagrammer.getView().popInteractor();
      }
    }

    /* (non-Javadoc)
     * @see ilog.views.diagrammer.application.IlvDiagrammerAction#isEnabled(ilog.views.diagrammer.IlvDiagrammer)
     */
    protected boolean isEnabled(IlvDiagrammer diagrammer) throws Exception {
      return diagrammer != null;
    }
    
  }
}