/*
 * 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.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
 * @proofread
 */
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;
  }

  Override
  protected void attach(IlvManagerView view) {
    view.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
    super.attach(view);
  }

  /**
   * Called when the interactor is detached from the view.
   */
  Override
  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.
   */
  Override
  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 * .5, p.y - _rect.height * .5);
      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.
   */
  Override
  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 * .5, p.y - _rect.height * .5);

        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.
   */
  Override
  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
   */
  Override
  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.
   */
  Override
  public final void setPermanent(boolean permanent) {
    _permanent = permanent;
  }

}