
/**
 * StreakViewDialog.java
 * Used to view streak camera data
 *
 * Created: Jan 22, 2004
 *
 * @author Nathan Stevens
 * @version 0.4 4/27/05
 */
 
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.awt.image.BufferedImage;
import java.awt.image.MemoryImageSource;
import java.awt.Image;

import java.awt.datatransfer.*;
import java.util.*;

public class StreakViewDialog extends JFrame implements ActionListener,
  SpectraDataSource, ListSelectionListener {
  
  // A Few Dialogs
  private InstruView instruView = null;
  private ImagePanel imagePanel = null;
  private DataViewDialog dataViewDialog = null;
  private PlotDialog plotDialog = null;
  private LifeTimeFitDialog lifetimeFitDialog = null;
  
  // Components to store data
  private StreakROIData srd = null;
  private SpectraData sd = null;
  private SpectraData roisd = null;
  private SpectraData roi3Dsd = null;
  private static SpectraData irfsd = null; // stores the instrument responce function
  private String name = null;
  private String[] header = null;
  private String[] dmeta = null;
  private float[][] data = null;
  private int pic_w, pic_h;
  private int width, height;
  private int wl1index, wl2index, t1index, t2index;
  
  // user interface components
  private JRadioButton hroiRB = null;
  private JRadioButton vroiRB = null;
  private JList roiList = null;
  private JTextField wl1TF = null;
  private JTextField wl2TF = null;
  private JTextField t1TF = null;
  private JTextField t2TF = null;
  private JLabel widthLB = null;
  private JLabel centerLB = null;
  private JLabel units1LB = null;
  private JLabel units2LB = null;
  private JTextField sliceTF = null;
  
  // some int fields to keep tract of roi data
  private Vector roidata = new Vector(); // stores the roidata
  private Rectangle rectangle = null;  // The rectangle to draw
  private int direction = 0; // the direction of the roi, horizontal = 0, vertical =1
  private int roicount = 0; // the number of ROI stored
  
  // These variables are for generation of horizontal and vertical slices
  private int horizontal_min = 10;
  private int vertical_min = 10;
  
  // stores the selected roi data
  private int[] selectedIndices = null;
  
  // The scaling factors
  private float width_scale = 2.0f;
  private float height_scale = 2.0f;
  
  // set whether thing should be done in log or not
  private boolean log = false;
  
  // the time unit ps or nanosecond
  private String time_scale = "ps";
  
  public StreakViewDialog(InstruView instruView, String title, SpectraData sd) {
    super(title);
    this.instruView = instruView;
    this.sd = sd;
    name = sd.getName();
    data = sd.getData();
    header = sd.getHeader();
    dmeta = sd.getMetaInfo();
    
    // set the scalinng parameters now
    if(dmeta[4].equals("F900")) {
      horizontal_min = 1; // set this to one
      width_scale = (data[0].length - 1)/256.0f; // make it so that the width is ~ 256 pixels
      height_scale =(data.length - 1)/256.0f ; // make the height ~256 pixels
    }
    
    // set the time scale now
    String ts = header[header.length -1];
    
    if(ts.equals("ps")) {
      time_scale = "ps";
    }
    else if(ts.equals("ns")) {
      time_scale = "ns";
    }
    else if(ts.equals("us")) {
      time_scale = "us";
    }
    else if(dmeta[4].equals("Hamamatsu StreakScope")) {
      time_scale = "ps";
    }
    else if(dmeta[4].equals("F900")) {
      time_scale = "ns";
    }
    else { // default to picoseconds
       time_scale = "ps";
    }
    
    // set whether to work in log
    String key = "log";
    String value =  instruView.plotProperties.getProperty(key);
    
    if(value != null && value.equals("yes")) {
      log = true;
    }
    
    // exits the program
		addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent we) {
				close();
		  }
		});
    
    // Set the layout to border layout
    getContentPane().setLayout(new BorderLayout());
    
    // Some gui components
		JButton button;
    JLabel label;
    TitledBorder title1, title2;
    Insets insets;
     
    // Add the spectra data list
    JPanel panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    
    title1 = BorderFactory.createTitledBorder("Streak Image");
    panel.setBorder(title1);
    
    // add the streak image now
    pic_w = data[0].length -1;
    pic_h = data.length -1;
    
    imagePanel = new ImagePanel(this, getStreakImage(), pic_w, pic_h, width_scale, height_scale);
    panel.add(imagePanel);
    
    // add the horizontal and radio buttons
    JPanel spanel = new JPanel();
    spanel.setLayout(new BoxLayout(spanel, BoxLayout.X_AXIS));
    
    ButtonGroup group = new ButtonGroup();
    
    hroiRB = new JRadioButton("Horizontal ROI");
    hroiRB.setActionCommand("horizontal");
		hroiRB.addActionListener(this);
    hroiRB.setSelected(true);
    group.add(hroiRB);
    spanel.add(hroiRB);
    
    spanel.add(Box.createHorizontalGlue());
    
    vroiRB = new JRadioButton("Vertical ROI");
    vroiRB.setActionCommand("vertical");
		vroiRB.addActionListener(this);
    group.add(vroiRB);
    spanel.add(vroiRB);
    
    panel.add(spanel);
    getContentPane().add(panel, "Center");
    
    // Add the spectra roi data list
    JPanel roiPanel = new JPanel();
    //roiPanel.setLayout(new BoxLayout(roiPanel, BoxLayout.Y_AXIS));
    roiPanel.setLayout(new BorderLayout());
    
    title2 = BorderFactory.createTitledBorder("Regions of Interest");
    roiPanel.setBorder(title2);
    
    JPanel roiPanel1 = new JPanel();
    roiPanel1.setLayout(new BoxLayout(roiPanel1, BoxLayout.Y_AXIS));
    
    // Add the wavelength limits
    JPanel rpA = new JPanel();
    rpA.setLayout(new FlowLayout(FlowLayout.LEFT));
    
    label = new JLabel("Wl");
    rpA.add(label);
    
    rpA.add(Box.createRigidArea(new Dimension(12,0)));
    
    wl1TF = new JTextField("000.000", 7);
    rpA.add(wl1TF);
    
    label = new JLabel(" To ");
    rpA.add(label);
    
    wl2TF = new JTextField("000.000", 7);
    rpA.add(wl2TF);
    
    label = new JLabel(" nm");
    rpA.add(label);
    
    roiPanel1.add(rpA);
    
    //Add the time limits
    JPanel rpB = new JPanel();
    rpB.setLayout(new FlowLayout(FlowLayout.LEFT));
    
    label = new JLabel("Time ");
    rpB.add(label);
    
    t1TF = new JTextField("00000", 7);
    rpB.add(t1TF);
    
    label = new JLabel(" To ");
    rpB.add(label);
    
    t2TF = new JTextField("00000", 7);
    rpB.add(t2TF);
    
    label = new JLabel(" " + time_scale);
    rpB.add(label);
    
    roiPanel1.add(rpB);
    
    // add the panel that hold the width and center infomation
    JPanel widthPanel = new JPanel();
    widthPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
    
    label = new JLabel("width ");
    widthPanel.add(label);
    
    widthLB = new JLabel("0000");
    widthLB.setForeground(Color.red);
    widthPanel.add(widthLB);
    
    units1LB = new JLabel(time_scale + " ");
    widthPanel.add(units1LB);
    
    label = new JLabel("center @ ");
    widthPanel.add(label);
    
    centerLB = new JLabel("0000");
    centerLB.setForeground(Color.red);
    widthPanel.add(centerLB);
    
    units2LB = new JLabel(time_scale);
    widthPanel.add(units2LB);
    
    roiPanel1.add(widthPanel);
    
    // add the panel that holds the slice buttons
    JPanel slicePanel = new JPanel();
    slicePanel.setLayout(new FlowLayout(FlowLayout.LEFT));
    
    button = new JButton("Wavelength");
		button.setActionCommand("wslice");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    slicePanel.add(button);
    
    button = new JButton("Time");
		button.setActionCommand("tslice");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    slicePanel.add(button);
    
    label = new JLabel(" Slice ");
    slicePanel.add(label);
    
    sliceTF = new JTextField("100", 3);
    slicePanel.add(sliceTF);
    
    label = new JLabel(" nm/" + time_scale);
    slicePanel.add(label);
    
    roiPanel1.add(slicePanel);
    
    // add the roiList action buttons
    JPanel rpC = new JPanel();
    rpC.setLayout(new FlowLayout(FlowLayout.LEFT));
    
    button = new JButton("Add");
		button.setActionCommand("addroi");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    rpC.add(button);
    
    button = new JButton("Remove");
		button.setActionCommand("removeroi");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    rpC.add(button);
    
    button = new JButton("Remove All");
		button.setActionCommand("removeall");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    rpC.add(button);
    
    button = new JButton("UnS");
		button.setActionCommand("unselect");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    rpC.add(button);
    
    button = new JButton("Copy");
		button.setActionCommand("copy");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    rpC.add(button);
    
    roiPanel1.add(rpC);
    
    roiPanel.add(roiPanel1, "North");
    
    // add the text area that display the slice data
    roiList = new JList();
    roiList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    roiList.addListSelectionListener(this);
    
    JScrollPane scrollPane = new JScrollPane(roiList);
    
    roiPanel.add(scrollPane, "Center");
    
    getContentPane().add(roiPanel, "East");
    // add the button panel south
		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
    
    button = new JButton("View");
		button.setActionCommand("view");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    buttonPanel.add(button);
    
    button = new JButton("View 3D");
		button.setActionCommand("view3d");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    buttonPanel.add(button);
    
    button = new JButton("Plot");
		button.setActionCommand("plot");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    buttonPanel.add(button);
    
    button = new JButton("Fit");
		button.setActionCommand("fit");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    buttonPanel.add(button);
    
    button = new JButton("Put");
		button.setActionCommand("put");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    buttonPanel.add(button);
    
    button = new JButton("Put 3D");
		button.setActionCommand("put3d");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    buttonPanel.add(button);
    
    button = new JButton("Store IRF");
		button.setActionCommand("storeirf");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    buttonPanel.add(button);
    
    // set button to save image
    button = new JButton("Save Image");
    button.setActionCommand("saveimage");
    button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    buttonPanel.add(button);
    
    buttonPanel.add(Box.createHorizontalGlue());
    
    button = new JButton("Close");
		button.setActionCommand("close");
		button.addActionListener(this);
    insets = new Insets(2, 2, 2, 2);
    button.setMargin(insets);
    buttonPanel.add(button);
    
    getContentPane().add(buttonPanel, "South");
    
    // initialize the LifeTimeFitDialog
    lifetimeFitDialog = new LifeTimeFitDialog(name, this, instruView.plotProperties);
    lifetimeFitDialog.pack();
  }
  
  // Method to handel action events
  public void actionPerformed(ActionEvent ae) {
    String cmd = ae.getActionCommand();
    
    if(cmd.equals("view")) {
      view2DROIData();
		}
    else if(cmd.equals("view3d")) {
      view3DROIData();
		}
    else if(cmd.equals("plot")) {
      plotROI();
		}
    else if(cmd.equals("fit")) {
      fitData();
		}
    else if(cmd.equals("put")) {
      putROI();
		}
    else if(cmd.equals("put3d")) {
      putROI3D();
		}
    else if(cmd.equals("storeirf")) {
      storeIRF();
		}
    else if(cmd.equals("horizontal")) {
      direction = 0;
    }
    else if(cmd.equals("vertical")) {
      direction = 1;
    }
    else if(cmd.equals("wslice")) {
      createWavelengthSlices();
    }
    else if(cmd.equals("tslice")) {
      createTimeSlices();
    }
    else if(cmd.equals("addroi")) {
      addROI();
    }
    else if(cmd.equals("removeroi")) {
      removeROI();
    }
    else if(cmd.equals("removeall")) {
      removeAllROI();
    }
    else if(cmd.equals("unselect")) {
      roiList.clearSelection();
      selectedIndices = null;
      srd = null;
    }
    else if(cmd.equals("copy")) {
      copyROIData();
    }
    else if(cmd.equals("close")) {
      close();
    }
    else {
      System.err.println("Action Command not handeled in class StreakViewDialog...");
      System.err.println(cmd);
    }
  }
  
  // Method to handle list selection events
  public void valueChanged(ListSelectionEvent e) {
    if (e.getValueIsAdjusting()) {
      return;
    }
    
    if (roiList.isSelectionEmpty()) { } //do nothing
    else {
      selectedIndices = roiList.getSelectedIndices();
      
      // display the first one selected
      srd = (StreakROIData)roidata.get(selectedIndices[0]);
      roisd = srd.getROI2D();
      roi3Dsd = srd.getROI3D();
      Rectangle r = (Rectangle)srd.getRectangle().clone(); // must clone to prevent
      imagePanel.drawROI(r);
      
      int dir = srd.getDirection();
      if(dir == 0) {  // horizontal direction
        hroiRB.setSelected(true);
        direction = 0;
      }
      else {  // vertical direction
        vroiRB.setSelected(true);
        direction = 1;
      }
    }
  }
  
  // Method to return the current regiion of interest data
  public SpectraData getSpectraData() {
    getROI();
    return roisd;
  }
  
  // Method to load spectra data from the data extract dialog
  public void  setSpectraData(String exptype, SpectraData[] sda) {
    for(int i = 0; i < sda.length; i++) {
      roidata.add(sda[i]);
    }
    roiList.setListData(roidata);
  }
  
  // Method to set any results
  public void setResults(String results) {
    if(srd != null) {
      srd.setNotes(results);
    }
  }
  
  // Method to get the store IRF
  public SpectraData getIRF() {
    return irfsd;
  }
  
  // Method to set the instrument response
  public void storeIRF() {
    getROI();
    if(roisd != null) {
      // create a copy of 2D ROI SpectraData object
      String sdname = roisd.getName() + "-" + roicount;
      String[] copyOfdmeta = (String[])dmeta.clone();
      String[] copyOfheader = (String[])roisd.getHeader().clone();
      float[][] data2d = roisd.getData();
      
      SpectraData copyOfroisd = new SpectraData(sdname, "streak", copyOfheader, 
                                                                                   copyOfdmeta, data2d, null);
      copyOfroisd.setProfileDirection(direction);
      
      irfsd = copyOfroisd;
    }
  }
  
  //Method to update the region of interest
  public void updateROI(Rectangle rect) {
    rectangle = rect;  //set the current rectangle
    
    width = Math.round(rect.width*width_scale);
    height = Math.round(rect.height*height_scale);
    int x = Math.round(rect.x*width_scale);
    int y = Math.round(rect.y*height_scale);
    
    //Make the coordinates look OK if a dimension is 0.
    if (width == 0 || width > pic_w) {
      x= 1;
      width = pic_w;
    }
    if (height == 0 || height > pic_h) {
      y = 1;
      height = pic_h;
    }
    
    if(x == 0) {
      x = 1;
    }
    
    if(y == 0) {
      y = 1;
    }
    
    // set the indices now
    wl1index = x;
    wl2index = x+ (width-1);
    t1index = y;
    t2index = y+ (height-1);
    
    // check bounds one more time and correct if nessary
    if(wl2index > pic_w) {
      wl2index = pic_w;
    }
    
    if(t2index > pic_h) {
      t2index = pic_h;
    }
    
    // update the labels
    String wl1 = data[0][wl1index] + "";
    String wl2 = data[0][wl2index] + "";
    wl1TF.setText(wl1);
    wl2TF.setText(wl2);
    
    String t1 = data[t1index][0] + "";
    String t2 = data[t2index][0] + "";
    t1TF.setText(t1);
    t2TF.setText(t2);
    
    // calculate the width and center
    int roi_width = 0;
    int roi_center = 0;
    
    if(direction == 0) {
      roi_width = Math.round(data[t2index][0] - data[t1index][0]);
      roi_center = Math.round((data[t2index][0] + data[t1index][0])/2);
      widthLB.setText(roi_width + "");
      centerLB.setText(roi_center + "");
      units1LB.setText(time_scale);
      units2LB.setText(time_scale);
    }
    else {
      roi_width = Math.round(data[0][wl2index] - data[0][wl1index]);
      roi_center = Math.round((data[0][wl2index] + data[0][wl1index])/2);
      widthLB.setText(roi_width + "");
      centerLB.setText(roi_center + "");
      units1LB.setText("nm");
      units2LB.setText("nm");
    }
  }
  
  // Method to view 2D Data
  private void view2DROIData() {
    getROI();
    dataViewDialog = new DataViewDialog(roisd.toString(), roisd);
    dataViewDialog.pack();
    dataViewDialog.setVisible(true);
  }
  
  // Method to view 3D Data
  private void view3DROIData() {
    getROI();
    dataViewDialog = new DataViewDialog(roi3Dsd.toString(), roi3Dsd);
    dataViewDialog.pack();
    dataViewDialog.setVisible(true);
  }
  
  // Method to plot the roi
  private void plotROI() {
    getROI();
    SpectraData[] roisda = null;
    
    if(selectedIndices != null && selectedIndices.length >= 1) {
      int size = selectedIndices.length;
      roisda = new SpectraData[size];
      StreakROIData temp_srd = null;
      
      for(int i = 0; i < size; i++) {
        temp_srd =  (StreakROIData)roidata.get(selectedIndices[i]);
        roisda[i] = temp_srd.getROI2D();
      }
    }
    else if (roisd != null) {
      roisda = new SpectraData[1];
      roisda[0] = roisd;
    }
    else {
      return;
    }
    
    plotDialog = new PlotDialog("Streak ROI", "streak", roisda, instruView.plotProperties);
    plotDialog.setSize(550, 450);
    plotDialog.setVisible(true);
  }
  
  // Method to fit lifetime data
  private void fitData() {
    getROI();
    lifetimeFitDialog.setVisible(true);
    lifetimeFitDialog.setSpectraData(roisd);
  }
  
  // method to add the roi to the streak panel
  private void putROI() {
    getROI();
    
    if(selectedIndices != null && selectedIndices.length >= 1) {
      int size = selectedIndices.length;
      SpectraData[] roisda = new SpectraData[size];
      StreakROIData temp_srd = null;
      
      for(int i = 0; i < size; i++) {
        temp_srd =  (StreakROIData)roidata.get(selectedIndices[i]);
        roisda[i] = temp_srd.getROI2D();
      }
      instruView.loadSpectraData("streak", roisda);
    }
    else if(roisd != null) {
      instruView.loadSpectraData("streak", roisd);
    }
  }
  
  // Method to add an 3D roi to the streak panel
  private void putROI3D() {
    getROI();
    
    if(selectedIndices != null && selectedIndices.length >= 1) {
      int size = selectedIndices.length;
      SpectraData[] roi3Dsda = new SpectraData[size];
      StreakROIData temp_srd = null;
      
      for(int i = 0; i < size; i++) {
        temp_srd =  (StreakROIData)roidata.get(selectedIndices[i]);
        roi3Dsda[i] = temp_srd.getROI3D();
      }
      instruView.loadSpectraData("streak", roi3Dsda);
    }
    else if(roi3Dsd != null) {
      instruView.loadSpectraData("streak", roi3Dsd);
    }
  }
  
  // Method to add a ROI to the list
  private void addROI() {
    getROI();
    
    // now create a roiDataObject
    StreakROIData srdata;
    String parent = sd.getName();
    String name = "";
    
    roicount++;
    if(direction == 0) {
      name = "roiH-" + roicount;
    }
    else {
      name = "roiV-" + roicount;
    }
    
    // create a copy of 2D ROI SpectraData object
    String sdname = roisd.getName() + "-" + roicount;
    String[] copyOfdmeta = (String[])dmeta.clone();
    String[] copyOfheader = (String[])roisd.getHeader().clone();
    float[][] data2d = roisd.getData();
    
    SpectraData copyOfroisd = new SpectraData(sdname, "streak", copyOfheader, 
                                                                                 copyOfdmeta, data2d, null);
    
    copyOfroisd.setProfileDirection(direction);
    
    // create a copy of 3D ROI SpectraData object
    sdname = roi3Dsd.getName() + "-" + roicount;
    float[][] data3d = roi3Dsd.getData();
    
    SpectraData copyOfroi3Dsd = new SpectraData(sdname, "streak", copyOfheader, 
                                                                                 copyOfdmeta, data3d, null);
    copyOfroi3Dsd.set3D(true);
    
    // now create the StreakROIData object and add it to the list
    Rectangle r = new Rectangle(rectangle);
    
    srdata = new StreakROIData(parent, name, direction, copyOfroisd, copyOfroi3Dsd, r);
    roidata.add(srdata);
    roiList.setListData(roidata);
  }
  
  // Method to remove a single ROI from the list
  private void removeROI() {
    if (selectedIndices != null && roidata.size() != 0) {
      for(int i = 0; i < selectedIndices.length; i++) {
        int index = selectedIndices[i] - i;
        roidata.remove(index);
      }
      
      roiList.clearSelection();
      roiList.setListData(roidata);
      selectedIndices= null;
      srd = null;
    }
  }
  
  // Method to removeAllROI from list
  private void removeAllROI() {
    roidata.clear();
    roiList.clearSelection();
    roiList.setListData(roidata);
    selectedIndices= null;
    srd = null;
    roicount = 0; // reset the count
  }
  
  // Method to copy all ROI data into the system clip board
  private void copyROIData() {
    if(selectedIndices != null && selectedIndices.length >= 1) {
      int size = selectedIndices.length;
      StreakROIData temp_srd = null;
      SpectraData roisd = null;
      float[][] xydata = null;
      String[][] sda = null; // The string data array
      String xlabel = null;
      String ylabel = null;
      StringBuffer buffer = new StringBuffer();
      
      for(int i = 0; i < size; i++) {
        temp_srd =  (StreakROIData)roidata.get(selectedIndices[i]);
        ylabel = temp_srd.toString();
        roisd = temp_srd.getROI2D();
        xydata = roisd.getData();
        
        // initialize the string data array
        if(i == 0) {
          sda = new String[xydata.length+ 1][size + 1];
          
          if(ylabel.indexOf("roiH") != -1) {
            xlabel = "wavelength";
          }
          else {
            xlabel = "time (ps)";
          }
          
          sda[0][0] = xlabel;
          sda[0][1] = ylabel;
          
          for(int j = 0; j < xydata.length; j++) {
            sda[j+1][0] = "" + xydata[j][0];
            sda[j+1][1] = "" + xydata[j][1];
          }
        }
        else {
          sda[0][i+1] = ylabel;
          for(int j = 0; j < xydata.length; j++) {
            sda[j+1][i+1] = "" + xydata[j][1];
          }
        }
      }
      
      // copy to string data array into the string buffer now
      for(int i = 0; i < sda.length; i++) {
        for(int j = 0; j < sda[0].length; j++) {
          buffer.append(sda[i][j]);
          if((j + 1) != sda[0].length) {
            buffer.append('\t');
          }
          else {
            buffer.append('\n');
          }
        }
      }
      
      // finally copy to the clip board
      String s = buffer.toString();
      StringSelection contents = new StringSelection(s);
      Clipboard cb =Toolkit.getDefaultToolkit().getSystemClipboard();
      cb.setContents(contents, null);
    }
  }
  
  // Method to create wavelength slices
  // ok, although it slices along the wavelength axis it actual create time profiles.
  private void createWavelengthSlices() {
    float slice_width = 0.0f;
    float precision = data[0][wl1index+1] - data[0][wl1index];
    float wl_width = (width - 1)*precision;
    int step_inc = 0;  // increment were we are 
    
    try {
      slice_width = Float.parseFloat(sliceTF.getText());
      step_inc = (int)(slice_width/precision);
    }
    catch(NumberFormatException nfe) {
      return;
    }
    
    // debug code
    //System.out.println("WL1 index " + wl1index + " WL2 index " + wl2index);
    
    if(wl_width >= horizontal_min && slice_width >= horizontal_min)  {
      // store some old variable
      Rectangle old_rectangle = (Rectangle)rectangle.clone();
      int old_wl1index = wl1index;
      int old_width = width;
      
      //set the width now
      width = step_inc;
      
      for(int i = wl1index; i <= wl2index; i += step_inc) {
        wl1index = i;
        direction = 1;
        
        rectangle = new Rectangle(Math.round((i-1)/width_scale), 
        Math.round(t1index/height_scale), 
        Math.round(width/width_scale), 
        Math.round(height/height_scale));
        
        if((i + width) <= (pic_w + 1)) {
          addROI();
        }
      }
      
      // reset the old value
      rectangle = old_rectangle;
      wl1index = old_wl1index;
      width = old_width;
    }
  }
  
  // Method to create time slices
  private void createTimeSlices() {
    float slice_height = 0.0f;
    float precision = data[t1index+ 1][0] - data[t1index][0];
    float time_width = (height - 1)*precision;
    int step_inc = 0;  // increment we are 
    
    try {
      slice_height = Float.parseFloat(sliceTF.getText());
      step_inc = (int)(slice_height/precision);
      
      /*debug code
      System.out.println("Slice height is " + slice_height);
      System.out.println("Step increment " + step_inc);
      System.out.println("height is "+ height);
      System.out.println("time width is "+ time_width);*/
    }
    catch(NumberFormatException nfe) {
      return;
    }
    
    if(time_width > vertical_min && slice_height > vertical_min)  {
      // store some old variable
      Rectangle old_rectangle = (Rectangle)rectangle.clone();
      int old_t1index = t1index;
      int old_height = height;
      
      //set the width now
      height = step_inc;
      
      for(int i = old_t1index; i < t2index - step_inc; i += step_inc) {
        t1index = i;
        direction = 0;
        
        rectangle = new Rectangle(Math.round((wl1index-1)/width_scale), 
        Math.round(i/height_scale), 
        Math.round(width/width_scale), 
        Math.round(height/height_scale));
        
        addROI();
      }
      
      // reset the old value
      rectangle = old_rectangle;
      t1index = old_t1index;
      height = old_height;
    }
  }
  
  // Method to get the region of interest
  private void getROI() {
    float[][] roidata = SpectraDataUtil.getROIData(data, wl1index, width, 
                                    t1index, height);
    
    // create the 3D spectra data object
    String[] copyOfdmeta = (String[])dmeta.clone();
    String name = sd.getName() + "ROI3D";
    String minwl = "" + roidata[0][1];
    String maxwl = "" + roidata[0][roidata[0].length-1];
    String mint = "" + roidata[1][0];
    String maxt = "" + roidata[roidata.length-1][0];
    String slaser = new String(header[5]);
    String dps = "" + (width*height);
    String sexp = new String(header[6]);
    String snexp = new String(header[7]);
    String ctype = new String(header[8]);
    String notes = new String(header[9]);
    
    String[] roiheader = {minwl, maxwl, mint, maxt, dps, slaser, sexp, snexp, 
                                         ctype, notes, time_scale};
    roi3Dsd = new SpectraData(name, "streak", roiheader, copyOfdmeta, roidata, null);
    roi3Dsd.set3D(true);
    
    // now get the 2D data
    String[] copyOfroiheader = (String[])roiheader.clone();
    
    float[][] data2d;
    String nameExt;
    String exptype;
    if(direction == 0) {
      nameExt = "ROIH";
      data2d = SpectraDataUtil.integrateH(roidata);
    }
    else {
      nameExt = "ROIV";
      data2d = SpectraDataUtil.integrateV(roidata);
    }
    
    // set the correct data point count
    copyOfroiheader[4] = "" + data2d.length;
    
    name = sd.getName() + nameExt;
    roisd = new SpectraData(name, "streak", copyOfroiheader, copyOfdmeta, data2d, null);
    roisd.setProfileDirection(direction);
  }
  
  // Method to create the streak camera image
  private Image getStreakImage() {
    int w = data[0].length - 1;
    int h = data.length -1;
    int pix[] = new int[w * h];
    int index = 0;
    
    // see whether to a log plot
    float[][] xydata = data;
    
    if(log) {
      xydata = getLogData();
    }
    
    // find the min and max values
    float min = 10000.0f;
    float max = 0.0f;
    
    for(int i = 1; i < xydata[0].length; i++) {
      for(int j = 1; j <xydata.length; j++) {
        float intensity = xydata[j][i];
        
        if(intensity < min) {
          min = intensity;
        }
        
        if(intensity > max) {
          max = intensity;
        }
      }
    }
    
    // set the maxuum blue increment
    float blue_increment = max/255;
    
    // now create the image
    for (int y = 0; y < h; y++) {
	    for (int x = 0; x < w; x++) {
        int blue = 0;
        
        /*if(xydata[y+1][x+1] >= 0.002*max) {
          blue = 155;
        }
        
        if(xydata[y+1][x+1] >= 0.01*max) {
          blue = 255;
        }*/
        
        if(!log) {
          if(xydata[y+1][x+1] >= 0.002*max) {
            blue = 155;
          }
          
          if(xydata[y+1][x+1] >= 0.01*max) {
            blue = 255;
          }
        }
        else {
          //see whether to brighten image using a bfactor
          int bfactor = 0;
          if(xydata[y + 1][x + 1] > 0) {
            bfactor = 35;
          }
          
          blue = (int)(xydata[y + 1][x + 1]/blue_increment + bfactor);
        }
        
        if(blue > 255) {
          blue = 255;
        }
        
        index = y*w + x;
        pix[index] = (255 << 24) | blue;
	    }
    }
    
    Image img = createImage(new MemoryImageSource(w, h, pix, 0, w));
    
    return img;
  }
  
  // Method to return log data
  private float[][] getLogData() {
    float[][] nxydata = new float[data.length][data[0].length];
    
    for(int i = 1; i < data.length; i++) {
      for(int j = 1; j < data[0].length; j++) {
        if(data[i][j] > 1.0f) { // avoid negative numbers
          nxydata[i][j] = (float)Math.log(data[i][j]);
        }
        else {
          nxydata[i][j] = 0.0f;
        }
      }
    }
    
    return nxydata;
  }
  
  // Method to close the window
  private void close() {
    setVisible(false);
    dispose();
    if(lifetimeFitDialog != null) {
      lifetimeFitDialog.setVisible(false);
      lifetimeFitDialog.dispose();
    }
  }
} //--end class StreakViewDialog

// Image Panel class
class ImagePanel extends JPanel {
  private Rectangle currentRect = null;
  private Rectangle rectToDraw = null;
  private Rectangle previousRectDrawn = new Rectangle();
  private Image image;
  private StreakViewDialog streakViewDialog;
  private int w;
  private int h;
  //private int direction = -1;
  //private int spacing = 0;
  
  public ImagePanel(StreakViewDialog streakViewDialog, Image image, int w, int h,
  float width_scale, float height_scale) {
    this.streakViewDialog = streakViewDialog;
    this.image = image;
    this.w = Math.round(w/width_scale);
    this.h = Math.round(h/height_scale);
    
    // set the preferred size to the size of the image
    setPreferredSize(new Dimension(this.w, this.h));
    
    MyListener myListener = new MyListener();
    addMouseListener(myListener);
    addMouseMotionListener(myListener);
  }
  
  class MyListener extends MouseInputAdapter {
    public void mousePressed(MouseEvent e) {
      int x = e.getX();
      int y = e.getY();
      currentRect = new Rectangle(x, y, 0, 0);
      updateDrawableRect(getWidth(), getHeight());
      repaint();
    }
    
    public void mouseDragged(MouseEvent e) {
      updateSize(e);
    }
    
    public void mouseReleased(MouseEvent e) {
      updateSize(e);
    }
    
    /*
    * Update the size of the current rectangle
    * and call repaint.  Because currentRect
    * always has the same origin, translate it
    * if the width or height is negative.
    *
    * For efficiency (though
    * that isn't an issue for this program),
    * specify the painting region using arguments
    * to the repaint() call.
    *
    */
    public void updateSize(MouseEvent e) {
      int x = e.getX();
      int y = e.getY();
      currentRect.setSize(x - currentRect.x, y - currentRect.y);
      updateDrawableRect(getWidth(), getHeight());
      Rectangle totalRepaint = rectToDraw.union(previousRectDrawn);
      repaint(totalRepaint.x, totalRepaint.y,
      totalRepaint.width, totalRepaint.height);
    }
  }
  
  public void paintComponent(Graphics g) {
    super.paintComponent(g); //paint background
    
    //Draw image at half it's natural size
    g.drawImage(image, 0, 0, w, h, this);
    
    //If currentRect exists, paint a box on top.
    if (currentRect != null) {
      //Draw a rectangle on top of the image.
	    g.setColor(Color.white);
      g.drawRect(rectToDraw.x, rectToDraw.y,
      rectToDraw.width - 1, rectToDraw.height - 1);
    }
    
    // tell the streak view dialog what was drawn
    if(rectToDraw != null) {
      streakViewDialog.updateROI(rectToDraw);
    }
  }
  
  public void updateDrawableRect(int compWidth, int compHeight) {
    int x = currentRect.x;
    int y = currentRect.y;
    int width = currentRect.width;
    int height = currentRect.height;
    
    //Make the width and height positive, if necessary.
    if (width < 0) {
      width = 0 - width;
      x = x - width + 1;
      if (x < 0) {
        width += x;
        x = 0;
      }
    }
    if (height < 0) {
      height = 0 - height;
      y = y - height + 1;
      if (y < 0) {
        height += y;
        y = 0;
      }
    }
    
    //The rectangle shouldn't extend past the drawing area.
    if ((x + width) > compWidth) {
      width = compWidth - x;
    }
    if ((y + height) > compHeight) {
      height = compHeight - y;
    }
    
    //Update rectToDraw after saving old value.
    if (rectToDraw != null) {
      previousRectDrawn.setBounds(
      rectToDraw.x, rectToDraw.y,
      rectToDraw.width, rectToDraw.height);
      rectToDraw.setBounds(x, y, width, height);
    } else {
      rectToDraw = new Rectangle(x, y, width, height);
    }
  }
  
  // Method to set the lines to be drawn either vertical or horizontal
  public void drawROI(Rectangle rectangle) {
    rectToDraw = rectangle;
    repaint();
  }
}//--end class ImagePanel
