/*
 * 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 java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;

import ilog.views.IlvManagerView;
import ilog.views.IlvManagerViewInteractor;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.IlvTransformer;
import ilog.views.interactor.IlvPermanentInteractorInterface;


/**
 * This interactor displays a rectangle which locks every nodes that intersect it. 
 * @author kaplan
 * @since JViews 8.0
 * 
 */
public class RectangleInteractor extends IlvManagerViewInteractor implements IlvPermanentInteractorInterface {
  private static final String ILLEGAL_SIZE = "size should be strictly greater than zero";


    private int _size = 128;
    final private IlvRect _rect = new IlvRect(0, 0, 128, 128);
    private IlvRect _previousRect = null;
    private boolean _dragging = false;
    private boolean _permanent = true;
    private boolean _allowDraw = true;
    private ContentDemo _source;
    
    /**
     * Constructs and initializes a new instance of the 
     * <code>RectangleInteractor</code>.
     * @param source 
     */
    public RectangleInteractor(ContentDemo source) {
      enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
      _source = source;
    }


    protected void attach(IlvManagerView view) {
      view.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));      
      super.attach(view);
    }
    
    /**
     * Called when the interactor is detached from the view.
     */
    protected void detach() {
      if (_dragging && _previousRect != null) { // remove traces 
        // _previousRect is stored in view's coords
        _dragging = false;
        getManagerView().invalidateRect(_previousRect);
        _previousRect = null;
        getManagerView().reDrawViews();
      }
      getManagerView().setCursor(Cursor.getDefaultCursor());
      super.detach();
    }

    /** 
     * Sets the size of the rectangle.
     * @param size The size to be set, in manager view coordinate values.
     * @see #getSize
     */
    public final void setSize(int size) {
      if (size < 0)
        throw new IllegalArgumentException(ILLEGAL_SIZE);
      if (size != _size) {
        int oldsize = _size;
        _size = size;
        _rect.width = _size;
        _rect.height = _size;
        if (_dragging) {
          if (_previousRect != null) {
            getManagerView().invalidateRect(_previousRect);
            _previousRect = null;
          }
          _rect.move(_rect.x + (oldsize - size) / 2, _rect.y + (oldsize - size) / 2);
          getManagerView().invalidateRect(_rect);
          getManagerView().reDrawViews();
        }
      }
    }

    /**
     * Returns the size of the rectangle in manager view coordinates.
     * The default value is <code>128</code>.
     * @return size
     * @see #setSize
     * @beaninfo
     */
    public final int getSize() {
      return _size;
    }

 
 
    /**
     * Processes the mouse events. The rectangle appears when a mouse button is 
     * pressed. It is removed when a mouse button is released.
     * @param e The mouse event.
     */
    protected void processMouseEvent(MouseEvent e) {
      super.processMouseEvent(e);
      if (e.getID() == MouseEvent.MOUSE_PRESSED) {
        _dragging = true;
        final IlvPoint p = new IlvPoint(e.getX(), e.getY());
        _rect.move(p.x - _rect.width * .5f, p.y - _rect.height * .5f);
        getManagerView().invalidateRect(getDrawingRect());
        getManagerView().reDrawViews();
        
        callLockArea();
      }

      if (e.getID() == MouseEvent.MOUSE_RELEASED) {
        _dragging = false;
        if (_previousRect != null)
          getManagerView().invalidateRect(_previousRect);
        _previousRect = null;
        getManagerView().reDrawViews();
        if (!_permanent && getManagerView() != null)
          getManagerView().popInteractor();
      }
    }


    private void callLockArea() {
      IlvRect drect = getDrawingRect();
      IlvTransformer transformer = getTransformer();
      transformer.boundingBox(drect, true);
      // callback 
      _source.lockArea(drect);
    }

    /**
     * Processes the mouse motion events. When the mouse is dragged, the rectangle
     * is moved over the view.
     * @param e The mouse motion event.
     */
    protected void processMouseMotionEvent(MouseEvent e) {
      super.processMouseMotionEvent(e);
      if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
        IlvManagerView view = getManagerView();
        if (view != null) {
          IlvTransformer transformer = getTransformer();
          final IlvPoint p = new IlvPoint(e.getX(), e.getY());
          _rect.move(p.x - _rect.width * .5f, p.y - _rect.height * .5f);

          IlvRect drect = getDrawingRect();
          transformer.boundingBox(drect, true);
          if (_previousRect != null)
            transformer.boundingBox(_previousRect, true);

          _allowDraw = false;
          ensureVisible(p);
          _allowDraw = true;

          // transformer may have changed
          transformer = getTransformer();
          transformer.boundingBox(drect, false);
          if (_previousRect != null)
            transformer.boundingBox(_previousRect, false);

          if (_previousRect != null)
            // _previousRect is stored in view's coords
            view.invalidateRect(_previousRect);
          // and invalidate the new bbox 
          view.invalidateRect(drect);

          // find a better way to do this
          _previousRect = null;

          // callback 
          callLockArea();
          
          // redraw the region of the view where our
          // graphic object has been drawn the previous time, as well
          // as the graphic object in its new position
          view.reDrawViews();
        }
      }
    }

    /**
     * Called by the view when the view
     * is drawn,;it actually calls the method that draws the rectangle
     * using {@link #drawSensitiveArea}.
     * @param g The view graphics.
     */
    protected void handleExpose(Graphics g) {
      if (_dragging && _allowDraw) {
        Rectangle clip = g.getClipBounds();
        Dimension size = getManagerView().getSize();
        Rectangle area = new Rectangle(Math.max(0, (int) Math.floor(_rect.x)), Math.max(0, (int) Math.floor(_rect.y)), Math.min((int) Math.floor(size.width - _rect.x), (int) Math.floor(_rect.width)), Math.min((int) Math.floor(size.height - _rect.y), (int) Math.floor(_rect.height)));
        if (clip.contains(area)) {
          draw(g);
        } else if (clip.intersects(area)) {
          // we are updating a clipping zone that may impact the rectangle
          // even if the rectangle is not entirely in the clipping zone.
          getManagerView().invalidateRect(_rect);
          getManagerView().reDrawViews();
          draw(g);
        }
      }
    }

    /**
     * Returns a copy of the rectangle that drives this interactor.
     * @return drawing rectangle
     */
    protected IlvRect getDrawingRect() {
      return new IlvRect(_rect);
    }

    private void draw(Graphics g) {
      if (_previousRect == null)
        _previousRect = new IlvRect();
      IlvRect currentRect = getDrawingRect();
      _previousRect.reshape(currentRect.x, currentRect.y, currentRect.width, currentRect.height);
      drawSensitiveArea(g);
    }

    /**
     * Draws the rectangle. If you redefine this method
     * to add additional drawings, you must also redefine the 
     * {@link #getDrawingRect} method to return a rectangle containing
     * the additional drawings.
     * @param g The view graphics.
     */
    protected void drawSensitiveArea(Graphics g) {
      int cx = (int) Math.floor(_rect.x + _rect.width / 2);
      int cy = (int) Math.floor(_rect.y + _rect.height / 2);
      int x = cx - (int) Math.floor(_rect.width / 2);
      int y = cy - (int) Math.floor(_rect.height / 2);
      int w = (int) Math.floor(_rect.width);
      int h = (int) Math.floor(_rect.height);
      g.setColor(Color.black);
      g.drawRect(x,y,w,h);
    }


    // IlvPermanentInteractorInterface implementation

    /**
     * Returns <code>false</code> if the interactor will be removed from the
     * view once the object was created.
     * The default value is <code>false</code>.
     * @beaninfo
     */
    public final boolean isPermanent() {
      return _permanent;
    }

    /**
     * Allows you to specify if the interactor will or will not be removed 
     * from the view once the object is created.
     * The default value is <code>false</code>.
     * @param permanent If <code>true</code> the interactor will be removed, 
     *                  otherwise not.
     */
    public final void setPermanent(boolean permanent) {
      _permanent = permanent;
    }


}