/**
 * LifetimeSimDialog.java
 * Contains the graph that plots 
 * Created: August 5, 2006
 *
 * @author Nathan Stevens
 * @version 0.2 2/18/2007 
 */
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import java.text.*;
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 LifetimeSimDialog extends JFrame implements ActionListener, 
ChangeListener, ChartChangeListener {
  private JLabel distanceLB = null;
  private JLabel efficiencyLB = null;
  private JLabel rateLB = null;
  private JLabel tauLB = null;
  //private JLabel xposLB = null;
  //private JLabel yposLB = null;
  private ChartPanel chartPanel = null;
  private String xunits = null;
  private String yunits = null;
  private String xlabel = null;
  private String ylabel = null;
  private JSlider xJS = null;
  
  // gui components to set the fit range
  private JTextField maxT1TF = null; // set the maximum time
   private JTextField pexATF = null; // set the percent of excited exceptors
  private JTextField minT2TF = null; // set the minimum time to get % photons
  private JTextField maxT2TF = null; // set the maximum time to get % photons
  private JLabel dintLB = null; // what's the percent of the donor intensity between t1 and t2
  private JLabel aintLB = null; // what's the percent of the donor intensity between t1 and t2
  
  // variables below used for plotting simulated spectra
  private float[][] efficiency = null;
  private float[][] transfer_rate = null;
  private float[][] lifetime = null;
  private float minX = 0.0f;
  private float maxX = 0.0f;
  private int index = 0;
  private float dx = 0.0f;
  
  // variable that hold the donor and acceptor lifetimes
  private float current_rate = 0.0f;
  private float donor_lifetime = 0.0f;
  private float acceptor_lifetime = 0.0f;
  private float pexA = 0.05f; // default 5%
  private int min_time = 0; // this will remain fix at zero
  private int max_time = 0; // this can be changed
  
  private float[][] Id0 = null; // holds the time dependent intensity of the donor with no acceptor
  private float[][] Id1 = null; // holds the time dependent intensity of the donor with acceptor
  private float[][] Ia1 = null; // holds the time dependent intensity of the acceptor with acceptor
  
  // hold data to be plotted
  private XYSeriesCollection sim_data = null; // stores the simulated data
  private JFreeChart sim_chart = null; // plots the simulated data
  
  // constructor implememented but not used
  public LifetimeSimDialog() { }
  
  // The main constructor
  public LifetimeSimDialog(DyeSpectra donorDye, DyeSpectra acceptorDye,
  float[][] efficiency, float[][] transfer_rate, float[][] lifetime) {
    super("Lifetime Simulation");
    
    this.efficiency = efficiency;
    this.transfer_rate = transfer_rate;
    this.lifetime = lifetime;
    
    // set some variables needed to do dynamic plotting
    minX = efficiency[0][0];
    maxX = efficiency[efficiency.length - 1][0];
    dx = efficiency[1][0] - efficiency[0][0];
    
    // get the lifetimes in nanoseconds
    donor_lifetime = 1.0e9f * FRETViewUtil.getLifetime(donorDye);
    acceptor_lifetime = 1.0e9f * FRETViewUtil.getLifetime(acceptorDye);
    
    max_time = Math.round(donor_lifetime) * 2;
    String maxTime = "" + max_time;
    
    // now going to calculate the intensity of donor and acceptor
    current_rate = transfer_rate[0][1]/1.0e9f; // change back to [0][1]
    calculateDecays();
    
    // handles clossing of this plugin
		addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent we) {
        setVisible(false);
        dispose();
      }
		});
    
    // some gui stuff
    JPanel panel;
    JLabel label;
    JButton button;
    TitledBorder titleBorder;
    
    // add the chart panel displaying the decays
    createChartPanel();
    getContentPane().add(chartPanel, "Center");
    
    // add the panel holdlig the slider and buttons
    panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    
    // construct the slider
    int min = (int)minX;
    int max = (int)maxX;
    int spacing = (max - min)/10;
    
    xJS = new JSlider(JSlider.HORIZONTAL, min, max, min);
    xJS.addChangeListener(this);
    xJS.setMajorTickSpacing(spacing);
    //xJS.setMinorTickSpacing(1);
    xJS.setPaintTicks(true);
    xJS.setPaintLabels(true);
    xJS.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
    
    panel.add(xJS);
    
    // add the panel that hold the rate and efficiency labels
    JPanel infoPanel = new JPanel();
    infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.X_AXIS));
    
    label = new JLabel("D-A Distance : ");
    infoPanel.add(label);
    
    distanceLB = new JLabel("" + minX);
    distanceLB.setForeground(Color.blue);
    infoPanel.add(distanceLB);
    
    label = new JLabel(" Efficiency : ");
    infoPanel.add(label);
    
    efficiencyLB = new JLabel("" + efficiency[0][1]);
    efficiencyLB.setForeground(Color.blue);
    infoPanel.add(efficiencyLB);
    
    label = new JLabel(" Transfer Rate (s^-1) : ");
    infoPanel.add(label);
    
    rateLB = new JLabel("" + transfer_rate[0][1]);
    rateLB.setForeground(Color.blue);
    infoPanel.add(rateLB);
    
    //infoPanel.add(Box.createHorizontalGlue());
    
    /*label = new JLabel("Selected Time (ns) : ");
    infoPanel.add(label);
    
    xposLB = new JLabel("0");
    xposLB.setForeground(Color.blue);
    infoPanel.add(xposLB);*/
    
    label = new JLabel(" Tau Acceptor (ns) : ");
    infoPanel.add(label);
    
    tauLB = new JLabel("0");
    tauLB.setForeground(Color.blue);
    infoPanel.add(tauLB);
    
    infoPanel.add(Box.createRigidArea(new Dimension(10,0)));
    
    panel.add(infoPanel);
    
    // add the button panel now
    JPanel buttonPanel = new JPanel();
    buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.Y_AXIS));
    Border border = BorderFactory.createRaisedBevelBorder();
    buttonPanel.setBorder(border);
    
    JPanel bp1 = new JPanel();
    bp1.setLayout(new BoxLayout(bp1, BoxLayout.X_AXIS));
    
    JPanel bp1a = new JPanel();
    bp1a.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
    
    label = new JLabel("Max Time (ns) : ");
    bp1a.add(label);
    
    maxT1TF = new JTextField(maxTime, 5);
    bp1a.add(maxT1TF);
    
    label = new JLabel(" ");
    bp1a.add(label);
    
    button = new JButton("Set");
    button.setActionCommand("setmaxtime");
		button.addActionListener(this);
    bp1a.add(button);
    
    bp1a.add(Box.createRigidArea(new Dimension(20,0)));
    
    label = new JLabel(" % Excited Acceptors : ");
    bp1a.add(label);
    
    pexATF = new JTextField("5", 5);
    bp1a.add(pexATF);
    
    label = new JLabel(" ");
    bp1a.add(label);
    
    button = new JButton("Set");
    button.setActionCommand("setpexa");
		button.addActionListener(this);
    bp1a.add(button);
    
    bp1.add(bp1a);
    
    bp1.add(Box.createHorizontalGlue());
    
    buttonPanel.add(bp1);
    
    JPanel bp2 = new JPanel();
    bp2.setLayout(new BoxLayout(bp2, BoxLayout.X_AXIS));
    
    /*JPanel bp2a = new JPanel();
    bp2a.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
    
    label = new JLabel("% Intensity From : ");
    bp2a.add(label);
    
    button = new JButton("Set");
    button.setActionCommand("setminT2");
		button.addActionListener(this);
    bp2a.add(button);
    
    minT2TF = new JTextField("0", 5);
    bp2a.add(minT2TF);
    
    label = new JLabel(" to ");
    bp2a.add(label);
    
    button = new JButton("Set");
    button.setActionCommand("setmaxT2");
		button.addActionListener(this);
    bp2a.add(button);
    
    maxT2TF = new JTextField(maxTime, 5);
    bp2a.add(maxT2TF);
    
    label = new JLabel(" ns Donor = ");
    bp2a.add(label);
    
    dintLB = new JLabel("000");
    dintLB.setForeground(Color.red);
    bp2a.add(dintLB);
    
    label = new JLabel(" Acceptor = ");
    bp2a.add(label);
    
    aintLB = new JLabel("000");
    aintLB.setForeground(Color.red);
    bp2a.add(aintLB);
    
    bp2.add(bp2a);*/
    
    bp2.add(Box.createHorizontalGlue());
    
    button = new JButton("Copy Data");
    button.setActionCommand("copy");
		button.addActionListener(this);
    bp2.add(button);
    
    button = new JButton("Close");
    button.setActionCommand("close");
		button.addActionListener(this);
    bp2.add(button);
    
    buttonPanel.add(bp2);
    
    panel.add(buttonPanel);
    
    getContentPane().add(panel, "South");
  }
  
  // Method to create the chart Panel
  private void createChartPanel() {
    String title = "Simulated Decay Profiles";
    ylabel = "Intensity (a.u)";
    xlabel = "Time (ns)";
    
    sim_data = new XYSeriesCollection();
    
    XYSeries Id0_series = getSeries("Donor");
    XYSeries Id1_series = getSeries("(Donor + Acceptor)");
    XYSeries Ia1_series = getSeries("(Acceptor + Donor)");
    
    sim_data.addSeries(Id0_series);
    sim_data.addSeries(Id1_series);
    sim_data.addSeries(Ia1_series);
    
    sim_chart = ChartFactory.createXYLineChart(title, xlabel, ylabel, 
                                                          sim_data,
                                                          PlotOrientation.VERTICAL,
                                                          true,
                                                          true,
                                                          false);
    // construct the chart now
    sim_chart.setBackgroundPaint(Color.white);
    sim_chart.addChangeListener(this);
    sim_chart.setAntiAlias(false); // will allow to be modifies
    
    XYPlot plot = (XYPlot)sim_chart.getPlot();
    plot.setOutlinePaint(Color.black);
    plot.setDomainCrosshairVisible(true);
    
    NumberAxis xaxis = (NumberAxis)plot.getDomainAxis();
    xaxis.setRange(-5.0, max_time);
    
    NumberAxis yaxis = (NumberAxis)plot.getRangeAxis();
    yaxis.setRange(0.0, 1.01);
    
    XYLineAndShapeRenderer r1 = (XYLineAndShapeRenderer)plot.getRenderer();
    BasicStroke stroke = new BasicStroke(2.0f);
    
    r1.setSeriesPaint(0, Color.green);
    r1.setSeriesStroke(0, stroke);
    
    r1.setSeriesPaint(1, Color.blue);
    r1.setSeriesStroke(1, stroke);
    
    r1.setSeriesPaint(2, Color.red);
    r1.setSeriesStroke(2, stroke);
    
    chartPanel = new ChartPanel(sim_chart);
    chartPanel.setDomainZoomable(true);
    chartPanel.setRangeZoomable(true);
  }
  
  // Method to return an XYseries
  private XYSeries getSeries(String name) {
    XYSeries series = new XYSeries(name);
    
    if(name.equals("Donor")) {
      for(int i = 0; i < Id0.length; i++) {
        series.add(Id0[i][0], Id0[i][1]);
      }
    }
    else if(name.equals("(Donor + Acceptor)")) {
      for(int i = 0; i < Id1.length; i++) {
        series.add(Id1[i][0], Id1[i][1]);
      }
    }
    else { // assum acceptor with donor
      for(int i = 0; i < Ia1.length; i++) {
        series.add(Ia1[i][0], Ia1[i][1]);
      }
    }
    
    return series;
  }
  
  // Method to handel action events
	public void actionPerformed(ActionEvent ae) {
		String cmd = ae.getActionCommand();
		
		if(cmd.equals("setmaxtime")) {
			setMaximumTime();
		}
    else if(cmd.equals("setpexa")) {
      setPercentExcitedAcceptors();
    }
    else if(cmd.equals("copy")) {
      copyData();
    }
    else if(cmd.equals("close")) {
      setVisible(false);
      dispose();
    }
    else {
      System.err.println("Action Command not handeled in class LifetimeSimDialog ...");
      System.err.println(cmd);
    }
	}
  
  // update the ploted spectra based on the distance entered
  public void stateChanged(ChangeEvent e) {
    JSlider source = (JSlider)e.getSource();
    
    if (!source.getValueIsAdjusting()) {
      int xvalue = (int)source.getValue();
      index = (int)((xvalue - minX)/dx); // locates the index in the array
      updatePlots();
    }
  }
  
  // Method to handel chartchange events
  public void chartChanged(ChartChangeEvent event) {
    if(chartPanel != null) {
      JFreeChart chart = chartPanel.getChart();
      XYPlot plot = chart.getXYPlot();
      float xx = (float)plot.getDomainCrosshairValue();
      
      // set the labels now
      //xposLB.setText("" + xx);
    }
  }
  
  // method to update the plot
  private void updatePlots() {
    NumberFormat nf = NumberFormat.getInstance();
    DecimalFormat df = (DecimalFormat) nf;
    df.setDecimalSeparatorAlwaysShown(true);
    
    current_rate = transfer_rate[index][1]/1.0e9f;
    
    // change the efficiency and rate labels
    distanceLB.setText("" + efficiency[index][0]);
    
    df.applyPattern("0.##");
    efficiencyLB.setText("" + df.format(efficiency[index][1]));
    
    df.applyPattern("0.##E0");
    rateLB.setText("" + df.format(transfer_rate[index][1]));
    
    df.applyPattern("0.##");
    tauLB.setText("" + df.format(lifetime[index][1]*1.0e9)); // time in nanoseconds
    
    // calculate the new decays
    calculateDecays();
    
    // get the new data and plot it
    XYSeries Id0_series = getSeries("Donor");
    XYSeries Id1_series = getSeries("(Donor + Acceptor)");
    XYSeries Ia1_series = getSeries("(Acceptor + Donor)");
    
    sim_data.removeAllSeries();
    
    sim_data.addSeries(Id0_series);
    sim_data.addSeries(Id1_series);
    sim_data.addSeries(Ia1_series);
    
    XYPlot plot = (XYPlot)sim_chart.getPlot();
    NumberAxis xaxis = (NumberAxis)plot.getDomainAxis();
    xaxis.setRange(-5.0, max_time);
  }
  
  // Method to set the maximum time of the simulation
  private void setMaximumTime() {
    try {
      max_time = Integer.parseInt(maxT1TF.getText());
      updatePlots();
    }
    catch (NumberFormatException nfe) { }
  }
  
  // Method to set the percent of acceptors that is initially excited
  private void setPercentExcitedAcceptors() {
    try {
      pexA = Float.parseFloat(pexATF.getText())/100.0f;
      updatePlots();
    }
    catch (NumberFormatException nfe) { }
  }
  
  // Method to calculate the decays based on a transfer rate
  private void calculateDecays() {
    int size = max_time + 1; // set the size and creat the data arrays
    Id0 = new float[size][2];
    Id1 = new float[size][2];
    Ia1 = new float[size][2];
    
    // calculate A
    float A = current_rate/((1/acceptor_lifetime) - (1/donor_lifetime) - current_rate);
    float max_Ia = 0.0f;
    
    for(int i = 0; i <= max_time; i++) {
      Id0[i][0] = i;
      Id1[i][0] = i;
      Ia1[i][0] = i;
      
      float exp_value1 = -(1/donor_lifetime) * i;
      Id0[i][1] = (float)Math.exp(exp_value1);
      
      float exp_value2 = -((1/donor_lifetime) + current_rate) * i;
      Id1[i][1] = (float)Math.exp(exp_value2);
      
      float exp_value3 = -((1/donor_lifetime )+ current_rate) * i;
      float exp_value4 = -(1/acceptor_lifetime) * i;
      Ia1[i][1] = (float)(A*Math.exp(exp_value3) +(pexA - A)*Math.exp(exp_value4));
      
      if(Ia1[i][1] > max_Ia) {
        max_Ia = Ia1[i][1];
      }
    }
    
    // normalized the intensity of IA to 1
    for(int i = 0; i <= max_time; i++) {
       Ia1[i][1] /= max_Ia;
    }
  }
  
  // function copy data to the clip board
  private void copyData() {
    Clipboard cb =Toolkit.getDefaultToolkit().getSystemClipboard();
    
    // define a string buffer just in case the data needs to be copied
    StringBuffer buffer = new StringBuffer();
    buffer.append("Time\tDonor\tDonor_Acceptor\tAcceptor_Donor\n");
    for(int i = 0; i <= max_time; i++) {
      buffer.append(Id0[i][0]).append("\t");
      buffer.append(Id0[i][1]).append("\t");
      buffer.append(Id1[i][1]).append("\t");
      buffer.append(Ia1[i][1]).append("\n");
    }
    
    StringSelection contents = new StringSelection(buffer.toString());
    cb.setContents(contents, null);
  }
}//--end class LifetimeSimDialog
