/*
 * 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.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

//import ilog.views.util.IlvProductUtil;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

import ilog.views.IlvGraphic;
import ilog.views.IlvManager;
import ilog.views.IlvManagerView;
import ilog.views.IlvObjectInteractor;
import ilog.views.IlvObjectInteractorContext;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.IlvTransformer;
import ilog.views.accelerator.IlvFitToSizeAccelerator;
import ilog.views.accelerator.IlvIdentityAccelerator;
import ilog.views.accelerator.IlvZoomInAccelerator;
import ilog.views.accelerator.IlvZoomOutAccelerator;
import ilog.views.graphic.IlvRectangle;

/**
 * An object interactor that allows to move a graphic object.
 */
public class MoveObjectInteractor
  extends IlvObjectInteractor
{
  private IlvRect mrect;
  private double dx, dy;
  private boolean dragging = false;
  
  /** 
   * Creates an instance of <code>MoveObjectInteractor</code>.
   */
  public MoveObjectInteractor()
  {
    super();
  }

  /**
   * Moves the graphic object.
   */
  private void doMove(IlvGraphic graphic, IlvObjectInteractorContext context)
  {
    if (mrect == null)
      return;

    // transform mrect from view coordinates to manager coordinates
    IlvTransformer t = context.getTransformer();

    if (t != null) 
      t.inverse(mrect);

    graphic.getGraphicBag().moveObject(graphic, mrect.x, mrect.y, true);
  }
  
  /**
   * Processes the button down events.
   */
  protected boolean processButtonDown(IlvGraphic obj, MouseEvent event,
                                      IlvObjectInteractorContext context)
  {
    if ((event.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK) != 0 ||
        (event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0)
      return true ;
    if (dragging)
      return true  ;
    dragging = true;
    IlvPoint p = new IlvPoint(event.getX(), event.getY());
    mrect = obj.boundingBox(context.getTransformer());

    dx = p.x - mrect.x;
    dy = p.y - mrect.y;

    invalidateGhost(obj, context);

    return true;
  }

  /**
   * Processes the button dragged events.
   */
  protected boolean processButtonDragged(IlvGraphic obj , 
                                        MouseEvent event,
                                        IlvObjectInteractorContext context)
  {
    if (!dragging || mrect == null) 
      return false;
    IlvPoint p = new IlvPoint(event.getX(), event.getY());

    invalidateGhost(obj, context);

    mrect.move(p.x - dx, p.y - dy);
    IlvTransformer t = context.getTransformer();
    if (t != null) 
      t.inverse(mrect);

    context.ensureVisible(p);

    t = context.getTransformer();
    if (t != null) 
      t.apply(mrect);
    
    invalidateGhost(obj, context);

    return true;
  }

  /**
   * Processes the button up events.
   */
  protected boolean processButtonUp(IlvGraphic obj, MouseEvent event, 
                                    IlvObjectInteractorContext context)
  {
    if (!dragging || mrect == null)
      return true;
    dragging = false;

    invalidateGhost(obj, context);

    doMove(obj, context);

    mrect = null;
    return true;
  }

  /**
   * Processes the events.
   */
  Override
  public boolean processEvent(IlvGraphic obj, AWTEvent event, 
                              IlvObjectInteractorContext context)
  {
    switch (event.getID()) {
      case MouseEvent.MOUSE_PRESSED:
        return processButtonDown(obj, (MouseEvent)event, context);

      case MouseEvent.MOUSE_DRAGGED:
        return processButtonDragged(obj, (MouseEvent)event, context);

      case MouseEvent.MOUSE_RELEASED:
        return processButtonUp(obj, (MouseEvent)event, context);
      default:
        return false;  
    }
  }

  /**
   * Called when the graphic that has this object interactor is entered.
   * That is, when this object interactor starts receiving events.
   */
  Override
  public void onEnter(IlvGraphic sel, IlvObjectInteractorContext context)
  {
    // System.err.println("On enter " + sel);
  }

  /**
   * Called when the graphic that has this object interactor is exited.
   * That is, when this object interactor stops receiving events.
   */
  Override
  public void onExit(IlvGraphic sel, IlvObjectInteractorContext context)
  {
    // System.err.println("On exit " + sel);
  }


  /**
   * This method is called by the view when the view is drawn.
   * <p>
   * This overridden implementation of the method from the base class
   * calls the <code>drawGhost</code> method.
   *
   * @param obj The graphic object to which the object interactor is associated.
   * @param g The <code>Graphics</code> where <code>obj</code> is drawn.
   * @param context The interactor context.
   */
  Override
  public void handleExpose(IlvGraphic obj,
                           Graphics g, 
                           IlvObjectInteractorContext context) 
  {
    drawGhost(obj, g, context);
  }

  /**
   * Draws the graphic object in xor mode while moving.
   */
  protected void drawGhost(IlvGraphic obj, Graphics g,
                           IlvObjectInteractorContext context)
  {

    if (mrect != null) {
      g.setColor(context.getDefaultGhostColor());
      g.setXORMode(context.getDefaultXORColor());
      IlvTransformer t = context.getTransformer();
      IlvRect r = obj.boundingBox(t);
      IlvTransformer t1 = new IlvTransformer(new IlvPoint(mrect.x - r.x,
                                                          mrect.y -r.y));
      t.compose(t1);
      obj.draw(g, t);
    }
  }

  /**
   * Invalidates the region where the ghost is drawn.
   */
  private void invalidateGhost(IlvGraphic obj, 
                               IlvObjectInteractorContext context)
  {
    if (obj == null || context == null)
      return; // for safety
    
    if (mrect == null || mrect.width == 0 || mrect.height == 0)
      return;
    
    IlvRect invalidRegion = new IlvRect(mrect);
    context.repaint(invalidRegion);
  }
  
  public static void main(String[] args)
  {
    // 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);

    // Sun recommends that to put the entire GUI initialization into the
    // AWT thread
    SwingUtilities.invokeLater(
      new Runnable() {
        Override
        public void run() {
          JFrame f = new JFrame();
          f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          f.setSize(new Dimension(300, 300));
          IlvManager mgr = new IlvManager();
          mgr.addAccelerator(new IlvIdentityAccelerator(
            KeyEvent.KEY_PRESSED, KeyEvent.VK_I, 0));
          mgr.addAccelerator(new IlvZoomOutAccelerator(
            KeyEvent.KEY_PRESSED, KeyEvent.VK_U, KeyEvent.CTRL_MASK));
          mgr.addAccelerator(new IlvZoomInAccelerator(
            KeyEvent.KEY_PRESSED, KeyEvent.VK_Z, KeyEvent.CTRL_MASK));
          mgr.addAccelerator(new IlvFitToSizeAccelerator(
            KeyEvent.KEY_PRESSED, KeyEvent.VK_F, 0));
          IlvGraphic g = new IlvRectangle(new IlvRect(10, 10, 30, 30));
          g.setFillOn(true);
          g.setStrokeOn(true);
          mgr.addObject(g, false);
          g.setObjectInteractor(IlvObjectInteractor.Get("MoveObjectInteractor"));
          IlvManagerView v = new IlvManagerView(mgr);
          f.getContentPane().setLayout(new BorderLayout());
          f.getContentPane().add("Center", v);
          f.setVisible(true);
        }
      });
  }
}