
/**
 * EPRPlugin.java
 * A simple plugin for analysing epr data
 * Created: June 8, 2007
 *
 * @author Nathan Stevens
 * @version 0.1 11/12/2007
 */
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;
import java.io.*;
import java.text.*;
import java.util.*;

// for chatting and doing line fitting
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.DefaultXYItemRenderer;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.function.Function2D;
import org.jfree.data.function.LineFunction2D;
import org.jfree.data.general.DatasetUtilities;
import org.jfree.data.statistics.Regression;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class EPRPlugin extends JFrame implements InstruViewPlugin, ActionListener, 
ListSelectionListener {
  private static final String VERSION = "0.1 (11-12-07)"; // the plugin version
  private String name = "EPR Tc"; // the name of the dialog
  private InstruView instruView= null; // the main instruview object
  private JTextField h1TF = null; // center of H1
  private JTextField h1wTF = null; // width of H1
  private JTextField h2TF = null; // center of H2
  private JTextField h2wTF = null; // width of H2
  private JTextField h3TF = null; // center of H3
  private JTextField h3wTF = null; // width of H3
  private JList eprList = null;  // hold the emission data
  private JTextArea textArea = null; // where text data
  private TitledBorder eprBorder = null; // title border for emission data
  private Vector eprData = null;  // holds the emission
  private int[] sdEM = null;  // data selected in emList
  
  // array to hold the calculated results
  private double[][] results = null;
  
  // this constructor isn't really used
  public EPRPlugin() { }
  
  // the main constructor
  public EPRPlugin(InstruView instruView) {
    super("EPR Tc v- " + VERSION);
    
    this.instruView = instruView;
    
    // handles clossing of this plugin
		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;
    TitledBorder titleBorder;
    
    // add the input parameter panel
    JPanel mp1 = new JPanel();
    mp1.setLayout(new BoxLayout(mp1, BoxLayout.Y_AXIS));
    titleBorder = BorderFactory.createTitledBorder("Input Parameters/Results");
    mp1.setBorder(titleBorder);
    
    JPanel mp1A = new JPanel();
    mp1A.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 2));
    
    // h1 stuff
    label = new JLabel(" h1 ");
    mp1A.add(label);
    
    h1TF = new JTextField("3335.0", 5);
    mp1A.add(h1TF);
    
    label = new JLabel(" width ");
    mp1A.add(label);
    
    h1wTF = new JTextField("10.0", 3);
    mp1A.add(h1wTF);
    
    // h2 stuff
    label = new JLabel(" h2 ");
    mp1A.add(label);
    
    h2TF = new JTextField("3350.0", 5);
    mp1A.add(h2TF);
    
    label = new JLabel(" width ");
    mp1A.add(label);
    
    h2wTF = new JTextField("10.0", 3);
    mp1A.add(h2wTF);
    
    
    // h3 stuff
    label = new JLabel(" h2 ");
    mp1A.add(label);
    
    h3TF = new JTextField("3363.0", 5);
    mp1A.add(h3TF);
    
    label = new JLabel(" width ");
    mp1A.add(label);
    
    h3wTF = new JTextField("10.0", 3);
    mp1A.add(h3wTF);
    
    mp1.add(mp1A);
    
    /*
    JPanel mp1B = new JPanel();
    mp1B.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 2));
    
    label = new JLabel(" Lifetime (ns) ");
    mp1B.add(label);
    
    lifetimeTF = new JTextField("2.0", 4);
    mp1B.add(lifetimeTF);
    
    label = new JLabel(" || ");
    mp1B.add(label);
    
    label = new JLabel("<html>Slope (k<sub>q</sub>t<sub>0</sub>) =");
    mp1B.add(label);
    
    slopeLB = new JLabel(" 000.0");
    slopeLB.setForeground(Color.blue);
    mp1B.add(slopeLB);
    
    label = new JLabel(" || ");
    mp1B.add(label);
    
    label = new JLabel("<html>Rate (<i>k</i><sub>q</sub>) = ");
    mp1B.add(label);
    
    rateLB = new JLabel(" 0.0E10 ");
    rateLB.setForeground(Color.blue);
    mp1B.add(rateLB);
    
    label = new JLabel("<html>M<sup>-1</sup>s<sup>-1</sup>");
    mp1B.add(label);
    
    mp1.add(mp1B);*/
    
    getContentPane().add(mp1, "North");
    
    // add the panels that hold the data and results now
    JPanel mp2 = new JPanel();
    mp2.setLayout(new GridLayout(1, 2));
    
    // add the panel that allows inputing of emission data
    JPanel mp2A = new JPanel();
		mp2A.setLayout(new BoxLayout(mp2A, BoxLayout.Y_AXIS));
    eprBorder = BorderFactory.createTitledBorder("EPR Data (0)");
    mp2A.setBorder(eprBorder);
    
    eprList = new JList();
		eprList.addListSelectionListener(this);
		JScrollPane listScrollPane = new JScrollPane(eprList);
		listScrollPane.setAlignmentX(Component.LEFT_ALIGNMENT);
    mp2A.add(listScrollPane);
    
    mp2A.add(Box.createRigidArea(new Dimension(0, 2)));
    
    JPanel mp2A1 = new JPanel();
    mp2A1.setLayout(new GridLayout(1, 2));
    mp2A1.setAlignmentX(Component.LEFT_ALIGNMENT);
    
    button = new JButton("Remove");
		button.addActionListener(this);
		button.setActionCommand("remove");
		mp2A1.add(button);
    
    button = new JButton("Add");
		button.addActionListener(this);
		button.setActionCommand("add");
    mp2A1.add(button);
    
    mp2A.add(mp2A1);
    mp2.add(mp2A);
    
    // add the panel that hold the absorption data list
    JPanel mp2B = new JPanel();
		mp2B.setLayout(new BoxLayout(mp2B, BoxLayout.Y_AXIS));
    titleBorder = BorderFactory.createTitledBorder("Calculated Tc Data");
    mp2B.setBorder(titleBorder);
    
    textArea = new JTextArea(11, 0);
		textArea.setEditable(false);
    
    listScrollPane = new JScrollPane(textArea);
		listScrollPane.setAlignmentX(Component.LEFT_ALIGNMENT);
    mp2B.add(listScrollPane);
    
    mp2B.add(Box.createRigidArea(new Dimension(0, 2)));
    
    JPanel mp2B1 = new JPanel();
    mp2B1.setLayout(new GridLayout(1, 2));
    mp2B1.setAlignmentX(Component.LEFT_ALIGNMENT);
    
    mp2B.add(mp2B1);
    mp2.add(mp2B);
    
    getContentPane().add(mp2, "Center");
    
    // add the button to the south
    JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
    
    button = new JButton("Calculate");
    button.setMargin(new Insets(2, 5, 2, 5));
		button.setActionCommand("calculate");
		button.addActionListener(this);
    buttonPanel.add(button);
    
    /*button = new JButton("Plot");
    button.setMargin(new Insets(2, 5, 2, 5));
		button.setActionCommand("plot");
		button.addActionListener(this);
    buttonPanel.add(button);*/
    
    button = new JButton("Copy Data");
    button.setMargin(new Insets(2, 5, 2, 5));
		button.setActionCommand("copy");
		button.addActionListener(this);
    buttonPanel.add(button);
    
    button = new JButton("Clear");
    button.setMargin(new Insets(2, 5, 2, 5));
		button.setActionCommand("clear");
		button.addActionListener(this);
    buttonPanel.add(button);
    
    buttonPanel.add(Box.createHorizontalGlue());
    
    button = new JButton("Close");
    button.setMargin(new Insets(2, 5, 2, 5));
		button.setActionCommand("close");
		button.addActionListener(this);
    buttonPanel.add(button);
    
    getContentPane().add(buttonPanel, "South");
    
    // set this visible
    pack();
    setVisible(true);
    
    // now initialize the vector that holds the data
    eprData = new Vector();
  }
  
  // Method to handel action events
  public void actionPerformed(ActionEvent ae) {
    String cmd = ae.getActionCommand();
    
    if(cmd.equals("calculate")) {
			calculate();
		}
    else if(cmd.equals("plot")) {
      showPlotDialog();
    }
    else if(cmd.equals("add")) {
      addEPRData();
    }
    else if(cmd.equals("remove")) {
      removeEPRData();
    }
    else if(cmd.equals("copy")) {
      copyData();
    }
    else if(cmd.equals("clear")) {
      clearData();
    }
    else if(cmd.equals("close")) {
      setVisible(false);
      dispose();
    }
    else {
      System.err.println("Action Command not handeled in class EPRPlugin ...");
      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()) {
      sdEM = theList.getSelectedIndices();
    }
  }
  
  // Method to calculate the rate of quenching
  private void calculate() {
    int size = eprData.size();
    if(size >= 1) { // point less to do calculation on small amount of data
      results = new double[size][6];
      
      // now get the quencher concentration data from the name and check it
      SpectraData[] sda = SpectraDataUtil.getSpectraDataArray(eprData);
      if(getTemperature(sda)) {
        float h1 = Float.parseFloat(h1TF.getText());
        float h1w = Float.parseFloat(h1wTF.getText());
        float h1L = h1 - h1w/2.0f;
        float h1R = h1 + h1w/2.0f;
        float h2 = Float.parseFloat(h2TF.getText());
        float h2w = Float.parseFloat(h2wTF.getText());
        float h2L = h2 - h2w/2.0f;
        float h2R = h2 + h2w/2.0f;
        float h3 = Float.parseFloat(h3TF.getText());
        float h3w = Float.parseFloat(h3wTF.getText());
        float h3L = h3 - h3w/2.0f;
        float h3R = h3 + h3w/2.0f;
        
        for(int i = 0; i < sda.length; i++) {
          float[][] data = sda[i].getData();
          //results[i][1] = area;
          float h1min = 0.0f;
          float h1max = 0.0f;
          float h2min = 0.0f;
          float h2max = 0.0f;
          float h3min = 0.0f;
          float h3max = 0.0f;
          float dHmin = 0.0f;
          float dHmax = 0.0f;
          
          for(int k = 0; k < data.length; k++) {
            float x = data[k][0];
            float y = data[k][1];
            
            // get h1
            if(x >= h1L && x <= h1R) {
              if(y > h1max) {
                h1max = y;
              }
              
              if(y < h1min) {
                h1min = y;
              }
            }
            
            // get h2 and dh
            if(x >= h2L && x <= h2R) {
              if(y > h2max) {
                h2max = y;
                dHmin = x;
              }
              
              if(y < h2min) {
                h2min = y;
                dHmax = x;
              }
            }
            
            // get h3
            if(x >= h3L && x <= h3R) {
              if(y > h3max) {
                h3max = y;
              }
              
              if(y < h3min) {
                h3min = y;
              }
            }
          }
          
          // now calculate the the values
          float dH = dHmax - dHmin;
          float h1v = h1max- h1min;
          float h2v = h2max- h2min;
          float h3v = h3max- h3min;
          float sq1 = (float)Math.sqrt(h2v/h3v);
          float sq2 = (float)Math.sqrt(h2v/h1v);
          float tc = 6.5E-10f*dH*(sq1 + sq2 - 2.0f);
          results[i][1] = h1v;
          results[i][2] = h2v;
          results[i][3] = h3v;
          results[i][4] = dH;
          results[i][5] = tc;
        }
        
        // now printout the results
        NumberFormat nf = NumberFormat.getInstance();
        DecimalFormat df = (DecimalFormat) nf;
        df.setDecimalSeparatorAlwaysShown(true);
        
        textArea.append("Temp \t h1 \t h2 \t h3 \t dH \t tc \n");
        for(int i = 0; i < results.length; i++) {
          df.applyPattern("0.00E0");
          String tcs = df.format(results[i][5]);
          
          df.applyPattern("0.00");
          
          textArea.append(results[i][0] + "\t" + df.format(results[i][1]) + "\t" +
          df.format(results[i][2]) + "\t" + df.format(results[i][3]) + "\t" +
          df.format(results[i][4]) + "\t" + tcs + "\n");
        }
      }
      else { }
    }
    else {
      textArea.setText("Data Set To Small ...\n");
    }
  }
  
  // Method to get the concentration data
  private boolean getTemperature(SpectraData[] sda) {
    boolean good = true;
    
    for(int i = 0; i < sda.length; i++) {
      String dataName = sda[i].getName();
      int start_index = dataName.indexOf("[");
      int end_index = dataName.indexOf("]");
      
      if(start_index != -1 && end_index != -1) {
        String tempString = dataName.substring(start_index + 1, end_index);
        
        try {
          results[i][0] = Float.parseFloat(tempString);
        }
        catch(NumberFormatException nfe) {
          //textArea.append("(NFE)No Temperature Data " + dataName + " ...\n");
          //good = false;
          results[i][0] = 200.0 + i; // some random number
        }
      }
      else {
        //textArea.append("No Temperature Data For " + dataName + " ...\n");
        //good = false;
        results[i][0] = 200.0 + i; // some random number
      }
    }
    
    return good;
  }
  
  // Method to import data
  private void addEPRData() {
    SpectraData[] sda = instruView.xydataPanel.getSelectedSpectraData();
    if(sda != null) {
      for(int i = 0; i < sda.length; i++) {
        eprData.add(sda[i]);
      }
      
      String title = "EPR Data ( " + eprData.size() + " )";
      eprBorder.setTitle(title);
      
      eprList.setListData(eprData); // add this data to the list
    }
    
    repaint();
  }
  
  // Mehod to remove any data
  private void removeEPRData() {
    if(eprData.size() > 0 && sdEM != null) {
      
      for(int i = 0; i < sdEM.length; i++) {
        int index = sdEM[i] - i;
        eprData.remove(index);
      }
      
      // set the selection type to null
      sdEM = null;
      
      eprList.clearSelection();
      sdEM = null;
      eprList.setListData(eprData); // add this data to the list
      String title = "EPR Data ( " + eprData.size() + " )";
      eprBorder.setTitle(title);
    }
    repaint();
  }
  
  // Method to clear all data
  private void clearData() {
    eprList.clearSelection();
    eprData.clear();
    
    String title = new String("EPR Data ( " + eprData.size() + " )");
    eprBorder.setTitle(title);
    
    eprList.setListData(eprData);
    results = null;
    textArea.setText("");
  }
  
  // Method to copy the information to the clipboard
  private void copyData() {
    Clipboard cb =Toolkit.getDefaultToolkit().getSystemClipboard();
    StringSelection contents = new StringSelection(textArea.getText());
    cb.setContents(contents, null);
  }
  
  // Method to plot the data
  public void showPlotDialog() {
    if(results != null) {
      /*SternVolmerPlotDialog dialog = new SternVolmerPlotDialog(this, results);
      dialog.pack();
      dialog.setVisible(true);*/
    }
  }
  
  // Method to return information about this plugin
  public String[] getPluginInformation() {
    String[] information = {"Author : Nathan Stevens",
                                            "Version : " + VERSION,
                                            "",
                                            "A simple plugin for analyzing rotational correlation data"};
    
    return information;
  }
  
  // Method to set any spectra data
  public void setSpectraData(SpectraData[] sda) { }
  
  // Method to add a single spectra data. not used
  public void addSpectraData(SpectraData sd) {  }
  
  // Method to set any spectra data. not used
  public void addSpectraData(SpectraData[] sda) {  }
  
  // Method to load an sdl file. not used
  public void loadSDL(ExperimentData experimentData, File file) {  }
  
  // Method to load an sdl file. not used
  public void loadSDL(ExperimentData experimentData) {  }
  
  // Method to print any messages
  public void print(String message) {
    System.out.println(message);
  }
  
  // the static class for putting it self into the class
  static class EPRFactory extends PluginFactory {
    public InstruViewPlugin create(InstruView instruView) {
      return (new EPRPlugin(instruView));
    }
    
    // get the experiment type
    public String getExperimentType() {
      return "EPR Rotational Correlation Time";
    }
    
    // get information about this plugin
    public String[] getPluginInformation() {
      String[] information = {"Author : Nathan Stevens",
                                              "Version : " + VERSION,
                                              "",
                                              "A simple plugin for analyzing rotational correlation data"};
      
      return information;
    }
  }// -- end inner static class PlotterFactory
  
  // add this factory to the InstruviewPluginLoader factory hashtable
  static {
    InstruViewPluginLoader.pluginFactories.put("EPR", new EPRFactory());
  }
}//--end class EPRPlugin
