package divconq.tool;

import java.io.File;
import java.util.Scanner;

import divconq.lang.Clock;
import divconq.util.IOUtil;
import divconq.util.ISettingsObfuscator;
import divconq.util.StringUtil;
import divconq.xml.XAttribute;
import divconq.xml.XElement;
import divconq.xml.XmlReader;
import divconq.xml.XmlWriter;

/**
 * Utility to construct a Project from a local (developer) repository.  Allows the user
 * to pick the packages to use and to setup the database connection.  Should only be run 
 * by developer using a local repository, will not work otherwise.
 * 
 * A Project is required both by developers running in developer mode and by deployed 
 * production Hubs.  Projects hold configuration, server keys and other project specific
 * resources.
 * 
 * @author Andy
 *
 */
public class Configure {
	/*
	 * Install Developer
	 * 
	 * hub/
	 * 		bin/
	 * 			run.bat						[configure, sync, testdb or run which takes developers class name as 2nd argument]
	 * 			run.sh
	 * 
	 * 		lib/
	 * 			third party*.jar
	 * 			divconq.core.jar
	 * 
	 * 			licenses/
	 * 				*.txt
	 * 
	 * 		packages/
	 * 			dcCore/
	 * 				m/
	 * 					dcConn.m
	 * 					dcShema.m
	 * 					dcInstall.m
	 * 					dcUninstall.m
	 * 					dcStrUtil.m
	 * 					dcTimeUtil.m
	 * 				all/
	 * 					dictionary/
	 * 						dictionary.xml
	 * 					schema/
	 * 						schema.xml
	 * 					lib/
	 * 						divconq.core.jar
	 * 
	 * 			dcTest/
	 * 				m/
	 * 					dcTest.m
	 * 					dcToyTest.m
	 * 				all/
	 * 					dictionary/
	 * 						dictionary.xml
	 * 					schema/
	 * 						schema.xml
	 * 
	 * 		LICENSE.txt
	 * 		NOTICE.txt
	 * 		README.txt
	 * 
	 * 
	 * ---------------------------------------------------
	 * 
	 * Configure adds:
	 * 
	 * 		project/
	 * 			packages.xml				[if not present, prompt for packages to use]
	 * 
	 * 			private/
	 * 				config.xml				[if not present, create and init Id and Feed - prompt for database connection details]
	 * 
	 * 				resource/				[if using a ssh client key]
	 * 					[dbclientkey].pem
	 * 
	 * 			public/
	 * 				config.xml
	 * 
	 * 	TODO, copy libs, upload routines, start install - create repo
	 * 
	 * TODO local/remote repo - where to get files
	 * 
	 * TODO configure for deployment - not just devmode
	 * 
	 */
	
	/**
	 * Utility must be run from the application base directory (parent to "bin")
	 * 
	 * @param args ignored
	 */
	public static void main(String[] args) {
		File pkgs = new File("./packages");
		
		if (!pkgs.exists()) {
			System.out.println("This deployment does not have any local repository, unable to configure.");
			return;
		}
		
		Scanner scan = new Scanner(System.in);
		boolean running = true;
		
		while(running) {
			try {
				System.out.println();
				System.out.println("-----------------------------------------------");
				System.out.println("   DivConq Configure Menu");
				System.out.println("-----------------------------------------------");
				System.out.println("0)  Exit");
				System.out.println("1)  Install Project");
				System.out.println("2)  Configure Database");
				System.out.println("3)  Encrypt Setting");
				System.out.println("4)  Hash Value");

				String opt = scan.nextLine();
				
				Long mopt = StringUtil.parseInt(opt);
				
				if (mopt == null)
					continue;
				
				switch (mopt.intValue()) {
				case 0:
					running = false;
					break;
					
				case 1:
					Configure.install(scan);					
					break;
					
				case 2:
					Configure.db(scan);					
					break;
					
				case 3:
					Configure.encrypt(scan);					
					break;
					
				case 4:
					Configure.hash(scan);					
					break;
				}
						
			}
			catch(Exception x) {
				System.out.println("CLI error: " + x);
			}
		}		
		
	}

	/**
	 * Assemble the packages and config file.
	 * 
	 * @param scan user input scanner
	 */
	private static void install(Scanner scan) {
		File config = new File("./project/private/config.xml");
		
		if (config.exists()) {
			System.out.println("Installing will wipe out previous project, are you sure you want to continue?");
			System.out.print("[yes/no]: ");
			
			if (!"yes".equals(scan.nextLine().toLowerCase()))
				return;
		}
		
		File proj = new File("./project");
		
		IOUtil.deleteRecursive(proj);
		
		proj.mkdirs();
		
		System.out.println();
		System.out.println("Adding packages, type 'yes' for packages you wish to include: ");
				
		File pkgs = new File("./packages");
		XElement xpkgs = new XElement("Packages");
		
		// TODO order the packages, not just in order found
		for (File pkg : pkgs.listFiles()) {
			if (pkg.isDirectory() || pkg.getName().endsWith(".zip")) {
				String name = pkg.getName();
				
				if (name.endsWith(".zip"))
					name = name.substring(0, name.length() - 4);
				
				System.out.println("Found: " + name);
				System.out.print("[yes/no]: ");
				
				if ("yes".equals(scan.nextLine().toLowerCase()))
					xpkgs.add(new XElement("Package", new XAttribute("Name", name)));
			}
		}
		
		XmlWriter.writeToFile(xpkgs, "./project/packages.xml");
			
		File priv = new File("./project/private");
		priv.mkdirs();
		
		File pub = new File("./project/public");
		pub.mkdirs();

		XElement conf = new XElement("Config");		
		XElement clock = new XElement("Clock");
		
		// TODO prompt for custom "TimerClass" - add that class name to clock
		
		Clock tclock = new Clock();
		tclock.init(clock);
		
		ISettingsObfuscator crypto1 = tclock.getObfuscator();
		
		crypto1.configure(clock);
		
		conf.add(clock);
		
		XmlWriter.writeToFile(conf, "./project/private/config.xml");
		XmlWriter.writeToFile(conf, "./project/public/config.xml");
		
		Configure.db(scan);
	}

	/**
	 * configure the database settings (only for private safety area)
	 * 
	 * @param scan user input scanner
	 */
	private static void db(Scanner scan) {
		File config = new File("./project/private/config.xml");
		
		if (!config.exists()) {
			System.out.println("Missing project config, please install first.");
			return;
		}
		
		XElement conf = XmlReader.loadFile(config);
		
		if (conf == null) {
			System.out.println("Project config is not well formed Xml, please re-install.");
			return;
		}
		
		XElement clock = conf.find("Clock");
		
		if ((clock == null) || !clock.hasAttribute("Id")) {
			System.out.println("Project config is invlaid, missing Clock, please re-install.");
			return;
		}		
		
		Clock tclock = new Clock();
		tclock.init(clock);
		
		ISettingsObfuscator crypto1 = tclock.getObfuscator();
		
		// remove old database settings - TODO support more than one connection and support editing instead of simple replace
		
		XElement xdb = conf.find("Database");

		if (xdb == null) {
			xdb = new XElement("Database");
			conf.add(xdb);
		}
		
		System.out.println();
		System.out.println("Enter database connection settings.");

		System.out.print("Host: ");
		String host = scan.nextLine();
		
		if (StringUtil.isEmpty(host))
			return;
		
		xdb.setAttribute("Host", host);
		
		System.out.print("Port [enter for 22]: ");
		String port = scan.nextLine();
		
		if (StringUtil.isNotEmpty(port)) 
			xdb.setAttribute("Port", port);
		
		System.out.print("User: ");
		String user = scan.nextLine();
		
		if (StringUtil.isEmpty(user))
			return;
		
		xdb.setAttribute("User", user);
		
		System.out.print("Name of ssh key file [leave blank if not using client ssh key]: ");
		String keyfilename = scan.nextLine();
		
		if (StringUtil.isNotEmpty(keyfilename)) {
			xdb.removeAttribute("Password");
			xdb.removeAttribute("Passphrase");
			
			if (!keyfilename.endsWith(".pem"))
				keyfilename = keyfilename + ".pem";
			
			System.out.println("Place the ssh key file in the correct project resource path.");
			System.out.println("./project/private/resource/" + keyfilename);
			
			while (true) {
				System.out.println("Press enter after placing the file and we'll confirm the file");
				scan.nextLine();
				
				File keyf = new File("./project/private/resource/" + keyfilename);
				
				if (keyf.exists())
					break;
			}

			System.out.println("Key file confirmed.");
			System.out.println();
			
			xdb.setAttribute("KeyFile", keyfilename);
			
			System.out.print("Client Key Passphrase [leave blank if none]: ");
			String passphrase = scan.nextLine();
			
			if (StringUtil.isNotEmpty(passphrase))
				xdb.setAttribute("Passphrase", crypto1.encryptStringToHex(passphrase));
		}
		else {
			xdb.removeAttribute("Passphrase");
			xdb.removeAttribute("KeyFile");
			
			System.out.print("Password: ");
			String password = scan.nextLine();
			
			if (StringUtil.isNotEmpty(password))
				xdb.setAttribute("Password", crypto1.encryptStringToHex(password));
		}
		
		XmlWriter.writeToFile(conf, "./project/private/config.xml");
	}	

	private static void encrypt(Scanner scan) {
		File config = new File("./project/private/config.xml");
		
		if (!config.exists()) {
			System.out.println("Missing project config, please install first.");
			return;
		}
		
		XElement conf = XmlReader.loadFile(config);
		
		if (conf == null) {
			System.out.println("Project config is not well formed Xml, please re-install.");
			return;
		}
		
		XElement clock = conf.find("Clock");
		
		if ((clock == null) || !clock.hasAttribute("Id")) {
			System.out.println("Project config is invlaid, missing Clock, please re-install.");
			return;
		}
		
		Clock tclock = new Clock();
		tclock.init(clock);
		
		ISettingsObfuscator crypto1 = tclock.getObfuscator();

		System.out.print("Value: ");
		String password = scan.nextLine();
		
		System.out.println("Result: " + crypto1.encryptStringToHex(password));
	}	

	private static void hash(Scanner scan) {
		File config = new File("./project/private/config.xml");
		
		if (!config.exists()) {
			System.out.println("Missing project config, please install first.");
			return;
		}
		
		XElement conf = XmlReader.loadFile(config);
		
		if (conf == null) {
			System.out.println("Project config is not well formed Xml, please re-install.");
			return;
		}
		
		XElement clock = conf.find("Clock");
		
		if ((clock == null) || !clock.hasAttribute("Id")) {
			System.out.println("Project config is invlaid, missing Clock, please re-install.");
			return;
		}
		
		Clock tclock = new Clock();
		tclock.init(clock);
		
		ISettingsObfuscator crypto1 = tclock.getObfuscator();

		System.out.print("Value: ");
		String password = scan.nextLine();
		
		System.out.println("Result: " + crypto1.hashStringToHex(password));
	}	
}
