/*
 * 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.
 */

package interactor;

import java.awt.AWTEvent;
import java.awt.event.*;
import java.util.Locale;

import ilog.views.chart.IlvChartInteractor;
import ilog.views.chart.IlvDisplayPoint;
import ilog.views.chart.event.ChartHighlightInteractionEvent;
import ilog.views.util.IlvResourceUtil;

/**
 * An interactor that notifies the listeners when the mouse cursor is
 * over a point.
 * <p> The registered name of this interactor is "Highlight".
 * @see ilog.views.chart.IlvChartInteractor
 */
public class IlvChartHighlightInteractor extends IlvChartDataInteractor
{
  // ============================ Metainformation ============================

  static {
    IlvChartInteractor.register("Highlight",
                                IlvChartHighlightInteractor.class);
  }

  /**
   * Returns a localized name for this interactor class.
   */
  public static String getLocalizedName(Locale locale)
  {
    return IlvResourceUtil.getBundle("messages",
                                     IlvChartHighlightInteractor.class,
                                     locale)
           .getString("IlvChartHighlightInteractor");
  }


  // ===================== Customization, Bean Properties =====================

  /**
   * Highlight mode used to highlight points.
   * @see #setHighlightMode
   */
  public static final int HIGHLIGHT_POINT  = 0;

  /**
   * Highlight mode used to highlight data sets.
   * @see #setHighlightMode
   */
  public static final int HIGHLIGHT_SERIES = 1;

  private int _highlightMode = HIGHLIGHT_POINT;

  /**
   * Returns the highlight mode.
   * @see #setHighlightMode
   */
  public int getHighlightMode()
  {
    return _highlightMode;
  }

  /**
   * Sets the highlight mode.
   * @param highlightMode The highlight mode. If set to
   * {@link #HIGHLIGHT_POINT}, a pair of highlight and unhighlight events is
   * sent whenever the mouse enters and leaves a display point. If set to
   * {@link #HIGHLIGHT_SERIES}, the highlight and unhighlight events
   * are sent only if the new display point does not belong to the
   * same data set as the previously highlighted point. The default mode is
   * <code>HIGHLIGHT_POINT</code>.
   * @see #getHighlightMode
   * @throws IllegalArgumentException The mode is invalid.
   */
  public void setHighlightMode(int highlightMode)
  {
    if (highlightMode != HIGHLIGHT_POINT && highlightMode != HIGHLIGHT_SERIES)
      throw new IllegalArgumentException("Bad Highlight Mode");
    _highlightMode = highlightMode;
  }


  // ============================ State Variables ============================

  private transient IlvDisplayPoint _highlightedPoint;

  /**
   * Returns the current highlighted point.
   */
  public IlvDisplayPoint getHighlightedPoint()
  {
    return (_highlightedPoint != null
            ? (IlvDisplayPoint)_highlightedPoint.clone()
            :  null);
  }

  /**
   * Sets the current highlighted point.
   */
  public void setHighlightedPoint(IlvDisplayPoint point)
  {
    _highlightedPoint = (point != null ? (IlvDisplayPoint)point.clone() : null);
  }


  // ========================= Mouse event handling =========================

  /**
   * Returns whether the mouse event occurred at the specified
   * location should be handled by the interactor.
   * By default, returns <code>true</code>.
   */
  Override
  public boolean isHandling(int x, int y)
  {
    // required to take into account the cases when the cursor
    // leaves  the plotrect
    return true;
  }

  /**
   * Processes a mouse event.
   */
  Override
  public void processMouseEvent(MouseEvent event)
  {
    switch (event.getID()) {
      case MouseEvent.MOUSE_ENTERED:
        if (event.getButton() == MouseEvent.NOBUTTON)
          processMouseMovedEvent(event);
        break;
      case MouseEvent.MOUSE_EXITED:
        // Just to remove the existing tooltip. But do
        // not consume the event.
        if (_highlightedPoint != null) {
          doIt(_highlightedPoint, false, event);
        }
        // reinit for oldPoint
        _highlightedPoint = null;
        break;
    }
  }

  /**
   * Processes a mouse motion event.
   */
  Override
  public void processMouseMotionEvent(MouseEvent event)
  {
    switch (event.getID()) {
      case MouseEvent.MOUSE_MOVED:
        processMouseMovedEvent(event);
        break;
    }
  }

  /**
   * Processes a mouse motion event with no button pressed.
   * @param event An event of type <code>MOUSE_MOVED</code> or
   *              <code>MOUSE_ENTERED</code>.
   * 
   */
  public void processMouseMovedEvent(MouseEvent event)
  {
    IlvDisplayPoint oldPoint = _highlightedPoint;
    _highlightedPoint = pickData(createDataPicker(event));
    if (_highlightedPoint != null) {
      // if new point, notify changes
      if (oldPoint != null) {
        if (!checkEqual(_highlightedPoint, oldPoint)) {
          // turn off old point
          doIt(oldPoint, false, event);
          // highlight new point
          doIt(_highlightedPoint, true, event);
        } else
          return;
      } else {
        // highlight new point
        doIt(_highlightedPoint, true, event);
      }
      if (isConsumeEvents())
        event.consume();
    } else if (oldPoint != null) {
      // nomore point... leaving oldpoint
      doIt(oldPoint, false, event);
    }
  }

  /**
   * Checks whether the two points are considered equal for (un)highlight
   * purposes.
   */
  private boolean checkEqual(IlvDisplayPoint oldPoint,
                             IlvDisplayPoint newPoint)
  {
    if (getHighlightMode()==HIGHLIGHT_SERIES)
      return (oldPoint.getDataSet() == newPoint.getDataSet());
    else // HIGHLIGHT_POINT
      return oldPoint.equals(newPoint);
  }


  // ========================== Overridable behavior ==========================

  /**
   * Called by the <code>processXXXEvent()</code> methods when the highlight
   * state of the given point has changed. By default, fires a
   * {@link ilog.views.chart.event.ChartHighlightInteractionEvent}.
   * @param highlightedPoint The display point.
   * @param isHighlighted The type of notification that occurred.
   * @param event The <code>mouseEvent</code> that leads to this notification.
   * @see ilog.views.chart.IlvChartInteractor#addChartInteractionListener
   * @see ilog.views.chart.event.ChartInteractionEvent
   */
  protected void doIt(IlvDisplayPoint highlightedPoint, boolean isHighlighted,
                      MouseEvent event)
  {
    IlvDisplayPoint pt = (IlvDisplayPoint)highlightedPoint.clone();
    fireChartInteractionEvent(
        new ChartHighlightInteractionEvent(this, pt, isHighlighted));
  }


  // ============================= Constructors =============================

  /**
   * Creates a new <code>IlvChartHighlightInteractor</code> object linked to
   * the first Y-axis index.
   */
  public IlvChartHighlightInteractor()
  {
    this(0);
  }

  /**
   * Creates a new <code>IlvChartHighlightInteractor</code> object linked to
   * the given axis.
   */
  public IlvChartHighlightInteractor(int yAxisIndex)
  {
    super(yAxisIndex,0);
    enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK |
                 AWTEvent.MOUSE_EVENT_MASK);
    setXORGhost(false);
  }

}