package eu.dnetlib.installer;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 * This class implements a driver installer.
 * @author thanos
 *
 */
public class Installer {
	private static final String GLOBAL_CONFIG_XPATH = "/deployment-descriptor/configuration/prop";
	private static final String CONFIG_KEY_XPATH = "@key";
	private static final String CONFIG_VALUE_XPATH = "@value";
	private static final String WEB_APP_XPATH = "/deployment-descriptor/webapp";
	private static final String WEB_APP_NAME_XPATH = "@name";
	private static final String WEB_APP_CONFIG_XPATH = "configuration/prop";
	private static final String DEPENDENCIES_FILE = "dependencies.properties";
	private static final String WEB_SERVICE_XPATH = "service";
	private static final String WEB_SERVICE_NAME_XPATH = "@name";
	private static final String DIST_DIR = "DIST_DIR";
	private static final File GLOBAL_CONFIG_FILE = new File(System.getProperty("java.io.tmpdir") + File.separator + "global.properties");
	private static final int OK = 0;
	private static final int INVOCATION_ERROR = -1;
	private static final int EXECUTION_ERROR = -2;

	private Properties globalConfiguration;
	private List<WebApplication> webApplications;
	private Properties dependencies;
	
	/**
	 * Create a new installer.
	 * @param services the property file containing services base dirs
	 * @param deploymentFile the deployment file to parse
	 * @throws FileNotFoundException if dependencies of a service are missing from cache
	 * @throws IOException if any I/O errors occur
	 * @throws XPathExpressionException if any XPath parsing errors occur while parsing deployment file
	 */
	public Installer(File services, File deploymentFile) throws FileNotFoundException, IOException, XPathExpressionException {
		// parse global configuration
		NodeList globalConfigNodes = (NodeList) XPathFactory.newInstance().newXPath().evaluate(GLOBAL_CONFIG_XPATH, new InputSource(
				deploymentFile.toString()), XPathConstants.NODESET);
		globalConfiguration = new Properties();
		for (int i = 0; i < globalConfigNodes.getLength(); i++) {
			Node globalConfigNode = globalConfigNodes.item(i);
			globalConfiguration.put(
					(String) XPathFactory.newInstance().newXPath().evaluate(CONFIG_KEY_XPATH, globalConfigNode, XPathConstants.STRING),
					(String) XPathFactory.newInstance().newXPath().evaluate(CONFIG_VALUE_XPATH, globalConfigNode, XPathConstants.STRING));
		}
		// parse web applications
		NodeList webAppNodes = (NodeList) XPathFactory.newInstance().newXPath().evaluate(WEB_APP_XPATH, new InputSource(deploymentFile.toString()),
				XPathConstants.NODESET);
		webApplications = new ArrayList<WebApplication>();
		for (int i = 0; i < webAppNodes.getLength(); i ++) {
			Node webAppNode = webAppNodes.item(i);
			String name = (String) XPathFactory.newInstance().newXPath().evaluate(WEB_APP_NAME_XPATH, webAppNode, XPathConstants.STRING);
			// parse web app configuration
			NodeList webAppConfigNodes = (NodeList) XPathFactory.newInstance().newXPath().evaluate(WEB_APP_CONFIG_XPATH, webAppNode,
					XPathConstants.NODESET);
			Properties webAppConfiguration = new Properties();
			for (int j = 0; j < webAppConfigNodes.getLength(); j++) {
				Node webAppConfigNode = webAppConfigNodes.item(j);
				webAppConfiguration.put(
						(String) XPathFactory.newInstance().newXPath().evaluate(CONFIG_KEY_XPATH, webAppConfigNode, XPathConstants.STRING),
						(String) XPathFactory.newInstance().newXPath().evaluate(CONFIG_VALUE_XPATH, webAppConfigNode, XPathConstants.STRING));
			}
			// load services
			Properties servicesBaseDirs = new Properties();
			servicesBaseDirs.load(new FileInputStream(services));
			// parse web services
			Set<File> jars = new HashSet<File>();
			NodeList webServiceNodes = (NodeList) XPathFactory.newInstance().newXPath().evaluate(WEB_SERVICE_XPATH, webAppNode,
					XPathConstants.NODESET);
			for (int j = 0; j < webServiceNodes.getLength(); j ++) {
				String serviceName = (String) XPathFactory.newInstance().newXPath().evaluate(WEB_SERVICE_NAME_XPATH, webServiceNodes.item(j), XPathConstants.STRING);
				for (File jar : new WebService(new File(servicesBaseDirs.getProperty(serviceName)), new File(servicesBaseDirs.getProperty(DIST_DIR))).resolveDependencies())
					jars.add(jar);
			}
			webApplications.add(new WebApplication(name, webAppConfiguration, jars.toArray(new File [0])));
		}
	}
	
	/**
	 * Build all web the applications (WARs) and the global configuration file.
	 * @return an array with the files built
	 * @throws IOException if any I/O errors occur
	 */
	public File [] buildAll() throws IOException {
		File [] result = new File [webApplications.size() + 1];
		// build web applications
		for (int i = 0; i < webApplications.size(); i++)
			result[i] = webApplications.get(i).build();
		// create global configuration file
		GLOBAL_CONFIG_FILE.createNewFile();
		globalConfiguration.store(new FileOutputStream(GLOBAL_CONFIG_FILE), null);
		result[webApplications.size()] = GLOBAL_CONFIG_FILE;
		return result;
	}
	
	/**
	 * Create an Installer instance and build everything.
	 * @param args the web service locations file and the deployment file to use
	 */
	public static void main(String [] args) {
		if (args.length < 2) {
			System.err.println("usage: java -jar installer.jar <services file> <deployment file>");
			System.exit(INVOCATION_ERROR);
		}
		try {
			File [] result= new Installer(new File(args[0]), new File(args[1])).buildAll();
			for (File file : result)
				System.out.println(file);
			System.exit(OK);
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(EXECUTION_ERROR);
		}
	}

}
