package gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.ScrollPane;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.LinkedHashSet;

import javax.swing.BoxLayout;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.SwingWorker;
import javax.swing.border.TitledBorder;

import jebl.evolution.graphs.Node;
import jebl.evolution.io.ImportException;
import jebl.evolution.io.NexusImporter;
import jebl.evolution.trees.RootedTree;
import templates.DiscreteTreeToKML;
import templates.DiscreteTreeToProcessing;
import templates.MapBackground;
import utils.Utils;
import app.SpreadApp;
import checks.DiscreteSanityCheck;
import colorpicker.swing.ColorPicker;

@SuppressWarnings("serial")
public class DiscreteModelTab extends JPanel {

	//Shared Frame
	private SpreadApp frame;
	
	// Sizing constants
	private final int leftPanelWidth = 260;
	private final int leftPanelHeight = 1000;
	private final int spinningPanelHeight = 20;
	private final int mapImageWidth = MapBackground.MAP_IMAGE_WIDTH;
	private final int mapImageHeight = MapBackground.MAP_IMAGE_HEIGHT;
	private final Dimension minimumDimension = new Dimension(0, 0);

	// Colors
	private Color backgroundColor;
	private Color polygonsMaxColor;
	private Color branchesMaxColor;
	private Color polygonsMinColor;
	private Color branchesMinColor;

	// Locations & coordinates table
	private InteractiveTableModel table = null;

	// Strings for paths
	private String treeFilename = null;
	private File workingDirectory = null;

	// Text fields
	private JTextField numberOfIntervalsParser;
	private JTextField maxAltMappingParser;
	private JTextField kmlPathParser;
	private JTextField timescalerParser;

	// Spinners
	private DateSpinner dateSpinner;

	// Buttons
	private JButton generateKml;
	private JButton openTree;
	private JButton openLocationCoordinatesEditor;
	private JButton generateProcessing;
	private JButton saveProcessingPlot;
	private JButton polygonsMaxColorChooser;
	private JButton branchesMaxColorChooser;
	private JButton polygonsMinColorChooser;
	private JButton branchesMinColorChooser;

	// Sliders
	private JSlider branchesWidthParser;
	private JSlider polygonsRadiusMultiplierParser;

	// Combo boxes
	private JComboBox eraParser;
	private JComboBox stateAttributeNameParser;

	// left tools pane
	private JPanel leftPanel;
	private JPanel tmpPanel;
	private SpinningPanel sp;
	private JPanel tmpPanelsHolder;

	// Processing pane
	private DiscreteTreeToProcessing discreteTreeToProcessing;

	// Progress bar
	private JProgressBar progressBar;

	public DiscreteModelTab(SpreadApp spreadApp) {

		this.frame = spreadApp;
		
		// Setup miscallenous
		setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
		backgroundColor = new Color(231, 237, 246);
		polygonsMinColor = new Color(0, 0, 0, 100);
		polygonsMaxColor = new Color(50, 255, 255, 255);
		branchesMinColor = new Color(0, 0, 0, 255);
		branchesMaxColor = new Color(255, 5, 50, 255);
		GridBagConstraints c = new GridBagConstraints();

		// Setup text fields
		numberOfIntervalsParser = new JTextField("100", 10);
		maxAltMappingParser = new JTextField("5000000", 10);
		kmlPathParser = new JTextField("output.kml", 10);
		timescalerParser = new JTextField("1", 10);

		// Setup buttons for tab
		generateKml = new JButton("Generate", SpreadApp.nuclearIcon);
		openTree = new JButton("Open", SpreadApp.treeIcon);
		openLocationCoordinatesEditor = new JButton("Setup", SpreadApp.locationsIcon);
		generateProcessing = new JButton("Plot", SpreadApp.processingIcon);
		saveProcessingPlot = new JButton("Save", SpreadApp.saveIcon);
		polygonsMaxColorChooser = new JButton("Setup max");
		branchesMaxColorChooser = new JButton("Setup max");
		polygonsMinColorChooser = new JButton("Setup min");
		branchesMinColorChooser = new JButton("Setup min");

		// Setup sliders
		branchesWidthParser = new JSlider(JSlider.HORIZONTAL, 2, 10, 4);
		branchesWidthParser.setMajorTickSpacing(2);
		branchesWidthParser.setMinorTickSpacing(1);
		branchesWidthParser.setPaintTicks(true);
		branchesWidthParser.setPaintLabels(true);
		polygonsRadiusMultiplierParser = new JSlider(JSlider.HORIZONTAL, 1, 11,
				1);
		polygonsRadiusMultiplierParser.setMajorTickSpacing(2);
		polygonsRadiusMultiplierParser.setMinorTickSpacing(1);
		polygonsRadiusMultiplierParser.setPaintTicks(true);
		polygonsRadiusMultiplierParser.setPaintLabels(true);

		// Setup Combo boxes
		stateAttributeNameParser = new JComboBox(new String[] {"      "});
		stateAttributeNameParser.setName("      ");
		
		// Setup progress bar
		progressBar = new JProgressBar();

		// Left tools pane
		leftPanel = new JPanel();
		leftPanel.setBackground(backgroundColor);
		leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS));

		openTree.addActionListener(new ListenOpenTree());
		generateKml.addActionListener(new ListenGenerateKml());
		openLocationCoordinatesEditor
				.addActionListener(new ListenOpenLocationCoordinatesEditor());
		generateProcessing.addActionListener(new ListenGenerateProcessing());
		saveProcessingPlot.addActionListener(new ListenSaveProcessingPlot());
		polygonsMaxColorChooser
				.addActionListener(new ListenPolygonsMaxColorChooser());
		branchesMaxColorChooser
				.addActionListener(new ListenBranchesMaxColorChooser());
		polygonsMinColorChooser
				.addActionListener(new ListenPolygonsMinColorChooser());
		branchesMinColorChooser
				.addActionListener(new ListenBranchesMinColorChooser());

		// /////////////
		// ---INPUT---//
		// /////////////

		tmpPanelsHolder = new JPanel();
		tmpPanelsHolder.setLayout(new BoxLayout(tmpPanelsHolder,
				BoxLayout.Y_AXIS));

		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("Load tree file:"));
		tmpPanel.add(openTree);
		tmpPanelsHolder.add(tmpPanel);

		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("State attribute name:"));
		tmpPanel.add(stateAttributeNameParser);
		tmpPanelsHolder.add(tmpPanel);
		
		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("Setup location coordinates:"));
		tmpPanel.add(openLocationCoordinatesEditor);
		tmpPanelsHolder.add(tmpPanel);

		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setLayout(new GridBagLayout());
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("Most recent sampling date:"));
		dateSpinner = new DateSpinner();
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 0;
		tmpPanel.add(dateSpinner, c);
		String era[] = { "AD", "BC" };
		eraParser = new JComboBox(era);
		c.gridx = 2;
		c.gridy = 0;
		tmpPanel.add(eraParser, c);
		tmpPanelsHolder.add(tmpPanel);

		sp = new SpinningPanel(tmpPanelsHolder, "   Input", new Dimension(
				leftPanelWidth, spinningPanelHeight));
		sp.showBottom(true);
		leftPanel.add(sp);

		// ////////////////////////
		// ---BRANCHES MAPPING---//
		// ////////////////////////

		tmpPanelsHolder = new JPanel();
		tmpPanelsHolder.setLayout(new BoxLayout(tmpPanelsHolder,
				BoxLayout.Y_AXIS));

		// Branches color mapping:
		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setLayout(new GridBagLayout());
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("Branches color mapping:"));
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 0;
		tmpPanel.add(branchesMinColorChooser, c);
		c.gridx = 2;
		c.gridy = 0;
		tmpPanel.add(branchesMaxColorChooser, c);
		tmpPanelsHolder.add(tmpPanel);

		// Branches width:
		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("Branches width:"));
		tmpPanel.add(branchesWidthParser);
		tmpPanelsHolder.add(tmpPanel);

		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("Maximal altitude:"));
		tmpPanel.add(maxAltMappingParser);
		tmpPanelsHolder.add(tmpPanel);

		sp = new SpinningPanel(tmpPanelsHolder, "   Branches mapping",
				new Dimension(leftPanelWidth, spinningPanelHeight));
		sp.showBottom(false);
		leftPanel.add(sp);

		// ////////////////////////
		// ---POLYGONS MAPPING---//
		// ////////////////////////

		tmpPanelsHolder = new JPanel();
		tmpPanelsHolder.setLayout(new BoxLayout(tmpPanelsHolder,
				BoxLayout.Y_AXIS));

		// Circles color mapping:
		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setLayout(new GridBagLayout());
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("Circles color mapping:"));
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 0;
		tmpPanel.add(polygonsMinColorChooser, c);
		c.gridx = 2;
		c.gridy = 0;
		tmpPanel.add(polygonsMaxColorChooser, c);
		tmpPanelsHolder.add(tmpPanel);

		// Circles radius multiplier:
		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("Circles radius multiplier:"));
		tmpPanel.add(polygonsRadiusMultiplierParser);
		tmpPanelsHolder.add(tmpPanel);

		sp = new SpinningPanel(tmpPanelsHolder, "   Circles mapping",
				new Dimension(leftPanelWidth, spinningPanelHeight));
		sp.showBottom(false);
		leftPanel.add(sp);

		// ////////////////////
		// ---COMPUTATIONS---//
		// ////////////////////

		tmpPanelsHolder = new JPanel();
		tmpPanelsHolder.setLayout(new BoxLayout(tmpPanelsHolder,
				BoxLayout.Y_AXIS));

		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("Number of intervals:"));
		tmpPanel.add(numberOfIntervalsParser);
		tmpPanelsHolder.add(tmpPanel);

		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("Time scale multiplier:"));
		tmpPanel.add(timescalerParser);
		tmpPanelsHolder.add(tmpPanel);

		sp = new SpinningPanel(tmpPanelsHolder, "   Computations",
				new Dimension(leftPanelWidth, spinningPanelHeight));
		sp.showBottom(false);
		leftPanel.add(sp);

		// //////////////
		// ---OUTPUT---//
		// //////////////

		tmpPanelsHolder = new JPanel();
		tmpPanelsHolder.setLayout(new BoxLayout(tmpPanelsHolder,
				BoxLayout.Y_AXIS));

		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("KML name:"));
		tmpPanel.add(kmlPathParser);
		tmpPanelsHolder.add(tmpPanel);

		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setLayout(new GridBagLayout());
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("Generate KML / Plot map:"));
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 0;
		tmpPanel.add(generateKml, c);
		c.gridx = 2;
		c.gridy = 0;
		tmpPanel.add(generateProcessing, c);
		c.ipady = 7;
		c.gridwidth = 3;
		c.gridx = 0;
		c.gridy = 1;
		tmpPanel.add(progressBar, c);
		tmpPanelsHolder.add(tmpPanel);

		tmpPanel = new JPanel();
		tmpPanel.setMaximumSize(new Dimension(leftPanelWidth, 100));
		tmpPanel.setBackground(backgroundColor);
		tmpPanel.setBorder(new TitledBorder("Save plot:"));
		tmpPanel.add(saveProcessingPlot);
		tmpPanelsHolder.add(tmpPanel);

		sp = new SpinningPanel(tmpPanelsHolder, "   Output", new Dimension(
				leftPanelWidth, spinningPanelHeight));
		sp.showBottom(false);
		leftPanel.add(sp);

		// ////////////////////////
		// ---LEFT SCROLL PANE---//
		// ////////////////////////

		JScrollPane leftScrollPane = new JScrollPane(leftPanel,
				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		leftScrollPane.setMinimumSize(minimumDimension);
		leftScrollPane.setMaximumSize(new Dimension(leftPanelWidth, leftPanelHeight));

		/**
		 * Processing pane
		 * */
		discreteTreeToProcessing = new DiscreteTreeToProcessing();
		discreteTreeToProcessing.setPreferredSize(new Dimension(mapImageWidth,
				mapImageHeight));

//		if (System.getProperty("java.runtime.name").toLowerCase().startsWith(
//				"openjdk")) {
//
//			JScrollPane rightScrollPane = new JScrollPane(
//					discreteTreeToProcessing,
//					JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
//					JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
//			rightScrollPane.setMinimumSize(minimumDimension);
//
//			SplitPane splitPane = new SplitPane(JSplitPane.HORIZONTAL_SPLIT,
//					leftScrollPane, rightScrollPane);
//			splitPane.setDividerLocation(leftPanelWidth);
//
//			this.add(splitPane);
//
//		} else {

			ScrollPane rightScrollPane = new ScrollPane(
					ScrollPane.SCROLLBARS_ALWAYS);
			rightScrollPane.add(discreteTreeToProcessing);
			rightScrollPane.setMinimumSize(minimumDimension);

			SplitPane splitPane = new SplitPane(JSplitPane.HORIZONTAL_SPLIT,
					leftScrollPane, rightScrollPane);
			splitPane.setDividerLocation(leftPanelWidth);

			this.add(splitPane);

//		}

	} // END: discreteModelTab

	private void populateAttributeComboboxes() {
		
		try {
			
			NexusImporter importer = new NexusImporter(new FileReader(
					treeFilename));
			RootedTree tree = (RootedTree) importer.importNextTree();

			LinkedHashSet<String> uniqueAttributes = new LinkedHashSet<String>();

			for (Node node : tree.getNodes()) {
				if (!tree.isRoot(node)) {

					uniqueAttributes.addAll(node.getAttributeNames());

				}// END: root check
			}// END: nodeloop

			// re-initialise comboboxes
			ComboBoxModel stateNameParserModel = new DefaultComboBoxModel(uniqueAttributes.toArray(new String[0]));
			stateAttributeNameParser.setModel(stateNameParserModel);
			
		} catch (FileNotFoundException e) {
			Utils.handleException(e, e.getMessage());
		} catch (IOException e) {
			Utils.handleException(e, e.getMessage());
		} catch (ImportException e) {
			Utils.handleException(e, e.getMessage());
		}// END: try-catch block

	}// END: populateAttributeComboboxes
	
	private class ListenOpenTree implements ActionListener {
		public void actionPerformed(ActionEvent ev) {

			try {

				String[] treeFiles = new String[] { "tre", "tree", "trees" };

				final JFileChooser chooser = new JFileChooser();
				chooser.setDialogTitle("Loading tree file...");
				chooser.setMultiSelectionEnabled(false);
				chooser.addChoosableFileFilter(new SimpleFileFilter(treeFiles,
						"Tree files (*.tree(s), *.tre)"));
				chooser.setCurrentDirectory(workingDirectory);

				int returnVal = chooser.showOpenDialog(Utils.getActiveFrame());
				if (returnVal == JFileChooser.APPROVE_OPTION) {

					File file = chooser.getSelectedFile();
					treeFilename = file.getAbsolutePath();
					
					frame.setStatus("Opened " + treeFilename + "\n");

					File tmpDir = chooser.getCurrentDirectory();

					if (tmpDir != null) {
						workingDirectory = tmpDir;
					}

					populateAttributeComboboxes();

				} else {
					frame.setStatus("Could not Open! \n");
				}

			} catch (Exception e) {
				Utils.handleException(e, e.getMessage());
			}// END: try-catch block

		}// END: actionPerformed
	}// END: ListenOpenTree

	private class ListenOpenLocationCoordinatesEditor implements ActionListener {
		public void actionPerformed(ActionEvent ev) {

			try {

				LocationCoordinatesEditor locationCoordinatesEditor = new LocationCoordinatesEditor(frame);
				locationCoordinatesEditor.launch(treeFilename,
						stateAttributeNameParser.getSelectedItem().toString(), workingDirectory);

				table = locationCoordinatesEditor.getTable();

			} catch (NullPointerException e) {

				Utils.handleException(e,
						"Have you imported the proper tree file?");

			} catch (RuntimeException e) {

				Utils.handleException(
						e,
						"Have you specified proper state attribute name?"
								+ "\nHave you set the posterior probability limit in treeAnnotator to zero?");

			}// END: try-catch block

		}// END: actionPerformed
	}// END: ListenOpenLocations

	private class ListenPolygonsMinColorChooser implements ActionListener {
		public void actionPerformed(ActionEvent ev) {

			Color c = ColorPicker.showDialog(Utils.getActiveFrame(),
					"Choose minimum polygons color...", polygonsMinColor, true);

			if (c != null)
				polygonsMinColor = c;

		}
	}

	private class ListenPolygonsMaxColorChooser implements ActionListener {
		public void actionPerformed(ActionEvent ev) {

			Color c = ColorPicker.showDialog(Utils.getActiveFrame(),
					"Choose maximum polygons color...", polygonsMaxColor, true);

			if (c != null)
				polygonsMaxColor = c;

		}
	}

	private class ListenBranchesMinColorChooser implements ActionListener {
		public void actionPerformed(ActionEvent ev) {

			Color c = ColorPicker.showDialog(Utils.getActiveFrame(),
					"Choose minimum branches color...", branchesMinColor, true);

			if (c != null)
				branchesMinColor = c;

		}
	}

	private class ListenBranchesMaxColorChooser implements ActionListener {
		public void actionPerformed(ActionEvent ev) {

			Color c = ColorPicker.showDialog(Utils.getActiveFrame(),
					"Choose maximum branches color...", branchesMaxColor, true);

			if (c != null)
				branchesMaxColor = c;

		}
	}

	private class ListenGenerateKml implements ActionListener {
		public void actionPerformed(ActionEvent ev) {

			if (treeFilename == null) {

				new ListenOpenTree().actionPerformed(ev);

			} else if (table == null) {

				new ListenOpenLocationCoordinatesEditor().actionPerformed(ev);

			} else {

				generateKml.setEnabled(false);
				progressBar.setIndeterminate(true);

				SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {

					// Executed in background thread
					public Void doInBackground() {

						try {

							if (new DiscreteSanityCheck().check(treeFilename,
									stateAttributeNameParser.getSelectedItem().toString(), table)) {

								DiscreteTreeToKML discreteTreeToKML = new DiscreteTreeToKML();

								discreteTreeToKML.setTable(table);

								discreteTreeToKML
										.setStateAttName(stateAttributeNameParser.getSelectedItem().toString());

								discreteTreeToKML
										.setMaxAltitudeMapping(Double
												.valueOf(maxAltMappingParser
														.getText()));

								discreteTreeToKML
										.setMrsdString(dateSpinner.getValue()
												+ " "
												+ (eraParser.getSelectedIndex() == 0 ? "AD"
														: "BC"));

								discreteTreeToKML.setTimescaler(Integer
										.valueOf(timescalerParser.getText()));

								discreteTreeToKML.setNumberOfIntervals(Integer
										.valueOf(numberOfIntervalsParser
												.getText()));

								discreteTreeToKML
										.setKmlWriterPath(workingDirectory
												.toString()
												.concat("/")
												.concat(kmlPathParser.getText()));

								discreteTreeToKML.setTreePath(treeFilename);

								discreteTreeToKML
										.setMinPolygonRedMapping(polygonsMinColor
												.getRed());

								discreteTreeToKML
										.setMinPolygonGreenMapping(polygonsMinColor
												.getGreen());

								discreteTreeToKML
										.setMinPolygonBlueMapping(polygonsMinColor
												.getBlue());

								discreteTreeToKML
										.setMinPolygonOpacityMapping(polygonsMinColor
												.getAlpha());

								discreteTreeToKML
										.setMaxPolygonRedMapping(polygonsMaxColor
												.getRed());

								discreteTreeToKML
										.setMaxPolygonGreenMapping(polygonsMaxColor
												.getGreen());

								discreteTreeToKML
										.setMaxPolygonBlueMapping(polygonsMaxColor
												.getBlue());

								discreteTreeToKML
										.setMaxPolygonOpacityMapping(polygonsMaxColor
												.getAlpha());

								discreteTreeToKML
										.setPolygonsRadiusMultiplier(polygonsRadiusMultiplierParser
												.getValue());

								discreteTreeToKML
										.setMinBranchRedMapping(branchesMinColor
												.getRed());

								discreteTreeToKML
										.setMinBranchGreenMapping(branchesMinColor
												.getGreen());

								discreteTreeToKML
										.setMinBranchBlueMapping(branchesMinColor
												.getBlue());

								discreteTreeToKML
										.setMinBranchOpacityMapping(branchesMinColor
												.getAlpha());

								discreteTreeToKML
										.setMaxBranchRedMapping(branchesMaxColor
												.getRed());

								discreteTreeToKML
										.setMaxBranchGreenMapping(branchesMaxColor
												.getGreen());

								discreteTreeToKML
										.setMaxBranchBlueMapping(branchesMaxColor
												.getBlue());

								discreteTreeToKML
										.setMaxBranchOpacityMapping(branchesMaxColor
												.getAlpha());

								discreteTreeToKML
										.setBranchWidth(branchesWidthParser
												.getValue());

								discreteTreeToKML.GenerateKML();

								System.out.println("Finished in: "
										+ discreteTreeToKML.time + " msec \n");

							}// END: check

						} catch (final Exception e) {
							Utils.handleException(e, null);
						}

						return null;
					}// END: doInBackground()

					// Executed in event dispatch thread
					public void done() {
						generateKml.setEnabled(true);
						progressBar.setIndeterminate(false);

						frame.setStatus("Generated "
								+ workingDirectory.toString().concat("/")
										.concat(kmlPathParser.getText()));
					}
				};

				worker.execute();

			}// END: if not loaded

		}// END: actionPerformed
	}// END: ListenGenerateKml

	private class ListenGenerateProcessing implements ActionListener {
		public void actionPerformed(ActionEvent ev) {

			if (treeFilename == null) {

				new ListenOpenTree().actionPerformed(ev);

			} else if (table == null) {

				new ListenOpenLocationCoordinatesEditor().actionPerformed(ev);

			} else {

				generateProcessing.setEnabled(false);
				progressBar.setIndeterminate(true);

				SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {

					// Executed in background thread
					public Void doInBackground() {

						// TODO This work should be activated after each option
						// changes (automatic responsive plotting); will make
						// for a much slicker program

						try {

							if (new DiscreteSanityCheck().check(treeFilename,
									stateAttributeNameParser.getSelectedItem().toString(), table)) {

								// TODO Should only be done with state data
								// changes, not with each draw
								discreteTreeToProcessing
										.setStateAttName(stateAttributeNameParser.getSelectedItem().toString());

								// TODO Should only be done when changed,
								// not with each draw
								discreteTreeToProcessing.setTable(table);

								// TODO Should only be done when changed,
								// not with each draw
								discreteTreeToProcessing
										.setTreePath(treeFilename);

								discreteTreeToProcessing
										.setNumberOfIntervals(Integer
												.valueOf(numberOfIntervalsParser
														.getText()));

								discreteTreeToProcessing
										.setMinPolygonRedMapping(polygonsMinColor
												.getRed());

								discreteTreeToProcessing
										.setMinPolygonGreenMapping(polygonsMinColor
												.getGreen());

								discreteTreeToProcessing
										.setMinPolygonBlueMapping(polygonsMinColor
												.getBlue());

								discreteTreeToProcessing
										.setMinPolygonOpacityMapping(polygonsMinColor
												.getAlpha());

								discreteTreeToProcessing
										.setMaxPolygonRedMapping(polygonsMaxColor
												.getRed());

								discreteTreeToProcessing
										.setMaxPolygonGreenMapping(polygonsMaxColor
												.getGreen());

								discreteTreeToProcessing
										.setMaxPolygonBlueMapping(polygonsMaxColor
												.getBlue());

								discreteTreeToProcessing
										.setMaxPolygonOpacityMapping(polygonsMaxColor
												.getAlpha());

								discreteTreeToProcessing
										.setPolygonsRadiusMultiplier(polygonsRadiusMultiplierParser
												.getValue());

								discreteTreeToProcessing
										.setMinBranchRedMapping(branchesMinColor
												.getRed());

								discreteTreeToProcessing
										.setMinBranchGreenMapping(branchesMinColor
												.getGreen());

								discreteTreeToProcessing
										.setMinBranchBlueMapping(branchesMinColor
												.getBlue());

								discreteTreeToProcessing
										.setMinBranchOpacityMapping(branchesMinColor
												.getAlpha());

								discreteTreeToProcessing
										.setMaxBranchRedMapping(branchesMaxColor
												.getRed());

								discreteTreeToProcessing
										.setMaxBranchGreenMapping(branchesMaxColor
												.getGreen());

								discreteTreeToProcessing
										.setMaxBranchBlueMapping(branchesMaxColor
												.getBlue());

								discreteTreeToProcessing
										.setMaxBranchOpacityMapping(branchesMaxColor
												.getAlpha());

								discreteTreeToProcessing
										.setBranchWidth(branchesWidthParser
												.getValue() / 2);

								discreteTreeToProcessing.init();

							}// END: check

						} catch (final Exception e) {

							Utils.handleException(e, null);

						}

						return null;
					}// END: doInBackground

					// Executed in event dispatch thread
					public void done() {

						generateProcessing.setEnabled(true);
						progressBar.setIndeterminate(false);
						frame.setStatus("Finished. \n");
						
					}
				};

				worker.execute();

			}// END: if not loaded

		}// END: actionPerformed
	}// END: ListenGenerateProcessing

	private class ListenSaveProcessingPlot implements ActionListener {
		public void actionPerformed(ActionEvent ev) {

			try {

				JFileChooser chooser = new JFileChooser();
				chooser.setDialogTitle("Saving as png file...");

				int returnVal = chooser.showSaveDialog(Utils.getActiveFrame());
				if (returnVal == JFileChooser.APPROVE_OPTION) {
					
					File file = chooser.getSelectedFile();
					String filename = file.getAbsolutePath();

					discreteTreeToProcessing.save(filename);
					
					frame.setStatus("Saved " + filename + "\n");

				} else {
					frame.setStatus("Could not Save! \n");
				}

			} catch (Exception e) {
				Utils.handleException(e, e.getMessage());
			}// END: try-catch block

		}// END: actionPerformed
	}// END: ListenSaveProcessingPlot

}// END: class
