
/**
 * PlotDialog.java
 * Contains the graphs to plot the data.
 * Created: Jan 5, 2003
 *
 * @author Nathan Stevens
 * @version 0.5 8/10/2006 
 */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import java.util.*;

import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.event.ChartChangeEvent;
import org.jfree.chart.event.ChartChangeListener;
import org.jfree.chart.renderer.*;
import org.jfree.chart.renderer.xy.*;

public class PlotDialog extends JFrame implements ActionListener, ChangeListener, ChartChangeListener {
  private DataPointSelectedListener dsListener = null;
  private PlotEditorDialog editorDialog = null;
  private String exptype = null;
  private SpectraData[] sda = null;
  private String cname_1 = null;
  private String cname_2 = null;
  private String cname_3 = null;
  private String xlabel = null;
  private String ylabel = null;
  private String xunits = null;
  private String yunits = null;
  private JLabel xposLB = null;
  private JLabel yposLB = null;
  private JLabel dxLB = null;
  private JLabel dyLB = null;
  private boolean hasref = false;
  private ChartPanel mchartPanel = null;
  private ChartPanel rchartPanel = null;
  private ChartPanel currentChartPanel = null;
  private int dindex = 0;  // used to calculate delta values
  private float[] deltaX = new float[2];
  private float[] deltaY = new float[2];
  
  // boolean values that set how the plot looks.  These will have to be expanded in the
  // future for sure (4/12/06)
  private boolean log = false; // specifies whether to do log plot
  private boolean antialias = false; // specifies whether to draw the plots using antialias
  public boolean crosshair = false; // specifies whether to draw the crosshairs
  public float line_size = 1.5f;
  
  // constructor used to plot using user defined axis labels, units, and properties
  public PlotDialog(String title, SpectraData[] sda, String plotTitle, String[] axis, Properties prop) {
    super(title);
    this.sda = sda;
    
    // set the plot properties
    setPlotProperties(prop);
    
    cname_1 = plotTitle;
    xunits = " " + axis[0];
    yunits = " "  + axis[1];
    xlabel = axis[2] + " (" + xunits + " )";
    ylabel = axis[3] + " (" + yunits + " )";
    
    // create the axis labels and titles
    createGui();
  }
  
  // constructor used to plot using user defined axis labels and units
  public PlotDialog(String title, SpectraData[] sda, String plotTitle, String[] axis) {
    super(title);
    this.sda = sda;
    
    cname_1 = plotTitle;
    xunits = " " + axis[0];
    yunits = " "  + axis[1];
    xlabel = axis[2] + " (" + xunits + " )";
    ylabel = axis[3] + " (" + yunits + " )";
    
    // create the axis labels and titles
    createGui();
  }
  
  // Method that excepts a plotProperties object
  public PlotDialog(String title, String exptype, SpectraData[] sda, Properties prop) {
    super(title);
    this.exptype = exptype;
    this.sda = sda;
    
    // set the plot properties
    setPlotProperties(prop);
    
    // set the labels and plot titles now
    setPlotTitlesAndLabels();
    
    // now create user interface including the plots
    createGui();
  }
  
  // This is the main constructor
  public PlotDialog(String title, String exptype, SpectraData[] sda) {
    super(title);
    this.exptype = exptype;
    this.sda = sda;
    
    // set the titles and labes for the plots
    setPlotTitlesAndLabels();
    
    // create the plots and the user interfaces now
    createGui();
  }
  
  // Method to set the plot properties
  private void setPlotProperties(Properties prop) {
    String key = "log";
    String value =  prop.getProperty(key);
    if(value != null && value.equals("yes")) {
      log = true;
    }
    else {
      log = false;
    }
    
    key = "antialias";
    value =  prop.getProperty(key);
    if(value != null && value.equals("yes")) {
      antialias = true;
    }
    else {
      antialias = false;
    }
    
    key = "crosshair";
    value =  prop.getProperty(key);
    if(value != null && value.equals("yes")) {
      crosshair = true;
    }
    else {
      crosshair = false;
    }
    
    key = "line_size";
    value =  prop.getProperty(key);
    if(value != null) {
      line_size = Float.parseFloat(value);
    }
  }
  
  // this fuction will set some plot labels and titles depending on the type of data
  private void setPlotTitlesAndLabels() {
    if(exptype.equals("absorption")) {
      cname_1 = "Absorption Spectra";
      cname_2 = "Reference Spectra";
      xlabel = "Wavelength (nm)";
      ylabel = "Absorption Units";
      xunits = " nm";
      yunits = " au";
    }
    else if(exptype.equals("emission")) {
      cname_1 = "Emission Spectra";
      cname_2 = "Normalized Spectra";
      xlabel = "Wavelength (nm)";
      ylabel = "Intensity (cps)";
      xunits = " nm";
      yunits = " cps";
    }
    else if(exptype.equals("raman")) {
      cname_1 = "Raman Spectra";
      cname_2 = "Reference Spectra";
      xlabel = "Wavelength (cm-1)";
      ylabel = "Intensity (cps)";
      xunits = " cm-1";
      yunits = " cps";
    }
    else if(exptype.equals("streak")) {
      if(sda[0].getProfileDirection() == 0) {
        cname_1 = "Wavelength Profile";
        xlabel = "Wavelength (nm)";
        ylabel = "Intensity (cps)";
        xunits = " nm";
        yunits = " counts";
      }
      else {
        cname_1 = "Time Profile";
        xlabel = "Time (ps)";
        ylabel = "Intensity (counts)";
        xunits = " ps";
        yunits = " counts";
        
        // set the time scale
        String[] dmeta = sda[0].getMetaInfo();
        String[] header = sda[0].getHeader();
        
        String time_scale = header[header.length -1]; // this should only be in newer objects
        
        if(time_scale.equals("ps")) {
          xlabel = "Time (ps)";
          xunits = " ps";
        }
        else if(time_scale.equals("ns")) {
          xlabel = "Time (ns)";
          xunits = " ns";
        }
        else if(time_scale.equals("us")) {
          xlabel = "Time (us)";
          xunits = " us";
        }
        else if(dmeta[4].equals("Hamamatsu StreakScope")) {
          xlabel = "Time (ps)";
          xunits = " ps";
        }
        else if(dmeta[4].equals("F900")) {
          xlabel = "Time (ns)";
          xunits = " ns";
        }
        else { // default to picoseconds
          xlabel = "Time (ps)";
          xunits = " ps";
        }
      }
    }
    else if(exptype.equals("oodata")) {
      cname_1 = "OO Data Spectra";
      cname_2 = "OO Reference Data Spectra";
      xlabel = "Wavelength (nm)";
      ylabel = "Intensity";
      xunits = " nm";
      yunits = " a.u.";
    }
    else if(exptype.equals("pump-probe")) {
      if(sda[0].getProfileDirection() == 0) {
        cname_1 = "Pump-Probe Wavelength Profile";
        cname_2 = "Reference Spectrum";
        xlabel = "Wavelength (nm)";
        ylabel = "Abitrary Units";
        xunits = " nm";
        yunits = " a.u";
      }
      else {
        cname_1 = "Pump-Probe Time Profile";
        xlabel = "Time (fs)";
        ylabel = "Abitrary Units";
        xunits = " fs";
        yunits = " a.u";
      }
    }
    else if(exptype.equals("xydata")) {
      // ok we are going to assume that all the data is the same type as the first
      String[] header = sda[0].getHeader();
      
      cname_1 = header[7];
      xunits = " " + header[3];
      yunits = " "  + header[4];
      xlabel = header[5] + " (" + xunits + " )";
      ylabel = header[6] + " (" + yunits + " )";
    }
    else {
      cname_1 = "Absorption Spectra";
      cname_2 = "Reference Spectra";
      xlabel = "Wavelength (nm)";
      ylabel = "Absorption Units";
      xunits = " nm";
      yunits = " au";
    }
  }
  
  // method that actually create the gui components
  private void createGui() {
    // dispose of this wondow
		addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent we) {
				setVisible(false);
        dispose();
		  }
		});
    
    // set the layout to border layer
    getContentPane().setLayout(new BorderLayout());
    
    // some gui stuff
    JPanel panel;
    JLabel label;
    JButton button;
    JTabbedPane chartPane = null;
    
    hasReference();
    
    // see what plot to draw based on whether there is reference data
    if(hasref) {
      chartPane = new JTabbedPane();
      
      chartPane.addTab("Master", null, getSpectraDataChart(),
                                      "Master Data Plot"); //tooltip text
      
      chartPane.addTab("Reference", null, getReferenceSpectraDataChart(),
                                      "Reference Data Plot"); //tooltip text
      
      getContentPane().add(chartPane, "Center");
      
      currentChartPanel = mchartPanel;
    }
    else {
      if(sda[0].isProfile()) {
        getContentPane().add(getProfileDataChart(), "Center"); // noty used for now
      }
      else {
        getContentPane().add(getSpectraDataChart(), "Center");
      }
      currentChartPanel = mchartPanel;
    }
    
    // add the button panel now
    JPanel buttonPanel = new JPanel();
    buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
    
    Border border = BorderFactory.createRaisedBevelBorder();
    buttonPanel.setBorder(border);
    
    JPanel bp1 = new JPanel();
    bp1.setLayout(new GridLayout(2,2));
    
    // add the first row
    panel = new JPanel();
    panel.setLayout(new FlowLayout(FlowLayout.LEFT));
    
    label = new JLabel("X position : ");
    panel.add(label);
    
    xposLB = new JLabel("0000.0");
    xposLB.setForeground(Color.blue);
    panel.add(xposLB);
    
    label = new JLabel(xunits);
    panel.add(label);
    
    bp1.add(panel);
    
    panel = new JPanel();
    panel.setLayout(new FlowLayout(FlowLayout.LEFT));
    
    label = new JLabel("Delta X : ");
    panel.add(label);
    
    dxLB = new JLabel("0000.0");
    dxLB.setForeground(Color.red);
    panel.add(dxLB);
    
    label = new JLabel(xunits);
    panel.add(label);
    
    bp1.add(panel);
    
    // add second row
    panel = new JPanel();
    panel.setLayout(new FlowLayout(FlowLayout.LEFT));
    
    label = new JLabel("Y position : ");
    panel.add(label);
    
    yposLB = new JLabel("0000.0");
    yposLB.setForeground(Color.blue);
    panel.add(yposLB);
    
    label = new JLabel(yunits);
    panel.add(label);
    
    bp1.add(panel);
    
    panel = new JPanel();
    panel.setLayout(new FlowLayout(FlowLayout.LEFT));
    
    label = new JLabel("Delta Y : ");
    panel.add(label);
    
    dyLB = new JLabel("0000.0");
    dyLB.setForeground(Color.red);
    panel.add(dyLB);
    
    label = new JLabel(yunits);
    panel.add(label);
    
    bp1.add(panel);
    bp1.setAlignmentY(Component.BOTTOM_ALIGNMENT);
    
    buttonPanel.add(bp1);
    
    buttonPanel.add(Box.createHorizontalGlue());
    
    button = new JButton("Edit");
    button.setActionCommand("edit");
		button.addActionListener(this);
    button.setAlignmentY(Component.BOTTOM_ALIGNMENT);
    buttonPanel.add(button);
    
    button = new JButton("Close");
    button.setActionCommand("close");
		button.addActionListener(this);
    button.setAlignmentY(Component.BOTTOM_ALIGNMENT);
    buttonPanel.add(button);
    
    getContentPane().add(buttonPanel, "South");
    
    // add the tabpane change listener now
    if(chartPane != null) {
      chartPane.addChangeListener(this);
    }
  }
  
  // Method to handel action events
	public void actionPerformed(ActionEvent ae) {
		String cmd = ae.getActionCommand();
		
		if(cmd.equals("close")) {
			setVisible(false);
      dispose();
		}
    else if(cmd.equals("edit")) {
      showPlotEditorDialog();
    }
    else {
      System.err.println("Action Command not handeled in class PlotDialog ...");
      System.err.println(cmd);
    }
	}
  
  // Method to listen for changes in the tabb pane
  public void stateChanged(ChangeEvent ce) {
		JTabbedPane tabbedPane = (JTabbedPane)ce.getSource();
		int index = tabbedPane.getSelectedIndex();
    
    if(index == 0) {
      currentChartPanel = mchartPanel;
    }
    else if(index == 1) {
      currentChartPanel = rchartPanel;
    }
    else {
      currentChartPanel = null;
    }
  }
  
  // Method to listen to chart change events
  public void chartChanged(ChartChangeEvent event) {
    if(currentChartPanel != null) {
      JFreeChart chart = currentChartPanel.getChart();
      XYPlot plot = chart.getXYPlot();
      float xx = (float)plot.getDomainCrosshairValue();
      float yy = (float)plot.getRangeCrosshairValue();
      
      // set the labels now
      xposLB.setText(""+xx);
      yposLB.setText(""+yy);
      
      // calculate the delta x now
      deltaX[dindex%2] = xx;
      deltaY[dindex%2] = yy;
      dindex++;
      
      
      float dx = Math.abs(deltaX[0] - deltaX[1]);
      float dy = Math.abs(deltaY[0] - deltaY[1]);
      
      // set the labels now
      xposLB.setText(""+xx);
      yposLB.setText(""+yy);
      dxLB.setText(""+dx);
      dyLB.setText(""+dy);
      
      // set the currently selected wavelength or time for any listeners
      if(dsListener != null) {
        dsListener.dataPointSelected(""+ xx);
      }
    }
  }
  
  // Method to set the wavelength Selected Listener
  public void setDataPointSelectedListener(DataPointSelectedListener dsListener) {
    this.dsListener = dsListener;
  }
  
  // Method to check to if any of the data have a reference
  private void hasReference() {
    for(int i = 0; i < sda.length; i++) {
      if(sda[i].hasRef()) {
        hasref = true;
      }
    }
  }
  
  // Method to get the spectra data chart
  private ChartPanel getSpectraDataChart() {
    XYSeriesCollection plotdata = new XYSeriesCollection();
		XYSeries series = null;
		int series_count = 0;
    
		for(int i = 0; i < sda.length; i++) {
      float[][] xydata = null;
      
      if(sda[i].isProfile()) {
        continue;
      }
      else if(sda[i].is3D()) {
        xydata = SpectraDataUtil.integrateH(sda[i].getData());
      }
      else {
        xydata = sda[i].getData();
      }
			
      if(log) { // should the Y values be calculated as log?
        xydata = getLogData(xydata);
      }
      
      String name;
      if(sda[i].getType().equals("pump-probe") || sda[i].getType().equals("frgs")) {
        name = sda[i].getName() + "-" + sda[i].getDelay();
      }
      else {
        name = sda[i].getName();
      }
      series = new XYSeries(name);
			
      for(int j = 0; j < xydata.length; j++) {
				series.add(xydata[j][0], xydata[j][1]);
			}
			plotdata.addSeries(series);
      series_count++;
		}
		
		JFreeChart chart = ChartFactory.createXYLineChart(cname_1, xlabel, ylabel, 
                                                          plotdata,
                                                          PlotOrientation.VERTICAL,
                                                          true,
                                                          true,
                                                          false);
    
    chart.setBackgroundPaint(Color.white);
    chart.addChangeListener(this);
    chart.setAntiAlias(antialias);
    XYPlot plot = (XYPlot) chart.getPlot();
    plot.setOutlinePaint(Color.black);
    NumberAxis axis = (NumberAxis) plot.getRangeAxis();
    axis.setAutoRangeIncludesZero(false);
    axis.setAutoRangeMinimumSize(1.0);
    
    plot.setDomainCrosshairVisible(crosshair);
    plot.setRangeCrosshairVisible(crosshair);
    
    // set the line thickness
    XYLineAndShapeRenderer r1 = (XYLineAndShapeRenderer)plot.getRenderer();
    BasicStroke stroke = new BasicStroke(line_size);
    
    for(int i = 0; i < series_count; i++) {
      r1.setSeriesStroke(i, stroke);
    }
    
    mchartPanel = new ChartPanel(chart);
    mchartPanel.setDomainZoomable(true);
    mchartPanel.setRangeZoomable(true);
    
    return mchartPanel;
  }
  
  // Method to get the Reference spectra chart
  private ChartPanel getReferenceSpectraDataChart() {
    XYSeriesCollection plotdata = new XYSeriesCollection();
		XYSeries series = null;
		int series_count = 0; // the number of series added
    
		for(int i = 0; i < sda.length; i++) {
			float[][] xydata = sda[i].getReferenceData();
			if(xydata != null) {
        series = new XYSeries("ref-" + sda[i].getName());
        for(int j = 0; j < xydata.length; j++) {
          series.add(xydata[j][0], xydata[j][1]);
        }
        plotdata.addSeries(series);
        series_count++;
      }
		}
		
		JFreeChart chart = ChartFactory.createXYLineChart(cname_2, xlabel, ylabel, 
                                                          plotdata,
                                                          PlotOrientation.VERTICAL,
                                                          true,
                                                          true,
                                                          false);
    
    chart.setBackgroundPaint(Color.white);
    chart.addChangeListener(this);
    chart.setAntiAlias(antialias);
    XYPlot plot = (XYPlot) chart.getPlot();
    plot.setOutlinePaint(Color.black);
    NumberAxis axis = (NumberAxis) plot.getRangeAxis();
    axis.setAutoRangeIncludesZero(false);
    axis.setAutoRangeMinimumSize(1.0);
    
    plot.setDomainCrosshairVisible(crosshair);
    plot.setRangeCrosshairVisible(crosshair);
    
    // set the line thickness
    XYLineAndShapeRenderer r1 = (XYLineAndShapeRenderer)plot.getRenderer();
    BasicStroke stroke = new BasicStroke(line_size);
    
    for(int i = 0; i < series_count; i++) {
      r1.setSeriesStroke(i, stroke);
    }
    
    rchartPanel = new ChartPanel(chart);
    rchartPanel.setDomainZoomable(true);
    rchartPanel.setRangeZoomable(true);
    
    return rchartPanel;
  }
  
  // Method to return a spectra of slice data
  private ChartPanel getProfileDataChart() {
    //code
    return null;
  }
  
  // Method to show the editor dialog
  private void showPlotEditorDialog() {
    if(editorDialog == null) {
      editorDialog = new PlotEditorDialog(this, mchartPanel);
      editorDialog.pack();
    }
    
    editorDialog.setVisible(true);
  }
  
  // Method to return the log of xydata
  private float[][] getLogData(float[][] xydata) {
    float[][] nxydata = new float[xydata.length][2];
    
    for(int i = 0; i < xydata.length; i++) {
      nxydata[i][0] = xydata[i][0];
      
      if(xydata[i][1] > 1.0f) { // avoid negative numbers
        nxydata[i][1] = (float)Math.log(xydata[i][1]);
      }
      else {
         nxydata[i][1] = 0.0f;
      }
    }
    
    return nxydata;
  }
} //--end class PlotDialog
