
/**
 * PumpProbeView.java
 * Used to process pump-probe data
 *
 * Created: Feb 29, 2004
 *
 * @author Nathan Stevens
 * @version 0.43 9/18/05
 */
 
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.util.*;
import java.io.File;

public class PumpProbeView extends JFrame implements ActionListener, 
TransformViewer, DataPointSelectedListener, ListSelectionListener {
  private static final float PRECISION = 1.0f;  // used for locating wavelength index
  private InstruView instruView = null; // The main instruview window
  public String filename = null;  // The file name of the spectra data object
  private PlotDialog plotDialog = null; // The plot dialog
  private PumpProbeWorksheet pumpProbeWorksheet = null; // display the worksheet
  private final JFileChooser fc = new JFileChooser(); // the file chooser
  private SpectraData[] refData = null;  // holds the reference data to be transformed
  private SpectraData[] rawData = null; // hold the raw data to be processed
  private SpectraData currentData = null; // the current spectra data
  private Vector sdVector = null; // holds the spectra data
  private int[] selectedData = null; // holds the selected data
  private JRadioButton ntf1RB = null; // use the default normalize transform
  private JRadioButton ntf2RB = null; // use the other normalize transform
  private JTextField minwlTF = null;  // the minimum wavelength
  private JTextField maxwlTF = null;  // the maximium wavelength
  private JTextField profileTF = null;  // the wavelength to get a time profile for
  private JList dataList = null; // the list that holds the transform data
  private JLabel reflabel = null; // shows if the reference data is set
  private JLabel datalabel = null; // shows if the raw data is set
  private JLabel runlabel = null; // is the tranform done runing
  
  // this stores wavelength profiles data
  private SpectraData[] WLPsda = null;
  private int WLPsda_count = 0;
  
   // this stores time profiles data
  private SpectraData[] TPsda = null;
  private int TPsda_count = 0;
  
  public PumpProbeView(InstruView instruView, String title, String filename) {
    super(title);
    this.instruView = instruView;
    this.filename= filename;
    
    // close this window
		addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent we) {
				setVisible(false);
        dispose();
		  }
		});
    
    // Set the layout to border layout
    getContentPane().setLayout(new BorderLayout());
    
    // Some gui components
		JButton button;
		JLabel label;
    TitledBorder title1, title2, title3;
    
    // Add the spectra data list
    JPanel dataListPanel, dataListPanel_A;
    
    dataListPanel = new JPanel();
		dataListPanel.setLayout(new BoxLayout(dataListPanel, BoxLayout.Y_AXIS));
    title1 = BorderFactory.createTitledBorder("Data ( " + filename + " )");
    dataListPanel.setBorder(title1);
    
    dataList = new JList();
		dataList.addListSelectionListener(this);
		JScrollPane listScrollPane = new JScrollPane(dataList);
		dataListPanel.add(listScrollPane);
    
    dataListPanel.add(Box.createRigidArea(new Dimension(0, 5)));
    
    dataListPanel_A = new JPanel();
    dataListPanel_A.setLayout(new GridLayout(2, 2));
    
    button = new JButton("Select All");
		button.addActionListener(this);
		button.setActionCommand("selectall");
		dataListPanel_A.add(button);
		
		button = new JButton("UnSelect");
		button.addActionListener(this);
		button.setActionCommand("unselect");
		dataListPanel_A.add(button);
    
		button = new JButton("Remove");
		button.addActionListener(this);
		button.setActionCommand("remove");
		dataListPanel_A.add(button);
    
		button = new JButton("Add");
		button.addActionListener(this);
		button.setActionCommand("add");
    dataListPanel_A.add(button);
    
    dataListPanel.add(dataListPanel_A);
    
    getContentPane().add(dataListPanel, "Center");
    
    // Add the information panel data list
    JPanel eastPanel, infoPanel, profilePanel, panel;
    
    eastPanel = new JPanel();
		eastPanel.setLayout(new BoxLayout(eastPanel, BoxLayout.Y_AXIS));
    
    
    infoPanel = new JPanel();
		infoPanel.setLayout(new GridLayout(5,2, 5, 0));
    title2 = BorderFactory.createTitledBorder("Normalize");
    infoPanel.setBorder(title2);
    
    ButtonGroup group = new ButtonGroup();
    
    ntf1RB = new JRadioButton("NTF 1");
    ntf1RB.setSelected(true);
    group.add(ntf1RB);
    infoPanel.add(ntf1RB);
    
    ntf2RB = new JRadioButton("NTF 2");
    group.add(ntf2RB);
    infoPanel.add(ntf2RB);
    
    minwlTF = new JTextField("400.0");
    infoPanel.add(minwlTF);
    
    maxwlTF = new JTextField("750.0");
    infoPanel.add(maxwlTF);
    
    button = new JButton("Set Ref. ");
		button.addActionListener(this);
		button.setActionCommand("setref");
		infoPanel.add(button);
    
    reflabel = new JLabel("None");
    reflabel.setForeground(Color.blue);
    infoPanel.add(reflabel);
    
    button = new JButton("Set Data");
		button.addActionListener(this);
		button.setActionCommand("setdata");
		infoPanel.add(button);
    
    datalabel = new JLabel("None");
    datalabel.setForeground(Color.blue);
    infoPanel.add(datalabel);
    
    button = new JButton("Run");
		button.addActionListener(this);
		button.setActionCommand("run");
		infoPanel.add(button);
    
    runlabel = new JLabel("Not Ready");
    runlabel.setForeground(Color.blue);
    infoPanel.add(runlabel);
    
    eastPanel.add(infoPanel);
    
    // add some spacing now
    //eastPanel.add(Box.createRigidArea(new Dimension(0, 10)));
    
    profilePanel = new JPanel();
		profilePanel.setLayout(new GridLayout(2, 2));
    title3 = BorderFactory.createTitledBorder("Time Profile");
    profilePanel.setBorder(title3);
    
    label = new JLabel("Wavelength");
    profilePanel.add(label);
    
    profileTF = new JTextField("000.00");
    profileTF.setForeground(Color.blue);
    profileTF.setActionCommand("profile");
    profileTF.addActionListener(this);
    profilePanel.add(profileTF);
    
    button = new JButton("Fit");
		button.addActionListener(this);
		button.setActionCommand("fitprofile");
		profilePanel.add(button);
    
    button = new JButton("Profile");
		button.addActionListener(this);
		button.setActionCommand("profile");
		profilePanel.add(button);
    
    eastPanel.add(profilePanel);
    
    getContentPane().add(eastPanel, "East");
    
    // The close buttons
		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
    
    button = new JButton("View");
		button.setActionCommand("view");
		button.addActionListener(this);
    buttonPanel.add(button);
    
    button = new JButton("Plot");
		button.setActionCommand("plot");
		button.addActionListener(this);
    buttonPanel.add(button);
    
    button = new JButton("Image");
		button.setActionCommand("image");
		button.addActionListener(this);
    buttonPanel.add(button);
    
    button = new JButton("Put");
		button.setActionCommand("put");
		button.addActionListener(this);
    buttonPanel.add(button);
    
    button = new JButton("Export");
		button.setActionCommand("export");
		button.addActionListener(this);
    buttonPanel.add(button);
    
    buttonPanel.add(Box.createHorizontalGlue());
    
    button = new JButton("Close");
		button.setActionCommand("close");
		button.addActionListener(this);
    buttonPanel.add(button);
    
    getContentPane().add(buttonPanel, "South");
    
    // initialize some stuff
    sdVector = new Vector();
    fc.addChoosableFileFilter(new ExperimentDataFileFilter("text"));
  }
  
  // Method to handel action events
  public void actionPerformed(ActionEvent ae) {
    String cmd = ae.getActionCommand();
    
    if(cmd.equals("selectall")) {
			selectAllData();
		}
		else if(cmd.equals("unselect")) {
			dataList.clearSelection();
			selectedData = null;
		}
    else if(cmd.equals("remove")) {
      removeData();
    }
    else if(cmd.equals("add")) {
      addData();
    }
    else if(cmd.equals("setref")) {
      setReferenceData();
    }
    else if(cmd.equals("setdata")) {
      setRawData();
    }
    else if(cmd.equals("run")) {
      normalizeData();
    }
    else if(cmd.equals("profile")) {
      getTimeProfile();
    }
    else if(cmd.equals("view")) {
      viewPumpProbeWorksheet();
    }
    else if(cmd.equals("plot")) {
      viewPumpProbePlot();
    }
    else if(cmd.equals("image")) {
      viewPumpProbeImage();
    }
    else if(cmd.equals("put")) {
      addDataToPumpProbePanel();
    }
    else if(cmd.equals("export")) {
      exportDataArray();
    }
    else if(cmd.equals("close")) {
      setVisible(false);
      dispose();
    }
    else {
      System.err.println("Action Command not handeled in class PumpProbeView");
      System.err.println(cmd);
    }
  }
  
  // Method to handel list selection events
	public void valueChanged(ListSelectionEvent e) {
    if (e.getValueIsAdjusting()) {
      return;
		}
		
    JList theList = (JList)e.getSource();
    if (!theList.isSelectionEmpty()) {
			selectedData = theList.getSelectedIndices();
		}
    
    if(sdVector.size() > 0 && selectedData != null) {
      currentData = (SpectraData)sdVector.elementAt(selectedData[0]);
    }
	}
  
  // set the status of the transformation that was done
  public void setStatus(int code, String status) {
    if(code != -1) {
      runlabel.setText("Done");
    }
    else {
      runlabel.setText("Error");
    }
  }
  
  // return the transformed data
  public void setResults(SpectraData[] sda) {
    addSpectraData(sda);
  }
  
  // Method to set the currently selected wavelength
  public void dataPointSelected(String wavelength) {
    try {
      float wl = Float.parseFloat(wavelength);
      
      if(wl >= 200 && wl <= 900) {
        profileTF.setText(wavelength);
      }
    }
    catch(NumberFormatException nfe) { }
  }
  
  // Method to select all the data in the list
	private void selectAllData() {
		if(sdVector.size() > 0) {
			int[] indices = new int[sdVector.size()];
			for(int i = 0; i < indices.length; i++) {
				indices[i] = i;
			}
			dataList.setSelectedIndices(indices);
		}
	}
  
  // Method to remove data
  public void removeData() {
    if(selectedData != null && sdVector.size() != 0) {
      
      for(int i= 0; i < selectedData.length; i++) {
        int index = selectedData[i] - i;
        sdVector.removeElementAt(index);
      }
      selectedData = null;
      dataList.setListData(sdVector);
    }
  }
  
  // method to clear the data
	private void RemoveAllData() {
    sdVector = new Vector();
    selectedData = null;
		dataList.setListData(sdVector);
	}
  
  // Method to add a data file
  private void addData() {
    SpectraData[] sda = instruView.pumpProbePanel.getSelectedSpectraData();
    if(sda != null) {
      addSpectraData(sda);
    }
  }
  
  // Method to add a single Spectra Data
  public void addSpectraData(SpectraData sd) {
    sdVector.addElement(sd);
    dataList.setListData(sdVector);
  }
  
  // Method to add data to this work sheet
  public void addSpectraData(SpectraData[] sda) {
    for(int i = 0; i < sda.length; i++) {
      sdVector.addElement(sda[i]);
    }
    dataList.setListData(sdVector);
  }
  
  // Method to get the selected Index
  private SpectraData[] getSelectedSpectraData() {
    SpectraData[] sda = null;
    
    if(selectedData != null && sdVector.size() !=0) {
      int dcount = selectedData.length;
      sda = new SpectraData[dcount];
      
      for(int i= 0; i < dcount; i++) {
        sda[i] = (SpectraData)sdVector.elementAt(selectedData[i]);
      }
    }
    return sda;
  }
  
  // Method to set the raw, refeference, and any normalized data
  public void setSpectraData(SpectraData[] sda) {
    SpectraData[] refdata = null;
    SpectraData[] rawdata = null;
    SpectraData sd = null;
    
    // set the size of the two arrays
    int ref_size = 0;  // the reference data size
    int raw_size = 0; // the raw data size
    for(int i = 0; i < sda.length; i++) {
      sd = sda[i];
      String name = sd.toString().toLowerCase();
      
      // do the counting
      if(name.indexOf("ref") != -1) {
        ref_size++;
      }
      else if(name.indexOf("ntf") != -1 || name.indexOf("tp") != -1) {
        // do nothing
      }
      else if(name.indexOf("pp") != -1) {
        raw_size++;
      }
    }
    
    refData = new SpectraData[ref_size];
    rawData = new SpectraData[raw_size];
    
    // now fill the raw and reference data array
    int ref_index = 0;
    int raw_index = 0;
    for(int i = 0; i < sda.length; i++) {
      sd = sda[i];
      String name = sd.toString().toLowerCase();
      
      // do the counting
      if(name.indexOf("ref") != -1) {
        refData[ref_index] = sda[i];
        ref_index = 0;
      }
      else if(name.indexOf("ntf") != -1 || name.indexOf("tp") != -1) {
        sdVector.addElement(sd);
      }
      else if(name.indexOf("pp") != -1) {
        rawData[raw_index] = sda[i];
        raw_index++;
      }
    }
    
    // now display the results
    if(refData.length != 0) {
      reflabel.setText("Set ( " + refData.length + " )");
    }
    else {
       reflabel.setText("None");
    }
    
    // now display the results
    if(rawData.length != 0) {
      datalabel.setText("Set ( " + rawData.length + " )");
    }
    else {
      datalabel.setText("None");
    }
    
    // update some gui components
    if(refData.length == 1) {
      ntf1RB.setSelected(true);
    }
    else if(refData.length == 2) {
      ntf2RB.setSelected(true);
    }
    
    if(refData.length > 0 && rawData.length > 0) {
      runlabel.setText("Ready");
    }
    else {
      runlabel.setText("Not Ready");
    }
    
    // update the JList if needed
    if(sdVector.size() != 0) {
      dataList.setListData(sdVector);
    }
  }
  
  // Method to set the reference data
  private void setReferenceData() {
    SpectraData[] sdata = instruView.pumpProbePanel.getSelectedSpectraData();
    if(sdata != null) {
      refData = sdata;
      reflabel.setText("Set ( " + sdata.length + " )");
    }
    else {
       reflabel.setText("None");
    }
  }
  
  // Method to set the raw data
  private void setRawData() {
    SpectraData[] sdata = instruView.pumpProbePanel.getSelectedSpectraData();
    if(sdata != null) {
      rawData = sdata;
      datalabel.setText("Set ( " + sdata.length + " )");
    }
    else {
      datalabel.setText("None");
    }
  }
  
  // Method to run the transform thread
  private void normalizeData() {
    if(refData != null && rawData != null) {
      // clear any data fist
      RemoveAllData();
      
      SpectraData[] refdata = null;
      
      if(ntf1RB.isSelected()) {
        refdata = new SpectraData[1];
        refdata[0] = refData[0];
      }
      else {
        refdata = new SpectraData[2];
        if(refData.length == 2) {
          refdata = refData;
        }
        else {  // use the same reference data for both references
          refdata[0] = refData[0];
          refdata[1] = refData[0];
        }
      }
      
      // get min and max wavelengths
      float minWL = 0.0f;
      float maxWL = 0.0f;
      
      try {
        minWL = Float.parseFloat(minwlTF.getText());
        maxWL = Float.parseFloat(maxwlTF.getText());
      }
      catch(NumberFormatException nfe) { }
      
      new PumpProbeTransformThread(this, rawData, refdata, minWL, maxWL).start();
    }
  }
  
  // Method to get get the selected time profile data
  private void getTimeProfile() {
    getWLPData();
    
    if(WLPsda != null && WLPsda.length > 4) {
      try {
        float[][] data = new float[WLPsda.length][2];
        float wl = Float.parseFloat(profileTF.getText());
        
        //now get the index of the wavelength that closest
        int index = getWavelengthLocation(WLPsda[0], wl);
        
        // ok create the data array
        float[][] tmpdata;
        for(int i = 0; i < data.length; i++) {
          tmpdata = WLPsda[i].getData();
          data[i][0] = Float.parseFloat(WLPsda[i].getDelay());
          data[i][1] = tmpdata[index][1];
        }
        
        // create new spectra data array
        String wavelength = Math.round(WLPsda[0].getData()[index][0]) + "";
        String[] hd = WLPsda[0].getHeader();
        String[] header = new String[hd.length];
        header[0] = new String(WLPsda[0].getDelay());
        header[1] = new String(WLPsda[WLPsda.length-1].getDelay());
        header[2] = new String(WLPsda.length+"");
        header[3] = new String(wavelength);
        header[4] = new String(hd[4]);
        header[5] = new String(hd[5]);
        header[6] = new String(hd[6]);
        header[7] = new String(hd[7]);
        header[8] = new String(hd[8]);
        
        String[] dmeta = (String[])WLPsda[0].getMetaInfo().clone();
        
        String name = "TP";
        SpectraData sd = new SpectraData(name, "pump-probeTP", header, dmeta, 
                                                                    data, null);
        sd.setDelay(wavelength); // just used to set the selected wavelength
        sd.setProfileDirection(1);
        String xlabel = name + "-" + wavelength + "-tm";
        String ylabel = name + "-" + wavelength + "-int";
        sd.setColumnLabels(xlabel, ylabel);
        addSpectraData(sd);
      }
      catch(NumberFormatException nfe) { }
    }
    else {
      // Alert user the required conditions was not meet
      String message = "More than 4 data objects must be selected in order\n" + 
                                     "To generate a time profile...";
      JOptionPane.showMessageDialog(this, message, "Time Profile Error",
		                                                 JOptionPane.ERROR_MESSAGE);
    }
  }
  
  // Method to get the selected spectra data
  private int getWavelengthLocation(SpectraData sd, float wl) {
    int index = -1;
    float data[][] = sd.getData();
    
    float current = 100.0f;
    float diff = 0;
    for(int i = 0; i < data.length; i++) {
      current = data[i][0];
      diff = Math.abs(current - wl);
      if(diff <= PRECISION) {
        if(i + 1 != data.length) {
          index = i + 1;
        }
        else {
          index = i;
        }
        break;
      }
    }
    
    return index;
  }
  
  // Method to view the pumpProbeWorksheet
  private void viewPumpProbeWorksheet() {
    pumpProbeWorksheet = new PumpProbeWorksheet(this);
    pumpProbeWorksheet.pack();
    pumpProbeWorksheet.setVisible(true);
  }
  
  // Method to View Data plots
  private void viewPumpProbePlot() {
    SpectraData[] sda = getSelectedSpectraData();
    if(sda != null) {
      plotDialog = new PlotDialog("Pump-Probe Plot", "pump-probe", sda);
      plotDialog.setDataPointSelectedListener(this);
      plotDialog.setSize(550, 450);
      plotDialog.setVisible(true);
    }
  }
  
  // Method to view a pump probe image
  private void viewPumpProbeImage() {
    if(sdVector.size() >= 50) {// only create image for fifty or more time slices
      PumpProbeImageDialog  imageDialog = new PumpProbeImageDialog(this);
      imageDialog.pack();
      imageDialog.setVisible(true);
    }
    else {
      // Alert user the required conditions was not meet
      String message = "50 or more data objects must be available\n" + 
                                     "to generate image...";
      JOptionPane.showMessageDialog(this, message, "Pump Probe Image Error",
		                                                 JOptionPane.ERROR_MESSAGE);
    }
  }
  
  // Method to add the selected data to the pumpProbePanel
  private void addDataToPumpProbePanel() {
    SpectraData[] sda = getSelectedSpectraData();
    
    if(sda != null) {
      instruView.pumpProbePanel.addSpectraData(sda);
    }
  }
  
  // Method to export the XY data array
  private void exportDataArray() {
    int returnVal = fc.showSaveDialog(this);
    
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			File file = fc.getSelectedFile();
      
      // add the xydata array to the string buffer
      StringBuffer buffer = new StringBuffer();
      float[][] xydata = getDataArray();
      
      for(int i = 0; i < xydata.length; i++) {
        for(int j = 0; j < xydata[0].length; j++) {
          if((j + 1) != xydata[0].length) {
            buffer.append(xydata[i][j]).append('\t');
          }
          else {
            buffer.append(xydata[i][j]).append('\n');
          }
        }
      }
      
      // now wrtie out the buffer to a file
      TextOutputFile out = new TextOutputFile(file);
      out.print(buffer.toString());
      out.close();
    }
  }
  
  // Method to get the time profile data
  public SpectraData[] getWLPData() {
    SpectraData[] sda = getSelectedSpectraData();
    return getWLPData(sda);
  }
  
  // Method to set the wlsda
  public SpectraData[] getWLPData(SpectraData[] sda) {
    Vector sdv = new Vector();
    
    if(sda == null && sdVector.size() != 0) {
      sda = SpectraDataUtil.getSpectraDataArray(sdVector);
    }
    
    for(int i = 0; i < sda.length; i++) {
      if(sda[i].getProfileDirection() == 0) {
        sdv.addElement(sda[i]);
      }
    }
    
    // check to se if it's empty
    if(sdv.size() != 0) {
      WLPsda = new SpectraData[sdv.size()];
      
      for(int i  = 0; i < sdv.size(); i++) {
        WLPsda[i] = (SpectraData)sdv.elementAt(i);
        WLPsda[i].setExportX(false);
      }
    }
    else {
      WLPsda = null;
    }
    
    return WLPsda;
  }
  
  // Method to get the time profile data
  public SpectraData[] getTPData() {
    SpectraData[] sda = getSelectedSpectraData();
    return getTPData(sda);
  }
  
  // Method to set the time profile data
  public SpectraData[] getTPData(SpectraData[] sda) {
    Vector sdv = new Vector();
    
    if(sda == null && sdVector.size() != 0) {
      sda = SpectraDataUtil.getSpectraDataArray(sdVector);
    }
    
    for(int i = 0; i < sda.length; i++) {
      if(sda[i].getProfileDirection() == 1) {
        sdv.addElement(sda[i]);
      }
    }
    
    // check to se if it's empty
    if(sdv.size() != 0) {
      TPsda = new SpectraData[sdv.size()];
      
      for(int i  = 0; i < sdv.size(); i++) {
        TPsda[i] = (SpectraData)sdv.elementAt(i);
        TPsda[i].setExportX(false);
      }
    }
    else {
      TPsda = null;
    }
    
    return TPsda;
  }
  
  // Method to return a 3D array of the collect data
  public float[][] getDataArray() {
    SpectraData[] wl_sda = getWLPData(); 
    
    // set the size of the xydata array and image now
    float[][] data = wl_sda[0].getData();
    int xsize = data.length + 1; // must add one for axis
    int ysize = wl_sda.length + 1; // must add one for axis;
    
    float[][] xydata = new float[ysize][xsize];
    
    // add the wavelenght axis
    for(int i = 0; i < data.length; i++) {
      xydata[0][i+1] = data[i][0];
    }
    
    for(int i = 1; i < xydata.length; i++) {
      SpectraData sd = wl_sda[i-1];
      data = sd.getData();
      float delay = getTimeDelay(sd);
      xydata[i][0] = delay;
      
      for(int j = 0; j < data.length; j++) {
        float intensity = data[j][1];
        xydata[i][j+1] = intensity;
      }
    }
    
    return xydata;
  }
  
  private float getTimeDelay(SpectraData sd) {
    float delay = 0.0f;
    try {
      delay = Float.parseFloat(sd.getDelay());
    }
    catch(NumberFormatException nfe){}
    
    return delay;
  }
} //--end class PumpProbeView
