/*
Copyright (C) 2011  Diego Darriba, David Posada

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package es.uvigo.darwin.jmodeltest.gui;

import java.awt.FileDialog;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
import javax.swing.border.LineBorder;
import javax.swing.plaf.BorderUIResource;
import javax.swing.plaf.BorderUIResource.TitledBorderUIResource;

import es.uvigo.darwin.jmodeltest.ModelTest;
import es.uvigo.darwin.jmodeltest.ModelTestConfiguration;
import es.uvigo.darwin.jmodeltest.utilities.Utilities;

public class FramePreferences extends JModelTestFrame {

	private static final int WINDOW_WIDTH = 500;
	private static final int OUTER_MARGIN = 10;
	private static final int MARGIN = 20;
	private static final int COMPONENTS_WIDTH = WINDOW_WIDTH - 2 * OUTER_MARGIN;
	private static final int COMPONENTS_HEIGHT = 20;
	private static final int MAX_INNER_WIDTH = COMPONENTS_WIDTH - 2 * MARGIN;
	private static final int BINARIES_PANEL_HEIGHT = MARGIN + 6
			* COMPONENTS_HEIGHT + OUTER_MARGIN;
	private static final int BUTTONS_RESET_WIDTH = 140;
	private static final int BUTTONS_WIDTH = 110;
	private static final int BUTTONS_HEIGHT = 40;
	private static final int BUTTONS_PADDING = 10;
	private static final int BUTTONS_PANEL_WIDTH = BUTTONS_RESET_WIDTH + 2 * BUTTONS_WIDTH + 2 * BUTTONS_PADDING;
	private static final int BUTTONS_VLOC = BINARIES_PANEL_HEIGHT;
	private static final int BUTTONS_HLOC = (COMPONENTS_WIDTH - BUTTONS_PANEL_WIDTH)/2;
	private static final int WINDOW_HEIGHT = 250;
	private static final long serialVersionUID = 1L;
	private static final String BACKUP_FILE = ModelTest.CONFIG_FILE + ".bk";

	private static final String BINARIES_COMMENT = "Set the phyml binary directory. The selected directory "
			+ "should exist and contain a valid phyml binary (\"phyml\" or \""+ Utilities.getBinaryVersion()+"\"). If Global "
			+ "Phyml checkbox is selected, \"phyml\" should exist in the path.";

	private static final String PROPERTIES_COMMENT = "This properties file was modified from "
			+ "jModelTest 2 GUI. A backup of the original configuration file has been "
			+ "stored in "
			+ BACKUP_FILE
			+ ". Replace this file with the backup for "
			+ "restoring the original format.";

	private JTextArea tfBinaryDescription = new JTextArea();
	private JCheckBox cbGlobalPhyml = new JCheckBox();
	private JLabel lbPathToPhyml = new JLabel();
	private JTextField tfPathToPhyml = new JTextField();
	private JPanel binarySettingsPanel = new JPanel();
	private JPanel buttonsPanel = new JPanel();
	private JPanel preferencesPanel = new JPanel();
	private JButton btnSetDefault = new JButton();
	private JButton btnAccept = new JButton();
	private JButton btnCancel = new JButton();
	private JButton btnOpen;

	public FramePreferences() {
		initComponents();
	}

	public void initComponents() {

		preferencesPanel.setLocation(0, 0);
		preferencesPanel.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
		preferencesPanel.setVisible(true);
		preferencesPanel.setLayout(null);

		binarySettingsPanel.setLocation(OUTER_MARGIN, OUTER_MARGIN);
		binarySettingsPanel.setSize(COMPONENTS_WIDTH, BINARIES_PANEL_HEIGHT);
		binarySettingsPanel
				.setBorder(new BorderUIResource.TitledBorderUIResource(
						new LineBorder(XManager.PANEL_BORDER_COLOR, 1, false),
						"PhyML Binaries Settings", TitledBorderUIResource.LEFT,
						TitledBorderUIResource.TOP, XManager.FONT_LABEL,
						XManager.LABEL_BLUE_COLOR));
		binarySettingsPanel.setVisible(true);
		binarySettingsPanel.setLayout(null);

		buttonsPanel.setLocation(BUTTONS_HLOC, BUTTONS_VLOC);
		buttonsPanel.setSize(COMPONENTS_WIDTH, BINARIES_PANEL_HEIGHT);
		buttonsPanel.setVisible(true);
		buttonsPanel.setLayout(null);

		tfBinaryDescription.setVisible(true);
		tfBinaryDescription.setEditable(false);
		tfBinaryDescription.setOpaque(false);
		tfBinaryDescription.setForeground(XManager.DARK_GRAY_COLOR);
		tfBinaryDescription.setSize(MAX_INNER_WIDTH, 3 * COMPONENTS_HEIGHT);
		tfBinaryDescription.setLocation(MARGIN, MARGIN);
		tfBinaryDescription.setText(BINARIES_COMMENT);
		tfBinaryDescription.setFont(XManager.FONT_LABEL);
		tfBinaryDescription.setLineWrap(true);
		tfBinaryDescription.setWrapStyleWord(true);

		cbGlobalPhyml.setVisible(true);
		cbGlobalPhyml.setSize(MAX_INNER_WIDTH, COMPONENTS_HEIGHT);
		cbGlobalPhyml.setText("Use global PhyML binary");
		cbGlobalPhyml.setLocation(MARGIN, 3 * COMPONENTS_HEIGHT + MARGIN);
		cbGlobalPhyml.setFont(XManager.FONT_LABEL);

		lbPathToPhyml.setVisible(true);
		lbPathToPhyml.setSize(120, COMPONENTS_HEIGHT);
		lbPathToPhyml.setText("PhyML binary path:");
		lbPathToPhyml.setLocation(2 * MARGIN, MARGIN + 4 * COMPONENTS_HEIGHT);
		lbPathToPhyml.setFont(XManager.FONT_LABEL);

		tfPathToPhyml.setVisible(true);
		tfPathToPhyml.setSize(280, COMPONENTS_HEIGHT);
		tfPathToPhyml.setLocation(2 * MARGIN + 120, MARGIN + 4 * COMPONENTS_HEIGHT);
		tfPathToPhyml.setFont(XManager.FONT_LABEL);
		
		btnOpen = XManager.makeIconButton("Open16", "Explore", "...");
		btnOpen.setLocation(2 * MARGIN + 400, MARGIN + 4 * COMPONENTS_HEIGHT);
		
		btnSetDefault.setText("Default Settings");
		btnSetDefault.setLocation(0, MARGIN);
		btnSetDefault.setSize(BUTTONS_RESET_WIDTH, BUTTONS_HEIGHT);
		btnSetDefault.setVisible(true);

		btnCancel.setText("Cancel");
		btnCancel.setLocation(BUTTONS_RESET_WIDTH+BUTTONS_PADDING, MARGIN);
		btnCancel.setSize(BUTTONS_WIDTH, BUTTONS_HEIGHT);
		btnCancel.setVisible(true);

		btnAccept.setText("Accept");
		btnAccept.setLocation(BUTTONS_RESET_WIDTH + BUTTONS_WIDTH+2*BUTTONS_PADDING, MARGIN);
		btnAccept.setSize(BUTTONS_WIDTH, BUTTONS_HEIGHT);
		btnAccept.setVisible(true);
		
		setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
		setResizable(false);
		binarySettingsPanel.add(tfBinaryDescription);
		binarySettingsPanel.add(tfPathToPhyml);
		binarySettingsPanel.add(btnOpen);
		binarySettingsPanel.add(cbGlobalPhyml);
		binarySettingsPanel.add(lbPathToPhyml);
		buttonsPanel.add(btnSetDefault);
		buttonsPanel.add(btnAccept);
		buttonsPanel.add(btnCancel);

		preferencesPanel.add(binarySettingsPanel);
		preferencesPanel.add(buttonsPanel);

		getContentPane().setLayout(null);
		getContentPane().add(preferencesPanel);
		setTitle("Preferences");
		setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
		setLocation(XManager.MAIN_LOCATION);
		getRootPane().setDefaultButton(btnAccept);
		setStatus();

		cbGlobalPhyml.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent e) {
				globalPhymlActionPerformed(e);
			}
		});

		btnSetDefault.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent e) {
				cbGlobalPhyml
						.setSelected(ModelTestConfiguration.DEFAULT_GLOBAL_PHYML);
				tfPathToPhyml.setText(ModelTestConfiguration.DEFAULT_EXE_DIR);
				tfPathToPhyml.setEnabled(!cbGlobalPhyml.isSelected());
			}
		});

		btnCancel.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent e) {
				cancelActionPerformed(e);
			}
		});

		btnAccept.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent e) {
				acceptActionPerformed(e);
			}
		});
		
		btnOpen.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent e) {
				exploreActionPerformed(e);
			}
		});

	}

	private void setStatus() {
		cbGlobalPhyml.setSelected(ModelTestConfiguration.isGlobalPhymlBinary());
		tfPathToPhyml.setText(ModelTestConfiguration.getExeDir());
		tfPathToPhyml.setEnabled(!cbGlobalPhyml.isSelected());
	}

	private void cancelActionPerformed(java.awt.event.ActionEvent e) {
		try {
			setStatus();
			setVisible(false);
			dispose();
		} catch (Exception f) {
			f.printStackTrace();
		}
	}

	private void acceptActionPerformed(java.awt.event.ActionEvent e) {
		try {
			// validate changes
			boolean validated = true;
			if (!cbGlobalPhyml.isSelected()) {
				File testBinDir = new File(tfPathToPhyml.getText());
				if (validated = (testBinDir.exists() && testBinDir
						.isDirectory())) {
					String path = tfPathToPhyml.getText();
					if (!path.endsWith(File.separator)) {
						path += File.separator;
					}
					File localExe = new File(path
							+ Utilities.getBinaryVersion());
					File genericExe = new File(path + "phyml");
					validated = (localExe.exists() && localExe.isFile() && localExe
							.canExecute())
							|| (genericExe.exists() && genericExe.isFile() && genericExe
									.canExecute());
				}
			}
			if (validated) {
				// update properties file
				Properties properties = ModelTestConfiguration.getProperties();
				properties.setProperty(ModelTestConfiguration.EXE_DIR,
						tfPathToPhyml.getText());
				properties.setProperty(ModelTestConfiguration.GLOBAL_PHYML_EXE,
						cbGlobalPhyml.isSelected() ? "true" : "false");
				writeProperties();

				setVisible(false);
				dispose();
			} else {
				JOptionPane
						.showMessageDialog(
								this,
								"There is no phyml binary in the selected location. \n"
										+ "Please make sure that exists an executable file named\n"
										+ "\"phyml\" or "
										+ Utilities.getBinaryVersion() + ".",
								"jModelTest error", JOptionPane.ERROR_MESSAGE);
				return;
			}
		} catch (Exception f) {
			f.printStackTrace();
		}
	}

	private void globalPhymlActionPerformed(java.awt.event.ActionEvent e) {
		try {
			tfPathToPhyml.setEnabled(!cbGlobalPhyml.isSelected());
		} catch (Exception f) {
			f.printStackTrace();
		}
	}
	
	private void exploreActionPerformed(java.awt.event.ActionEvent e) {
		FileDialog dialog;
		try {
			try {
				dialog = new FileDialog(this, "Select PhyML binaries directory",
						FileDialog.LOAD);
				dialog.setDirectory(ModelTestConfiguration.DEFAULT_EXE_DIR);
				dialog.setVisible(true);
			} catch (Throwable f) {
				f.printStackTrace();
				return;
			}

			tfPathToPhyml.setText(dialog.getDirectory());

		} catch (Exception f) {
			f.printStackTrace();
		}
	}

	private void writeProperties() {
		Properties properties = ModelTestConfiguration.getProperties();
		try {
			File bkFile = new File(BACKUP_FILE);
			if (!bkFile.exists()) {
				File propertiesFile = new File(ModelTest.CONFIG_FILE);
				InputStream in = new FileInputStream(propertiesFile);
				OutputStream out = new FileOutputStream(bkFile);
				byte[] buf = new byte[1024];
				int len;
				while ((len = in.read(buf)) > 0) {
					out.write(buf, 0, len);
				}
				in.close();
				out.close();
			}
			// Escribier en el archivo los cambios
			FileOutputStream fos = new FileOutputStream(
					ModelTest.CONFIG_FILE.replace("\\", "/"));

			properties.store(fos, PROPERTIES_COMMENT);

		} catch (FileNotFoundException e) {
			System.out.println(e.getMessage());
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}

}
