package controller;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;

import model.Instructions;
import model.Settings;
import model.Settings.Temp;
import model.Settings.Tilt;
import model.Specifications;
import ui.Main;

/**
 * The Export-Class that creates the STLs, instructions and the Android-file.
 * @author Martin Herbers
 *
 */
public class Export {
	
	private static Settings settings = Settings.getSettings();
	//Depending on the OS, a path with spaces needs different symbols around it. E.g. Windows: "C:\program files\" or MacOS: '/Applications/'
	private static String pathComma;

	
	public static void createSTL() {	
		
		//Set the console's chaining/parallel starting syntax and the "PathComma" depending on the OS
		String start = "";
		if (System.getProperty("os.name").contains("Windows")) {
			start = " && start ";
			pathComma = "\"";
		}
		else if (System.getProperty("os.name").contains("Linux")) {
			start = " & ";
			pathComma = "\"";
		}
		else if (System.getProperty("os.name").toLowerCase().contains("mac")) {
			start = " && open -a ";
			pathComma = "'";
		}
		
		//Part the command in 5 parts that get additions depending on which object got chosen
		//comFolder changes the directory to the .exe on Windows or the binary file in MacOs. Not needed in Linux
		String comFolder = "cd " + pathComma + settings.getOpenSCADPath() + pathComma;
		//Line for the full non-conductive object
		String comLine =  start + "openscad "+ (System.getProperty("os.name").toLowerCase().contains("mac")?"-W -n --args ":"") +"-o " + pathComma + Main.olipPath + "output.stl"+ pathComma + " ";
		//Line for all conductive parts
		String comLine2 = start + "openscad "+ (System.getProperty("os.name").toLowerCase().contains("mac")?"-W -n --args ":"") +"-o " + pathComma + Main.olipPath + "output_conductive.stl" + pathComma + " ";
		//Line for the non-conductive part for cross-section view
		String comLine3 = start + "openscad "+ (System.getProperty("os.name").toLowerCase().contains("mac")?"-W -n --args ":"") +"-o " + pathComma + Main.olipPath + "output_crossSection.stl" + pathComma + " ";
		//Line for the conducive parts for cross-section view
		String comLine4 = start + "openscad "+ (System.getProperty("os.name").toLowerCase().contains("mac")?"-W -n --args ":"") +"-o " + pathComma + Main.olipPath + "output_crossSection_conductive.stl" + pathComma + " ";
		//Instructions that will be added to the .txt file
		List<String> instructions = null;
		//Specification of the selected object, used for the android app to show the correct shape
		List<String> android = null;
		//Copy the OpenSCAD-file temporarily from the .jar, otherwise it can't be used by OpenSCAD.
		File tempFile = null;
		
		//If weight is selected...
		if (settings.isWeight()) {
			tempFile = new File(Main.olipPath + "weight.scad");
			//Copy the .scad file
			try {
				InputStream in = Export.class.getResourceAsStream("/OpenSCADFiles/weight.scad");
				OutputStream out = new FileOutputStream(tempFile);
				int read;
				byte[] bytes = new byte[1024];

				while ((read = in.read(bytes)) != -1) {
					out.write(bytes, 0, read);
				}
				in.close();
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			//Add the necessary variables to the command line arguments
			int walls = Math.round(settings.getWeight()/27);
			comLine += "-D numberSupports=" + walls + " -D conductive=false -D crossSection=false -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
			comLine2+= "-D numberSupports=" + walls + " -D conductive=true -D crossSection=false -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
			comLine3+= "-D numberSupports=" + walls + " -D conductive=false -D crossSection=true -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
			comLine4+= "-D numberSupports=" + walls + " -D conductive=true -D crossSection=true -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
			instructions = Instructions.getWeightInstruction();
			android = Specifications.getWeightSpecs();
		}
		
		else if (settings.isAcceleration()) {
				tempFile = new File(Main.olipPath + "acceleration.scad");
				try {
					InputStream in = Export.class.getResourceAsStream("/OpenSCADFiles/acceleration.scad");
					OutputStream out = new FileOutputStream(tempFile);
					int read;
					byte[] bytes = new byte[1024];

					while ((read = in.read(bytes)) != -1) {
						out.write(bytes, 0, read);
					}
					in.close();
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				
			double height = (settings.getAcceleration()<8?10:18);//TODO function from acceleration to wall height? linear? exponential? something else?
			comLine += "-D wallHeight=" + height + " -D conductive=false -D crossSection=false -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
			comLine2+= "-D wallHeight=" + height + " -D conductive=true -D crossSection=false -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
			comLine3+= "-D wallHeight=" + height + " -D conductive=false -D crossSection=true -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
			comLine4+= "-D wallHeight=" + height + " -D conductive=true -D crossSection=true -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
			instructions = Instructions.getAccelerationInstruction();
			android = Specifications.getAccelerationSpecs();
		}
		
		else if (settings.isSqueeze()) {
				tempFile = new File(Main.olipPath + "squeeze.scad");
				try {
					InputStream in = Export.class.getResourceAsStream("/OpenSCADFiles/squeeze.scad");
					OutputStream out = new FileOutputStream(tempFile);
					int read;
					byte[] bytes = new byte[1024];

					while ((read = in.read(bytes)) != -1) {
						out.write(bytes, 0, read);
					}
					in.close();
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				
			comLine += "-D conductive=false -D crossSection=false -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
			comLine2+= "-D conductive=true -D crossSection=false -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
			comLine3+= "-D conductive=false -D crossSection=true -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
			comLine4+= "-D conductive=true -D crossSection=true -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
			instructions = Instructions.getSqueezeInstruction();
			android = Specifications.getSqueezeSpecs();
		}
		
		else if (settings.isTemperature()) {
			if (settings.getTemperature() == Temp.OVER0) {
					tempFile = new File(Main.olipPath + "iceMelting.scad");
					try {
						InputStream in = Export.class.getResourceAsStream("/OpenSCADFiles/iceMelting.scad");
						OutputStream out = new FileOutputStream(tempFile);
						int read;
						byte[] bytes = new byte[1024];

						while ((read = in.read(bytes)) != -1) {
							out.write(bytes, 0, read);
						}
						in.close();
						out.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				
				comLine += "-D conductive=false -D crossSection=false -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				comLine2+= "-D conductive=true -D crossSection=false -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				comLine3+= "-D conductive=false -D crossSection=true -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				comLine4+= "-D conductive=true -D crossSection=true -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				instructions = Instructions.getTemperatureMeltInstruction();
				android = Specifications.getTemperatureMeltSpecs();
			}
			else {
					tempFile = new File(Main.olipPath + "iceFreezing.scad");
					try {
						InputStream in = Export.class.getResourceAsStream("/OpenSCADFiles/iceFreezing.scad");
						OutputStream out = new FileOutputStream(tempFile);
						int read;
						byte[] bytes = new byte[1024];

						while ((read = in.read(bytes)) != -1) {
							out.write(bytes, 0, read);
						}
						in.close();
						out.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
					
				comLine += "-D conductive=false -D crossSection=false -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				comLine2+= "-D conductive=true -D crossSection=false -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				comLine3+= "-D conductive=false -D crossSection=true -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				comLine4+= "-D conductive=true -D crossSection=true -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				instructions = Instructions.getTemperatureFreezeInstruction();
				android = Specifications.getTemperatureFreezeSpecs();
			}
		}
		
		else if (settings.isTilt()) {
			if (settings.getTilting().equals(Tilt.FLIP)) {
					tempFile = new File(Main.olipPath + "flip.scad");
					try {
						InputStream in = Export.class.getResourceAsStream("/OpenSCADFiles/flip.scad");
						OutputStream out = new FileOutputStream(tempFile);
						int read;
						byte[] bytes = new byte[1024];

						while ((read = in.read(bytes)) != -1) {
							out.write(bytes, 0, read);
						}
						in.close();
						out.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
					
				comLine += "-D conductive=false -D crossSection=false -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				comLine2+= "-D conductive=true -D crossSection=false -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				comLine3+= "-D conductive=false -D crossSection=true -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				comLine4+= "-D conductive=true -D crossSection=true -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				instructions = Instructions.getFlipInstruction();
				android = Specifications.getFlipSpecs();
			}
			
			else {
					tempFile = new File(Main.olipPath + "tilt.scad");
					try {
						InputStream in = Export.class.getResourceAsStream("/OpenSCADFiles/tilt.scad");
						OutputStream out = new FileOutputStream(tempFile);
						int read;
						byte[] bytes = new byte[1024];

						while ((read = in.read(bytes)) != -1) {
							out.write(bytes, 0, read);
						}
						in.close();
						out.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
					
				comLine += "-D conductive=false -D crossSection=false -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				comLine2+= "-D conductive=true -D crossSection=false -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				comLine3+= "-D conductive=false -D crossSection=true -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				comLine4+= "-D conductive=true -D crossSection=true -D fillLater="+ settings.getFill() +" -D sizeScreen=" + settings.getSizeScreen() + " -D sizeFinger=" + settings.getSizeFinger() + " " + pathComma + tempFile.toString() + pathComma;
				instructions = Instructions.getTiltInstruction();
				android = Specifications.getTiltSpecs();
			}
			
		}
		//ADD CODE HERE FOR ADDITIONAL INTERACTION
		//(else if()...)
		//Needs: 
				//Copying openscad-file to current folder
				//Commandline addition for each view-part
				//Instructions-text and object-specification
		
		if (instructions == null)
			return;

		String[] arguments = new String[3];
		
		//Depending on the os more arguments are needed to interpret the strings as commands
		if (System.getProperty("os.name").contains("Windows")) {
			arguments[0] = "cmd.exe";
			arguments[1] = "/c";
		}
		else if (System.getProperty("os.name").contains("Linux")) {
			arguments[0] = "bash";
			arguments[1] = "-c";
		}
		else {
			arguments[0] = "/bin/bash";
			arguments[1] = "-c";
		}
		
		arguments[2] = (System.getProperty("os.name").contains("Linux")?"":comFolder) + 
				(System.getProperty("os.name").contains("Linux")?comLine.substring(3):comLine) +
				comLine2 + comLine4 + comLine3;

		//Run the command line
		try {
			Process p = Runtime.getRuntime().exec(arguments);
			 BufferedReader stdOut=new BufferedReader(new InputStreamReader(p.getInputStream()));
			 @SuppressWarnings("unused")
		        String s;
		        while((s=stdOut.readLine())!=null){
		        	System.out.println(s);
		        }
			Main.finished();
		} catch (IOException e1) {
			e1.printStackTrace();
		}

		//Create the instructions .txt file
	    Path file = Paths.get(Main.olipPath + "instructions.txt");
	    try {
			Files.write(file, instructions, Charset.forName("UTF-8"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	    //Create the objectSpecs.txt file
	    Path output = Paths.get(Main.olipPath + "objectSpecs.txt");
	    try {
			Files.write(output, android, Charset.forName("UTF-8"));
		} catch (IOException e) {
			e.printStackTrace();
		}

	    //Delete all created files after closing the programm. If you want permanent files, either use the "save object" button or copy the files before closing the programm.
	    File f1 = new File(Main.olipPath + "output.stl");
		f1.deleteOnExit();
		File f2 = new File(Main.olipPath + "output_conductive.stl");
		f2.deleteOnExit();
		File f3 = new File(Main.olipPath + "output_crossSection.stl");
		f3.deleteOnExit();
		File f4 = new File(Main.olipPath + "output_crossSection_conductive.stl");
		f4.deleteOnExit();
		File f5 = new File(Main.olipPath + "instructions.txt");
		f5.deleteOnExit();
		File f6 = new File(Main.olipPath + "objectSpecs.txt");
		f6.deleteOnExit();
		
		//Delete the OpenSCAD file
		tempFile.delete();
	}
	
	/**
	 * Copy all files that are necessary for printing and using the object to a location of your choice.
	 * @param folder	directory where the files will be saved to
	 */
	public static void saveFilesTo(File folder) {
		try {
			File output = new File(Main.olipPath + "output.stl");
			Files.copy(output.toPath(), new File(folder.getAbsolutePath() + File.separator + "output.stl").toPath(), StandardCopyOption.REPLACE_EXISTING);
			
			File outputCond = new File(Main.olipPath + "output_conductive.stl");
			Files.copy(outputCond.toPath(), new File(folder.getAbsolutePath() + File.separator + "output_conductive.stl").toPath(), StandardCopyOption.REPLACE_EXISTING);
			
			File instructions = new File(Main.olipPath + "instructions.txt");
			Files.copy(instructions.toPath(), new File(folder.getAbsolutePath() + File.separator + "instructions.txt").toPath(), StandardCopyOption.REPLACE_EXISTING);
			
			File android = new File(Main.olipPath + "objectSpecs.txt");
			Files.copy(android.toPath(), new File(folder.getAbsolutePath() + File.separator + "objectSpecs.txt").toPath(), StandardCopyOption.REPLACE_EXISTING);
	
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
