/*
 * Open Source Physics software is free software as described near the bottom of this code file.
 *
 * For additional information and documentation on Open Source Physics please see:
 * <http://www.opensourcephysics.org/>
 */

package org.opensourcephysics.display2d;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
import org.opensourcephysics.display.InteractivePanel;
import org.opensourcephysics.display.axes.XAxis;
import org.opensourcephysics.display.axes.XYAxis;

public class ComplexColorMapper {
  static final double PI2 = Math.PI*2;
  static final double COLOR_ERR = 1.0E-9;
  private double ceil;
  private Color ceilColor = Color.lightGray;
  private JFrame legendFrame;
  int[] reds = new int[256];
  int[] greens = new int[256];
  int[] blues = new int[256];
  protected ZExpansion zMap = null;

  /**
   * Constructor ComplexColorMapper
   * @param _ceil
   */
  public ComplexColorMapper(double _ceil) {
    ceil = _ceil;
    if(zMap!=null) {
      zMap.setMinMax(0, ceil);
    }
    initColors();
  }

  public static JFrame showPhaseLegend() {
    InteractivePanel dp = new InteractivePanel();
    dp.setPreferredSize(new java.awt.Dimension(300, 66));
    dp.setPreferredGutters(0, 0, 0, 35);
    dp.setClipAtGutter(false);
    JFrame legendFrame = new JFrame("Complex Phase"); //$NON-NLS-1$
    legendFrame.setResizable(false);
    legendFrame.setContentPane(dp);
    int numPts = 360;
    GridPointData pointdata = new GridPointData(numPts, 1, 3);
    double[][][] data = pointdata.getData();
    double theta = -Math.PI, delta = 2*Math.PI/(numPts);
    for(int i = 0, n = data.length; i<n; i++) {
      data[i][0][2] = 0.999;
      data[i][0][3] = Math.cos(theta);
      data[i][0][4] = Math.sin(theta);
      theta += delta;
    }
    pointdata.setScale(-Math.PI, Math.PI, 0, 1);
    Plot2D plot = new ComplexGridPlot(pointdata);
    plot.setShowGridLines(false);
    plot.update();
    dp.addDrawable(plot);
    XAxis xaxis = new XAxis(""); //$NON-NLS-1$
    xaxis.setLocationType(XYAxis.DRAW_AT_LOCATION);
    xaxis.setLocation(-0.5);
    xaxis.setEnabled(true);
    dp.addDrawable(xaxis);
    legendFrame.pack();
    legendFrame.setVisible(true);
    return legendFrame;
  }

  /**
   * Shows the phase legend.
   */
  public JFrame showLegend() {
    InteractivePanel dp = new InteractivePanel();
    dp.setPreferredSize(new java.awt.Dimension(300, 66));
    dp.setPreferredGutters(0, 0, 0, 35);
    dp.setClipAtGutter(false);
    if((legendFrame==null)||!legendFrame.isDisplayable()) {
      legendFrame = new JFrame("Complex Phase"); //$NON-NLS-1$
    }
    legendFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    legendFrame.setResizable(false);
    legendFrame.setContentPane(dp);
    int numPts = 360;
    GridPointData pointdata = new GridPointData(numPts, 1, 3);
    double[][][] data = pointdata.getData();
    double theta = -Math.PI, delta = 2*Math.PI/(numPts);
    for(int i = 0, n = data.length; i<n; i++) {
      data[i][0][2] = 0.999;
      data[i][0][3] = Math.cos(theta);
      data[i][0][4] = Math.sin(theta);
      theta += delta;
    }
    pointdata.setScale(-Math.PI, Math.PI, 0, 1);
    Plot2D plot = new ComplexGridPlot(pointdata);
    plot.setShowGridLines(false);
    plot.update();
    dp.addDrawable(plot);
    XAxis xaxis = new XAxis(""); //$NON-NLS-1$
    xaxis.setLocationType(XYAxis.DRAW_AT_LOCATION);
    xaxis.setLocation(-0.5);
    xaxis.setEnabled(true);
    dp.addDrawable(xaxis);
    legendFrame.pack();
    legendFrame.setVisible(true);
    return legendFrame;
  }

  /**
   * Sets the z scale.
   * @param _ceil
   */
  public void setScale(double _ceil) {
    ceil = _ceil;
    if(zMap!=null) {
      zMap.setMinMax(0, ceil);
    }
  }

  /**
   * Converts a double to color components.
   *
   * @param samples double[]
   * @param rgb byte[]
   * @return byte[]
   */
  public byte[] samplesToComponents(double[] samples, byte[] rgb) {
    Color color = samplesToColor(samples);
    rgb[0] = (byte) color.getRed();
    rgb[1] = (byte) color.getGreen();
    rgb[2] = (byte) color.getBlue();
    return rgb;
  }

  /**
   * Converts a phase angle in the range [-Pi,Pi] to hue, saturation, and brightness.
   *
   * @param phi phase angle
   * @return the HSB color
   */
  public Color phaseToColor(double phi) {
    float b = 1; // brightness
    float h = (float) ((Math.PI+phi)/PI2);
    int index = ((int) (255*h));
    return new Color((int) (b*reds[index]), (int) (b*greens[index]), (int) (b*blues[index]));
  }

  /**
   * Converts a complex number to hue, saturation, and brightness.
   * @param re
   * @param im
   * @return the HSB color
   */
  public Color complexToColor(double re, double im) {
    float b = 1; // brightness
    float h = (float) ((Math.PI+Math.atan2(im, re))/PI2);
    int index = ((int) (255*h));
    return new Color((int) (b*reds[index]), (int) (b*greens[index]), (int) (b*blues[index]));
  }

  /**
   * Converts an array of samples to hue, saturation, and brightness.
   * Samples contains magnitude, re, and im.
   * @param samples
   * @return the HSB color
   */
  public Color samplesToColor(double[] samples) {
    double zval = samples[0];
    if(zMap!=null) {
      zval = zMap.evaluate(zval);
    }
    if(zval<=0) {
      return Color.black;
    } else if((zMap==null)&&(zval>ceil+COLOR_ERR)) {
      return ceilColor;
    } else {
      zval = Math.min(zval, ceil);
    }
    float b = (float) (zval/ceil);                                        // brightness
    float h = (float) ((Math.PI+Math.atan2(samples[2], samples[1]))/PI2); // hue
    int index = ((int) (255*h));
    return new Color((int) (b*reds[index]), (int) (b*greens[index]), (int) (b*blues[index]));
  }

  /**
   * Converts a vertex point array of samples to hue, saturation, and brightness.
   *
   * A vertex containing x, y, magnitude, re, and im.
   *
   * @param vertex the point
   * @return the HSB color
   */
  public Color pointToColor(double[] vertex) {
    double zval = vertex[2];
    if(zMap!=null) {
      zval = zMap.evaluate(zval);
    }
    if(zval<=0) {
      return Color.black;
    } else if(zval>ceil+COLOR_ERR) {
      return ceilColor;
    }
    float b = (float) (zval/ceil);
    float h = (float) ((Math.PI+Math.atan2(vertex[4], vertex[3]))/PI2);
    int index = ((int) (255*h));
    return new Color((int) (b*reds[index]), (int) (b*greens[index]), (int) (b*blues[index]));
    // return  Color.getHSBColor(h,1,b);
  }

  /**
   * Sets map for z values.
   *
   * @param map ZExpansion
   */
  public void setZMap(ZExpansion map) {
    zMap = map;
    if(zMap!=null) {
      zMap.setMinMax(0, ceil);
    }
  }

  /**
   * Gets the ceiling color.
   * @return
   */
  public double getCeil() {
    return ceil;
  }

  /**
   * Gets the ceiling color.
   * @return
   */
  public Color getCeilColor() {
    return ceilColor;
  }

  /**
   * Sets the ceiling.
   *
   * @param _ceilColor
   */
  public void setCeilColor(Color _ceilColor) {
    ceilColor = _ceilColor;
  }

  private void initColors() {
    double pi = Math.PI;
    for(int i = 0; i<256; i++) {
      double val = Math.abs(Math.sin(pi*i/255));
      blues[i] = (int) (255*val*val);
      val = Math.abs(Math.sin(pi*i/255+pi/3));
      greens[i] = (int) (255*val*val*Math.sqrt(val));
      val = Math.abs(Math.sin(pi*i/255+2*pi/3));
      reds[i] = (int) (255*val*val);
    }
  }

}

/*
 * Open Source Physics software is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public License (GPL) as
 * published by the Free Software Foundation; either version 2 of the License,
 * or(at your option) any later version.

 * Code that uses any portion of the code in the org.opensourcephysics package
 * or any subpackage (subdirectory) of this package must must also be be released
 * under the GNU GPL license.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
 * or view the license online at http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (c) 2007  The Open Source Physics project
 *                     http://www.opensourcephysics.org
 */
