package eu.dnetlib.installer;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;


/**
 * This class implements a driver web application.
 * @author thanos
 *
 */
public class WebApplication {
	private static final String TMP_DIR = System.getProperty("java.io.tmpdir");
	private static final String ZIP_EXTENSION = ".zip";
	private static final String WEB_INF_DIR = "WEB-INF";
	private static final String LIB_DIR = "lib";
	private static final String TEMPLATES_DIR = "templates";
	private static final String INDEX_FILE = "index.html";
	private static final String WEB_FILE = "web.xml";
	private static final String APPLICATION_CONTEXT_FILE = "applicationContext.xml";
	private static final String LOG4J_FILE = "log4j.properties";
	private static final String CXF_FILE = "cxf.xml";
	private static final String NAME_PLACEHOLDER = "$name$";
	private static final String PROPERTIES_EXTENSION = ".properties";
	private static final String WAR_EXTENSION = ".war";
	private static final int BUFFER_SIZE = 1024;
	
	private String name;
	private Properties configuration;
	private File [] dependencies;
	private File buildDir;
	
	/**
	 * Create a new WebApplication.
	 * @param name the name of the web application
	 * @param configuration the configuration of the web application
	 * @param dependencies the JARs needed
	 */
	public WebApplication(String name, Properties configuration, File [] dependencies) {
		this.name = name;
		this.configuration = configuration;
		this.dependencies = dependencies;
		buildDir = new File(TMP_DIR + File.separator + name);
	}
	
	public File build() throws IOException {
		// create standard dir hierarchy
		File webInfDir = new File(buildDir + File.separator + WEB_INF_DIR); 
		File libDir = new File(webInfDir + File.separator + LIB_DIR);
		libDir.mkdirs();
		createFileFromTemplate(new File(buildDir + File.separator + INDEX_FILE));
		createFileFromTemplate(new File(webInfDir + File.separator + WEB_FILE));
		createFileFromTemplate(new File(webInfDir + File.separator + APPLICATION_CONTEXT_FILE));
		createFileFromTemplate(new File(webInfDir + File.separator + LOG4J_FILE));
		createFileFromTemplate(new File(webInfDir + File.separator + CXF_FILE));
		// create configuration properties file
		File configFile = new File(webInfDir + File.separator + name + PROPERTIES_EXTENSION);
		configuration.store(new FileOutputStream(configFile), null);
		// add dependencies (JARs)
		for (File jar : dependencies)
			Utils.copyFile(jar, new File(libDir + File.separator + jar.getName()));
		// bundle build directory into WAR
		return buildWAR();
	}
	
	/**
	 * Create a static file using a template of the same name.
	 * @param file the file to create
	 * @throws IOException if any I/O errors occur	
	 */
	private void createFileFromTemplate(File file) throws IOException {
		
		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream(TEMPLATES_DIR + File.separator +
				file.getName())));
		BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file));
		try {
			String line;
			while ((line = bufferedReader.readLine()) != null)
				bufferedWriter.write(line.replace(NAME_PLACEHOLDER, name) + "\n");
		} finally {
			bufferedReader.close();
			bufferedWriter.close();
		}
	}
	
	/**
	 * Build a WAR.
	 * @return the WAR file built
	 * @throws IOException if any I/O errors occur
	 */
	private File buildWAR() throws IOException {
		File war = new File(TMP_DIR + File.separator + name + WAR_EXTENSION);
		JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(war), new Manifest());
		byte [] buffer = new byte [BUFFER_SIZE];
		try {
			for (File file : Utils.getAllContents(buildDir)) {
				if (file.isFile()) {
					jarOutputStream.putNextEntry(new JarEntry(file.toString().substring(buildDir.toString().length())));
					FileInputStream fileInputStream = new FileInputStream(file);
					int i = 0;
					try {
						while ((i = fileInputStream.read(buffer)) != -1)
							jarOutputStream.write(buffer, 0, i);
					} finally {
						fileInputStream.close();
					}
				}
			}
		} finally {
			jarOutputStream.close();
		}
		WebApplication.delete(buildDir);
		return war;
	}
	
	/**
	 * Delete a file or directory recursively.
	 * @param file the file or directory to delete
	 */
	private static  void delete(File file) {
		if (file.isDirectory()) {
			for (String fileName : file.list())
				WebApplication.delete(new File(file + File.separator + fileName));
		}
		file.delete();
	}
}
