package nxm.sys.libg;

import nxm.sys.inc.Indexable;
import nxm.sys.inc.Tablizable;
import nxm.sys.lib.Convert;
import nxm.sys.lib.MidasException;
import nxm.sys.lib.Shell;
import nxm.sys.lib.Table;

/** A class to hold Dragbox or Zoom information used in conjunction with <code>PLOT</code>. This
    class implements {@link Indexable} to provide backwards compatibility with NeXtMidas 2.2.0,
    this behavior is deprecated and this class will not implement {@link Indexable} in future
    releases of NeXtMidas.

    @see MPlot

    @author  Jay Hallam
    @author  J. Czechowski IV
    @since   NeXtMidas 2.3.0
*/
public class DragBox implements Indexable, Tablizable {
  /** Minimum X-axis value */ private double xmin;
  /** Maximum X-axis value */ private double xmax;
  /** Minimum Y-axis value */ private double ymin;
  /** Maximum Y-axis value */ private double ymax;
  /** Minimum time value (usually for a raster) */ private double tmin;
  /** Maximum time value (usually for a raster) */ private double tmax;
  /** Geodetic viewer params (null when not in GEODETIC view) */ private double[] geov;

  /** Top-most time value (usually for a raster)
      Usually equal to {@link #tmin}, except when box crosses pass boundary
      @since NeXtMidas 2.9.0
   */
  private double t1;

  /** Bottom-most time value (usually for a raster)
      Usually equal to {@link #tmax}, except when box crosses pass boundary
      @since NeXtMidas 2.9.0
   */
  private double t2;

  /** Package-private, only used by MPlot.
      @param values The {xmin, xmax, ymin, ymax} values from MPlot.
   */
  public DragBox (double[] values) {
    this(values[0], values[1], values[2], values[3]);
  }

  /** Creates a new instance. If the min and max values are in the incorrect order they will be
      swapped.
      @param xmin Minimum X-axis value.
      @param xmax Maximum X-axis value.
      @param ymin Minimum Y-axis value.
      @param ymax Maximum Y-axis value.
   */
  public DragBox (double xmin, double xmax, double ymin, double ymax) {
    this.xmin = Math.min(xmin, xmax);
    this.xmax = Math.max(xmin, xmax);
    this.ymin = Math.min(ymin, ymax);
    this.ymax = Math.max(ymin, ymax);
    this.tmin = Double.NaN; // Not set yet
    this.tmax = Double.NaN; // Not set yet
    this.t1   = Double.NaN; // Not set yet
    this.t2   = Double.NaN; // Not set yet
  }

  /** Creates a new instance. If the min and max values are in the incorrect order they will be
      swapped. Used by MPlot when in GEODETIC view, because it needs reference dimensions relative
      to the current viewport to handle a zoom.
      @param xmin Minimum X-axis value.
      @param xmax Maximum X-axis value.
      @param ymin Minimum Y-axis value.
      @param ymax Maximum Y-axis value.
      @param geov Geodetic viewer params (null when not in GEODETIC view)
   */
  DragBox (double xmin, double xmax, double ymin, double ymax, double[] geov) {
    this(xmin, xmax, ymin, ymax);
    this.geov = geov;
  }

  /** Sets the minimum X-axis value.
   *  @param in the minimum X-axis value */
  public void setXMin (double in) {this.xmin = in;}
  /** Gets the minimum X-axis value.
   *  @return the minimum X-axis value */
  public double getXMin ()        {return this.xmin;}

  /** Sets the maximum X-axis value.
   *  @param in the maximum X-axis value */
  public void setXMax (double in) {this.xmax = in;}
  /** Gets the maximum X-axis value.
   *  @return the maximum X-axis value */
  public double getXMax ()        {return this.xmax;}

  /** Sets the minimum Y-axis value.
   *  @param in the minimum Y-axis value */
  public void setYMin (double in) {this.ymin = in;}
  /** Gets the minimum Y-axis value.
   *  @return the minimum Y-axis value */
  public double getYMin ()        {return this.ymin;}

  /** Sets the maximum Y-axis value.
   *  @param in the maximum Y-axis value. */
  public void setYMax (double in) {this.ymax = in;}
  /** Gets the maximum Y-axis value.
   *  @return the maximum Y-axis value. */
  public double getYMax ()        {return this.ymax;}

  /** Sets the minimum time value (usually for a raster).
   *  @param in the minimum time value */
  public void setTMin (double in) {this.tmin = in;}
  /** Gets the minimum time value, returns {@link Double#NaN} if not set.
   *  @return the minimum time value */
  public double getTMin ()        {return this.tmin;}

  /** Sets the maximum time value (usually for a raster).
   *  @param in the maximum time value */
  public void setTMax (double in) {this.tmax = in;}
  /** Gets the maximum time value, returns {@link Double#NaN} if not set.
   *  @return the maximum time value */
  public double getTMax ()        {return this.tmax;}

  /** Sets the top-most time value (usually for a raster).
   *  @param in the top-most time value */
  public void setT1 (double in) { t1 = in; }
  /** Gets the top-most time value, returns {@link Double#NaN} if not set.
   *  @return the top-most time value */
  public double getT1 ()        { return t1;}

  /** Sets the bottom-most time value (usually for a raster).
   *  @param in the bottom-most time value */
  public void setT2 (double in) { t2 = in; }
  /** Gets the bottom-most time value, returns {@link Double#NaN} if not set.
   *  @return the bottom-most time value */
  public double getT2 ()        { return t2; }

  // These methods are only used by MPlot when in GEODETIC view
  /** Is in Geoetic view */
  boolean isGeoView() { return (geov != null); }
  /** Gets the minimum Geodetic X value, returns {@link Double#NaN} if not set. */
  double getGeoXMin() { return (isGeoView())? geov[0]: Double.NaN; }
  /** Gets the maximum Geodetic X value, returns {@link Double#NaN} if not set. */
  double getGeoXMax() { return (isGeoView())? geov[1]: Double.NaN; }
  /** Gets the minimum Geodetic Y value, returns {@link Double#NaN} if not set. */
  double getGeoYMin() { return (isGeoView())? geov[2]: Double.NaN; }
  /** Gets the maximum Geodetic Y value, returns {@link Double#NaN} if not set. */
  double getGeoYMax() { return (isGeoView())? geov[3]: Double.NaN; }
  /** Sets the minimum Geodetic X value, only if in geodetic view. */
  void setGeoXMin(double in) { if (isGeoView()) geov[0] = in; }
  /** Sets the maximum Geodetic X value, only if in geodetic view. */
  void setGeoXMax(double in) { if (isGeoView()) geov[1] = in; }
  /** Sets the minimum Geodetic Y value, only if in geodetic view. */
  void setGeoYMin(double in) { if (isGeoView()) geov[2] = in; }
  /** Sets the maximum Geodetic Y value, only if in geodetic view. */
  void setGeoYMax(double in) { if (isGeoView()) geov[3] = in; }

  /** Creates a table with all the XMIN,XMAX,YMIN,YMAX,etc values (those not set will be omitted).
      @return An applicable table.
   */
  public Table toTable () {
    Table tbl = new Table();

    tbl.put("XMIN", getXMin());
    tbl.put("XMAX", getXMax());
    tbl.put("YMIN", getYMin());
    tbl.put("YMAX", getYMax());
    if (!Double.isNaN(getTMin())) { tbl.put("TMIN", getTMin()); }
    if (!Double.isNaN(getTMax())) { tbl.put("TMAX", getTMax()); }
    if (!Double.isNaN(getT1()))   { tbl.put("T1",   getT1()); }
    if (!Double.isNaN(getT2()))   { tbl.put("T2",   getT2()); }

    return tbl;
  }

  /** Creates a string version of this object useful for debugging. */
  public String toString () {
    return "DragBox:" + toTable();
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////
  // DEPRECATED METHODS
  //////////////////////////////////////////////////////////////////////////////////////////////////
  private static final void deprecate (String msg) {
    Shell.getSharedMidasContext().deprecate("DragBox: "+msg);
  }

  /** @deprecated This method is only here for backwards-compatibility with NeXtMidas 2.2.0. */ @Deprecated
  public Object getIndex (int index) {
    double val = 0;
    String str = null;
    if (isGeoView()) {
      switch (index) {
        case 0:  val = getGeoXMin(); str = "XMIN"; break;
        case 1:  val = getGeoXMax(); str = "XMAX"; break;
        case 2:  val = getGeoYMin(); str = "YMIN"; break;
        case 3:  val = getGeoYMax(); str = "YMAX"; break;
        case 4:  val = getXMin();    str = "XMIN"; break;
        case 5:  val = getXMax();    str = "XMAX"; break;
        case 6:  val = getYMax();    str = "YMAX"; break; // Old code had this backwards, here
        case 7:  val = getYMin();    str = "YMIN"; break; // we keep the same so old macros work
        default: throw new MidasException("DragBox: Invalid index "+index);
      }
    } else {
      switch (index) {
        case 0:  val = getXMin(); str = "XMIN"; break;
        case 1:  val = getXMax(); str = "XMAX"; break;
        case 2:  val = getYMin(); str = "YMIN"; break;
        case 3:  val = getYMax(); str = "YMAX"; break;
        case 4:  val = getTMin(); str = "TMIN"; break;
        case 5:  val = getTMax(); str = "TMAX"; break;
        default: throw new MidasException("DragBox: Invalid index "+index);
      }
    }

    if (isGeoView() && (index <= 3)) {
      deprecate("Getting Geodetic Viewer values is deprecated, use "+str+".");
    } else {
      deprecate("Getting DRAGBOX/ZOOM value using "+index+" is deprecated, use "+str+".");
    }
    return Double.valueOf(val);
  }

  /** @deprecated This method is only here for backwards-compatibility with NeXtMidas 2.2.0. */ @Deprecated
  public void setIndex (int index, Object value) {
    double val = Convert.o2d(value);
    String str = null;

    if (isGeoView()) {
      switch (index) {
        case 0:  setGeoXMin(val); str = "XMIN"; break;
        case 1:  setGeoXMax(val); str = "XMAX"; break;
        case 2:  setGeoYMin(val); str = "YMIN"; break;
        case 3:  setGeoYMax(val); str = "YMAX"; break;
        case 4:  setXMin(val);    str = "XMIN"; break;
        case 5:  setXMax(val);    str = "XMAX"; break;
        case 6:  setYMax(val);    str = "YMAX"; break; // Old code had this backwards, here
        case 7:  setYMin(val);    str = "YMIN"; break; // we keep the same so old macros work
        default: throw new MidasException("DragBox: Invalid index "+index);
      }
    } else {
      switch (index) {
        case 0:  setXMin(val); str = "XMIN"; break;
        case 1:  setXMax(val); str = "XMAX"; break;
        case 2:  setYMin(val); str = "YMIN"; break;
        case 3:  setYMax(val); str = "YMAX"; break;
        case 4:  setTMin(val); str = "TMIN"; break;
        case 5:  setTMax(val); str = "TMAX"; break;
        default: throw new MidasException("DragBox: Invalid index "+index);
      }
    }

    if (isGeoView() && (index <= 3)) {
      deprecate("Setting Geodetic Viewer values is deprecated, use "+str+".");
    } else {
      deprecate("Setting DRAGBOX/ZOOM value using "+index+" is deprecated, use "+str+".");
    }
  }

  /** @deprecated This method is only here for backwards-compatibility with NeXtMidas 2.2.0. */ @Deprecated
  public int getSize () {
    return (isGeoView())? 8: 6;
  }
}
