package org.spview.filehandler;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import org.spview.gui.JobPlay;
import org.spview.Spview;

public class SPVProject {
	private String projectFileName;
	private final JobPlay jobplay;

	private boolean unsave = false;

	private ArrayList<Element> paths = new ArrayList<>(); // List of paths of the project to load
	private ArrayList<String> typeAttribute = new ArrayList<>();
	private ArrayList<String> AttributeOfFileToLoad = new ArrayList<>(); // Contains all attribute-types in order to
	private ArrayList<String> yReverseElementToLoad = new ArrayList<>(); // Contains information about "y-reverse" state
	private ArrayList<String> hideElementToLoad = new ArrayList<>();
	private ArrayList<String> xShiftedElementToLoad = new ArrayList<>();
	private ArrayList<String> yShiftedElementToLoad = new ArrayList<>();
	private int indexColorPredStr; // Contains the color got from the project file
	private int indexColorExpStr; // Contains the color got from the project file
	private double MinX, MaxX; // X Zoom values
	private double MinY, MaxY; // Y Zoom values

	public SPVProject(JobPlay jobplay) {
		this.jobplay = jobplay;
	}

	public SPVProject(JobPlay jobplay, String projectFileName)
			throws ParserConfigurationException, SAXException, IOException {
		this.projectFileName = projectFileName;
		this.jobplay = jobplay;

		FileParser cFileParser = new FileParser(this.projectFileName);

		paths = cFileParser.getPathlist();
		typeAttribute = cFileParser.getAttrlist();
		AttributeOfFileToLoad = cFileParser.getAttributeOfFilelist();
		yReverseElementToLoad = cFileParser.getyReverseElementslist();
		hideElementToLoad = cFileParser.getHideElementslist();
		xShiftedElementToLoad = cFileParser.getxShiftElementslist();
		yShiftedElementToLoad = cFileParser.getyShiftElementslist();

		indexColorPredStr = cFileParser.getIndexColorPred();
		indexColorExpStr = cFileParser.getIndexColorExp();

		MinX = cFileParser.getMinX();
		MaxX = cFileParser.getMaxX();

		MinY = cFileParser.getMinY();
		MaxY = cFileParser.getMaxY();
	}

	public void open() {
		int j = 0;
		try {
			ArrayList <Double> xshift = new ArrayList<>();
			ArrayList <Double> yshift = new ArrayList<>();
			for (Element pl : paths) {
				jobplay.loadFileByFilename(pl.getTextContent(), typeAttribute.get(j), j);
				if (typeAttribute.get(j).equals("spectrum") || typeAttribute.get(j).equals("tdsas")
						|| typeAttribute.get(j).equals("hitras")) {

					if (!yReverseElementToLoad.isEmpty() && yReverseElementToLoad.get(j).equals("true")) {
						jobplay.getPanAff().yReverse(j);
					}
					if (!hideElementToLoad.isEmpty() && hideElementToLoad.get(j).equals("true")) {
						jobplay.getPanAff().setShow(j, false);
						jobplay.enableHideMenu(j, false);
						jobplay.enableShowMenu(j, true);
					}

					if (MinX != 0.0 && MaxX != 0.0)
						jobplay.getPanAff().setXZoom(MinX, MaxX);

					if (MinY != 0.0 && MaxY != 0.0)
						jobplay.getPanAff().setYZoom(MinY, MaxY);

					xshift.add(Double.parseDouble(xShiftedElementToLoad.get(j)));
					yshift.add(Double.parseDouble(yShiftedElementToLoad.get(j)));

				}
				j++;
			}
			/* we need to load everything before shifting the curves */
			for (int i = 0; i < xshift.size(); i++) {
				if (xshift.get(i) != 0.0) {
					jobplay.getPanAff().setXShift(xshift.get(i), i);
					jobplay.enableXUnshiftMenu(i, true);
				}
				if (yshift.get(i) != 0.0) {
					jobplay.getPanAff().setYShift(yshift.get(i), i);
					jobplay.enableYUnshiftMenu(i, true);
				}
			}
			/* if success we add the project into the openRecentList */
			jobplay.addInOpenRecentList(projectFileName);
		} catch (Exception e) { // IO error
			jobplay.removeInOpenRecentList(projectFileName);
			JOptionPane.showMessageDialog(null, "Cannot Open File " + projectFileName, "Error",
					JOptionPane.ERROR_MESSAGE);
			e.printStackTrace(new java.io.PrintStream(System.out));
		}
	}

	private void saveVersion(Document doc, Element Project) {
		Element Version = doc.createElement("Version");
		Project.appendChild(Version);

		Project.appendChild(doc.createTextNode(Spview.VERSION));
	}

	private void saveZoomValues(Document doc, Element Project) {
		// Zoom
		Element Zoom = doc.createElement("Zoom");
		Project.appendChild(Zoom);
		// create xZoomMin node
		double xmin = jobplay.getPanAff().getbxmi();
		Element xZoomMin = doc.createElement("xmin");
		xZoomMin.appendChild(doc.createTextNode(Double.toString(xmin)));
		Zoom.appendChild(xZoomMin);

		// create xZoomMax node
		double xmax = jobplay.getPanAff().getbxma();
		Element xZoomMax = doc.createElement("xmax");
		xZoomMax.appendChild(doc.createTextNode(Double.toString(xmax)));
		Zoom.appendChild(xZoomMax);

		// create yZoomMin node
		double yZoom0 = jobplay.getPanAff().getbymi();
		Element yZoomMin = doc.createElement("ymin");
		yZoomMin.appendChild(doc.createTextNode(Double.toString(yZoom0)));
		Zoom.appendChild(yZoomMin);

		// create yZoomMax node
		double yZoom1 = jobplay.getPanAff().getbyma();
		Element yZoomMax = doc.createElement("ymax");
		yZoomMax.appendChild(doc.createTextNode(Double.toString(yZoom1)));
		Zoom.appendChild(yZoomMax);
	}

	private void saveDataFiles(Document doc, Element Project) {
		for (int i = 0; i < jobplay.getPathsList().size(); i++) {
			// file element
			Element File = doc.createElement("File");
			Project.appendChild(File);

			Element path = doc.createElement("path");
			path.appendChild(doc.createTextNode(jobplay.getPathsList().get(i)));
			File.appendChild(path);

			// create and append attribute to File
			Attr AttributeOfFile = doc.createAttribute("AttributeOfFile");
			AttributeOfFile.setValue(jobplay.getAttributeOfFileList().get(i));
			File.setAttributeNode(AttributeOfFile);

			// create and append attribute to path
			Attr Type = doc.createAttribute("Type");
			Type.setValue(jobplay.getTypeOfFileList().get(i));
			path.setAttributeNode(Type);

			// create yReverse node
			Element yReverse = doc.createElement("yReverse");
			yReverse.appendChild(doc.createTextNode(jobplay.getReverseYElementList().get(i)));
			File.appendChild(yReverse);

			// create hide node
			Element hide = doc.createElement("hide");
			hide.appendChild(doc.createTextNode(jobplay.getHideElementList().get(i)));
			File.appendChild(hide);

			// create xShift node
			double xState = jobplay.getPanAff().getXShift(i);
			Element xShiftState = doc.createElement("xShiftState");
			xShiftState.appendChild(doc.createTextNode(Double.toString(xState)));
			File.appendChild(xShiftState);

			// create yShift node
			double yState = jobplay.getPanAff().getYShift(i);
			Element yShiftState = doc.createElement("yShiftState");
			yShiftState.appendChild(doc.createTextNode(Double.toString(yState)));
			File.appendChild(yShiftState);
		}
	}

	private void savePredictionFile(Document doc, Element Project) {
		// file element
		Element File = doc.createElement("File");
		Project.appendChild(File);

		Element path = doc.createElement("path");
		path.appendChild(doc.createTextNode(jobplay.getPathOfPredFile()));
		File.appendChild(path);

		// create and append attribute to File
		Attr AttributeOfFile = doc.createAttribute("AttributeOfFile");
		AttributeOfFile.setValue(jobplay.getAttributeOfPredFile());
		File.setAttributeNode(AttributeOfFile);

		// create and append attribute to path
		Attr Type = doc.createAttribute("Type");
		Type.setValue(jobplay.getTypeOfPredFile());
		path.setAttributeNode(Type);

		// create color node
		String indexColorToSavePred = Integer.toString(jobplay.getPanAff().getPfass2());
		Element indexColorPred = doc.createElement("indexColorPred");
		indexColorPred.appendChild(doc.createTextNode(indexColorToSavePred));
		File.appendChild(indexColorPred);
	}

	private void saveExperimentFile(Document doc, Element Project) {
		// file element
		Element File = doc.createElement("File");
		Project.appendChild(File);

		Element path = doc.createElement("path");
		path.appendChild(doc.createTextNode(jobplay.getPathOfExpfile()));
		File.appendChild(path);

		// create and append attribute to File
		Attr AttributeOfFile = doc.createAttribute("AttributeOfFile");
		AttributeOfFile.setValue(jobplay.getAttributeOfExpFile());
		File.setAttributeNode(AttributeOfFile);

		// create and append attribute to path
		Attr Type = doc.createAttribute("Type");
		Type.setValue(jobplay.getTypeOfExpFile());
		path.setAttributeNode(Type);

		// create color node
		String indexColorToSaveExp = Integer.toString(jobplay.getPanAff().getEfass2());
		Element indexColorExp = doc.createElement("indexColorExp");
		indexColorExp.appendChild(doc.createTextNode(indexColorToSaveExp));
		File.appendChild(indexColorExp);
	}

	/**
	 * Save the current project to an XML file with .spv extension
	 * 
	 * @since SPVIEW2
	 */

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

		jfcfile.setSize(400, 300);
		jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only
		jfcfile.setDialogTitle("Save Project");
		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(jobplay.getParent()) == JFileChooser.APPROVE_OPTION) {
			selectedFile = jfcfile.getSelectedFile();
			if (!selectedFile.getAbsolutePath().endsWith(".spv")) {
				selectedFile = new File(jfcfile.getSelectedFile() + ".spv");
			}
			if (!selectedFile.exists() || (selectedFile.exists()
					&& JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(jobplay.getParent(),
							"File " + selectedFile.getAbsolutePath() + " already exists! Overwrite?", "Save",
							JOptionPane.YES_NO_OPTION))) {
				return selectedFile;
			}
		}
		return null;
	}

	public boolean saveSPVFile(String nameSuggestion, boolean fileExist) {
		boolean ret = false;
		if (jobplay.getNbOfDataFile() == 0) return false;
		File pickedFile;
		if (fileExist) 
			pickedFile = new File(nameSuggestion);
		else
			pickedFile = pickAFileName(nameSuggestion);
		if (pickedFile != null) {
			// Sort the attributes of each type of file, first Predfile, then Expfile
			for (int i = 0; i < AttributeOfFileToLoad.size(); i++) {
				// Sort the attributes "TDS" or "HITRAN"
				if (AttributeOfFileToLoad.get(i).equals("predread")) {
					jobplay.setAttributefPredFile(AttributeOfFileToLoad.get(i));
					jobplay.setTypeOfPredFile(typeAttribute.get(i));
					jobplay.setPathOfPredFile(jobplay.getPredFileName());
				}

				// Sort the attributes "peakExp"
				if (AttributeOfFileToLoad.get(i).equals("expread")) {
					jobplay.setAttributefExpFile(AttributeOfFileToLoad.get(i));
					jobplay.setTypeOfExpFile(typeAttribute.get(i));
					jobplay.setPathOfExpFile(jobplay.getExpFileName());
				}
			}

			if (jobplay.getNbOfDataFile() > 0 && jobplay.getTypeOfFileList().size() + jobplay.getPredfileToSave()
					+ jobplay.getExpfileToSave() > 0) {
				try {
					// --- instantiate a project file --- //
					DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
					DocumentBuilder docBuilder = dbFactory.newDocumentBuilder();
					Document doc = docBuilder.newDocument();

					// Project root element
					Element Project = doc.createElement("Project");
					doc.appendChild(Project);

					saveVersion(doc, Project);
					saveZoomValues(doc, Project);
					saveDataFiles(doc, Project);

					// The prediction and experimentation files have to be added to the project
					// AFTER the data files, otherwise there would be a shift in the colors.
					if (jobplay.getPredfileToSave() == 1) {
						savePredictionFile(doc, Project);
					}

					if (jobplay.getExpfileToSave() == 1) {
						saveExperimentFile(doc, Project);
					}

					// Write the content into xml file
					TransformerFactory transformerFactory = TransformerFactory.newInstance();
					Transformer transformer = transformerFactory.newTransformer();
					DOMSource source = new DOMSource(doc);

					StreamResult result = new StreamResult(pickedFile);
					transformer.transform(source, result);

				} catch (TransformerException | ParserConfigurationException tfe) {
					tfe.printStackTrace();
				}
				// handle exception creating TransformerFactory
				catch (TransformerFactoryConfigurationError factoryError) {
					System.err.println("Error creating " + "TransformerFactory");
					factoryError.printStackTrace();
				}

				this.setUnsave(false);
				System.out.println("Your project has been saved succesfully");
				ret = true;
			}
		}
		return ret;

	}

	public int getIndexColorPred() {
		return indexColorPredStr;
	}

	public int getIndexColorExp() {
		return indexColorExpStr;
	}

	public ArrayList<String> getAttributeOfFileList() {
		return AttributeOfFileToLoad;
	}

	public boolean isUnsave() {
		return unsave;
	}

	public void setUnsave(boolean unsave) {
		this.unsave = unsave;
	}
}
