package org.spview.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Point2D;
import java.io.File;
import java.util.ArrayList;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.filechooser.FileNameExtensionFilter;

import org.spview.filehandler.FortranFormat;
import org.spview.filehandler.DataFile;
import org.spview.peakfinding.DigitalFilter;
import java.awt.Toolkit;

/////////////////////////////////////////////////////////////////////

/**
 * Window for peak file creation.
 */
public class JFCreatePeakFile extends JFrame implements ActionListener {
	/**
	 * 
	 */
	private static final long serialVersionUID = -7784282622048412967L;
	private final String workd; // working directory
	//
	private final JButton jbfile; // spectrum file choice
	private final JLabel jlnfile; // show spectrum file name
	private final JButton jbclose; // cancel button
	private final JButton jbfind; // create button
	private final JTextField jftfwidth; // jftf for polynom degree
	private final JTextField jtfthreshold; // jftf for height threshold
	private final JCheckBox inverseButton;
	private final JTextField xuncvalue;
	private final JTextField yuncvalue;
	DataFile dataf;
	// variables
	private int filterWidth; // filter width
	private float threshold; // height threshold
	private String nfile; // spectrum full file name
	private Point2D uncert; // X and Y uncertainties

/////////////////////////////////////////////////////////////////////

	/**
	 * Construct a new JFCreatePeakFile.
	 *
	 */
	public JFCreatePeakFile(DataFile dataf) {

		super("Peak File Creation"); // main window
		setIconImage(Toolkit.getDefaultToolkit().getImage(JFCreatePeakFile.class.getResource("/pixmaps/spview.png")));

		addWindowListener(new WindowAdapter() { // clean end
			public void windowClosing(WindowEvent e) {
				dispose(); // free window
			}
		});

		workd = System.getProperty("user.dir"); // working directory

		this.dataf = dataf;
		nfile = this.dataf == null ? "" : this.dataf.getname();

		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setLocation(100, 100); // location
		setSize(405, 254); // size

		// panels
		getContentPane().setLayout(new BorderLayout());
		JPanel jp = new JPanel(new BorderLayout());
		jp.setBorder(null);
		getContentPane().add(jp, "Center");

		// panels
		JPanel pouest = new JPanel(new GridLayout(6, 1, 5, 5));
		JPanel pcentre = new JPanel(new GridLayout(6, 1, 5, 5));
		JPanel psud = new JPanel();

		// spectrum file
		jbfile = new JButton("Spectrum file...");
		jbfile.setToolTipText("Select the spectrum file to process");
		jbfile.addActionListener(this);
		jlnfile = new JLabel(nfile);
		// polynom degree
		//
		JLabel jldwidth = new JLabel("Filter Width:");
		jftfwidth = new JTextField("7");
		jftfwidth.setToolTipText("This is the filter width (in # of points) used for the convolution.");
		// height threshold
		// show height threshold
		JLabel jlthreshold = new JLabel("Height Threshold:");
		jtfthreshold = new JTextField("0.0");
		jtfthreshold.setToolTipText("This is the line height threshold above which lines are detected.");

		pouest.add(jbfile);
		pcentre.add(jlnfile);
		pouest.add(jldwidth);
		pcentre.add(jftfwidth);
		pouest.add(jlthreshold);
		pcentre.add(jtfthreshold);
		Box boxsud = Box.createHorizontalBox(); // add buttons to box
		jbfind = new JButton("Find Peaks");
		boxsud.add(jbfind);
		jbfind.setToolTipText("Create peak file");
		jbfind.addActionListener(this);
		boxsud.add(Box.createHorizontalStrut(15));

		// south
		jbclose = new JButton("Close");
		jbclose.setToolTipText("Cancel peak file creation");
		jbclose.addActionListener(this);
		boxsud.add(jbclose);
		psud.add(boxsud);

		// insert panels
		jp.add(pouest, "West");

		//
		Component verticalGlue = Box.createVerticalGlue();
		pouest.add(verticalGlue);

		JLabel jtuncx = new JLabel("X uncertainty");
		pouest.add(jtuncx);

		JLabel jtuncy = new JLabel("Y uncertainty");
		pouest.add(jtuncy);
		jp.add(pcentre, "Center");

		inverseButton = new JCheckBox("Down");
		inverseButton.setToolTipText("Check this option when spectrum is bottom oriented.");
		pcentre.add(inverseButton);

		xuncvalue = new JTextField("0.001000");
		xuncvalue.setToolTipText("x uncertainties are written in the peak file output.");
		pcentre.add(xuncvalue);
		xuncvalue.setColumns(10);

		yuncvalue = new JTextField("10.0");
		yuncvalue.setToolTipText("y uncertainties are written in the peak file output.");
		pcentre.add(yuncvalue);
		yuncvalue.setColumns(10);

		jp.add(psud, "South");

	}

	private static double getMinData(double[] data, int size) {
		double minData = Double.MAX_VALUE;
		for (int i = 0; i < size; i++) {
			minData = Math.min(minData, data[i]);
		}
		return minData;
	}

	private static double getMaxData(double[] data, int size) {
		double maxData = Double.MIN_VALUE;
		for (int i = 0; i < size; i++) {
			maxData = Math.max(maxData, data[i]);
		}
		return maxData;
	}

	private static String removeExtention(String filePath) {
		// These first few lines the same as Justin's
		File f = new File(filePath);

		// if it's a directory, don't remove the extention
		if (f.isDirectory())
			return filePath;

		String name = f.getName();

		// Now we know it's a file - don't need to do any special hidden
		// checking or contains() checking because of:
		final int lastPeriodPos = name.lastIndexOf('.');
		if (lastPeriodPos <= 0) {
			// No period after first character - return name as it was passed in
			return filePath;
		} else {
			// Remove the last period and everything after it
			File renamed = new File(f.getParent(), name.substring(0, lastPeriodPos));
			return renamed.getPath();
		}
	}

	private boolean checkValue() {
		if (dataf == null) {
			return false;
		}
		filterWidth = Integer.parseInt(jftfwidth.getText());
		if (filterWidth <= 0) {
			JOptionPane.showMessageDialog(null, "Filter width has to be > 3");
			return false;
		}
		// height threshold
		try {
			threshold = Float.parseFloat(jtfthreshold.getText());
		} catch (NumberFormatException nfe) {
			// format error
			JOptionPane.showMessageDialog(null, "threshold is not valid");
			jtfthreshold.setText("0.0");
			return false;
		}
		if (threshold < 0.0) {
			JOptionPane.showMessageDialog(null, "Threshold has to be >= 0.0");
			jtfthreshold.setText("0.0");
			return false;
		}

		float xUncert, yUncert;

		try {
			xUncert = Float.parseFloat(xuncvalue.getText());
		} catch (NumberFormatException nfe) {
			// format error
			JOptionPane.showMessageDialog(null, "X uncertainty is not valid");
			xuncvalue.setText("0.001000");
			return false;
		}
		if (xUncert < 0.0) {
			JOptionPane.showMessageDialog(null, "X uncertainty has to be >= 0.0");
			xuncvalue.setText("0.001000");
			return false;
		}
		try {
			yUncert = Float.parseFloat(yuncvalue.getText());
		} catch (NumberFormatException nfe) {
			// format error
			JOptionPane.showMessageDialog(null, "Y uncertainty is not valid");
			yuncvalue.setText("10.0");
			return false;
		}
		if (yUncert < 0.0) {
			JOptionPane.showMessageDialog(null, "Y uncertainty has to be >= 0.0");
			yuncvalue.setText("10.0");
			return false;
		}
		uncert = new Point2D.Float(xUncert, yUncert);
		return true;
	}

	private File pickAFileName(String nameSuggestion) {
		// This method creates an XML File to save the file(s) added in the project.
		// choose the project file
		JFileChooser jfcfile = new JFileChooser(nameSuggestion); // default choice directory
		FileNameExtensionFilter filter = new FileNameExtensionFilter("Peak list file (*.asg)", "asg"); // Only files
																										// .spv

		jfcfile.setSize(400, 300);
		jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only
		jfcfile.setDialogTitle("Save Peak List");
		jfcfile.addChoosableFileFilter(filter); // add the filter created above
		jfcfile.setAcceptAllFileFilterUsed(false); // All types of file are NOT accepted
		jfcfile.setSelectedFile(new File(nameSuggestion));

		if (jfcfile.showSaveDialog(this.getParent()) == JFileChooser.APPROVE_OPTION) {
			File selectedFile = jfcfile.getSelectedFile();
			if (!selectedFile.getAbsolutePath().endsWith(".asg")) {
				selectedFile = new File(jfcfile.getSelectedFile() + ".asg");
			}
			if (!selectedFile.exists() || (selectedFile.exists()
					&& JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(this.getParent(),
							"File " + selectedFile.getAbsolutePath() + " already exists! Overwrite?", "Save",
							JOptionPane.YES_NO_OPTION))) {
				return selectedFile;
			}
		}
		return null;
	}

	private boolean savePeakList(ArrayList<Point2D> peakPositions) {
		String newFilename = removeExtention(this.nfile);
		File pickedFile = pickAFileName(newFilename);
		if (pickedFile != null) {
			FortranFormat.printAssignmentFormattedOutput(peakPositions, uncert, pickedFile);
			return true;
		}
		return false;
	}

/////////////////////////////////////////////////////////////////////

	/**
	 * Process events.
	 */
	public void actionPerformed(ActionEvent evt) {

		// Spectrum file
		if (evt.getSource() == jbfile) {
			JFileChooser jfcfile = new JFileChooser(workd); // default directory
			jfcfile.setSize(400, 300);
			jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only
			Container parent = jbfile.getParent();
			int choice = jfcfile.showDialog(parent, "Select"); // Dialog, Select
			if (choice == JFileChooser.APPROVE_OPTION) {
				File f = jfcfile.getSelectedFile();
				nfile = f.getAbsolutePath();
				dataf = new DataFile(nfile); // full exp file name;
				dataf.setType("dataread");
				dataf.read();
			} else {
				nfile = "";
			}
			jlnfile.setText(nfile);
			return;
		}
		// cancel
		if (evt.getSource() == jbclose) {
			dispose();
			return;
		}
		// find peaks
		if (evt.getSource() == jbfind) {
			if (checkValue()) {
				int pt = inverseButton.isSelected() ? -1 : 1;

				if (inverseButton.isSelected()) {
					double yInv = getMaxData(this.dataf.getY(), this.dataf.getNbxy())
							+ getMinData(this.dataf.getY(), this.dataf.getNbxy());
					threshold = (float) yInv - threshold;
				}

				ArrayList<Point2D> peakPositions = DigitalFilter.findPeaks(this.dataf.getX(), this.dataf.getY(), pt,
						threshold, filterWidth);
				if (peakPositions == null) {
					JOptionPane.showMessageDialog(null, "No peak detected!");
				} else {
					JOptionPane.showMessageDialog(null, "Number of detected peaks: " + peakPositions.size());
					if (peakPositions.size() > 0) {
						if (savePeakList(peakPositions)) {
							dispose();
						}
					}
				}
			}
		}

	}

}
