package org.etsi.common.configuration;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;

import org.etsi.common.MiscTools;
import org.etsi.common.exceptions.TerminationException;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.ConversionException;

public class ConfigTools {
	
	private ToolConfiguration config = null;
	private String configurationClassName;
	private String configurationProfileClassName;
	private String toolVersion;
	
	public ConfigTools(String configurationClassName, String configurationProfileClassName) {
		this.configurationClassName = configurationClassName;
		this.configurationProfileClassName = configurationProfileClassName;
	}
	
	
	public void loadConfig(String configurationFilename) throws TerminationException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		System.out.println("Loading configuration ...");
		File configFile = new File(configurationFilename);
		if (configFile.exists()) {
			config = readConfig(configurationFilename);
			if (config == null) {
				System.out.println("ERROR: XML configuration could not be loaded! "
						+ "\n  Please check the configuration for consistency or use an auto-generated configuration file to migrate any custom settings!");
				throw new TerminationException("");
			}
		} else {
			System.out.println("ERROR: Configuration file \""+configurationFilename+"\" does not exist! Use the appropriate option to generate a new default configuration.");
			throw new TerminationException("");
//			config = initializeNewDefaultConfig(configurationFilename);
		}
	}

	public ToolConfiguration initializeNewDefaultConfig(
			String configurationFilename) throws TerminationException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		System.out.println("  Generating new default configuration in: \""
				+ configurationFilename + "\" ...");
		ToolConfiguration config = null;
		ConfigurationProfile configurationProfile = null;
		config = (ToolConfiguration) Class.forName(configurationClassName).newInstance();
		configurationProfile = (ConfigurationProfile) Class.forName(configurationProfileClassName).newInstance();
		configurationProfile.setProfileName("defaultProfile");
		configurationProfile.setProfileVersion(getToolVersion());
		config.setDefaultConfigurationProfile("all");
		if (config.getConfigurationProfiles() == null) {
			ArrayList<ConfigurationProfile> configurationProfiles = new ArrayList<ConfigurationProfile>();
			config.setConfigurationProfiles(configurationProfiles);
		}
		config.getConfigurationProfiles().add(configurationProfile);
		writeConfig(config, configurationFilename);
		return config;
	}	

	// -------------------------------------------------------------------------
	
/*	//TO BE DEPRECATED
	//TODO: Reorganize and reuse 
	public static String findConfig(String filename) {
		File configPath = new File(getConfigurationPath());
		if (configPath.exists()) {
			File configFile = null;
			if (filename.length() > 0) {
				configFile = new File(getConfigurationPath() + filename);
			} else {
				configFile = new File(getDefaultConfigurationFilePath());
			}
			if (configFile.exists())
				return configFile.getAbsolutePath();
			else
				return null;
		} else {
			configPath.mkdir();
			return null;
		}
	}
	//TODO: TO BE DEPRECATED
	public static String findConfig() {
		return findConfig("");
	}
	
	
	//TODO: TO BE DEPRECATED
	//TODO: FIX THESE HARDCODED PARTS
	public static String getConfigurationPath() {
		if (System.getProperty("os.name").toLowerCase().contains("windows")) {
			String appdata = System.getenv("appdata");
			return appdata + "\\T3Q\\";
		} else { // assume unix
			String home = System.getProperty("user.home");
			return home + "/.t3q/";
		}
	}

	
	//TODO: TO BE DEPRECATED
	public static String getDefaultConfigurationFilePath() {
		return getConfigurationPath() + "t3q.xml";
	}
*/	
	// -------------------------------------------------------------------------
	
	private ToolConfiguration readConfig(String filename) throws TerminationException {
		System.out.println("  Reading XML configuration from: " + filename);
		String str = MiscTools.readFile(filename);
		XStream xstream = new XStream();
		initXstreamAliases(xstream);
		ToolConfiguration config = null;
		try{
			config = (ToolConfiguration)xstream.fromXML(str);
		} catch (ConversionException ce){
			String line = "";
			line = ce.getMessage().split("line\\snumber[\\t\\s]+?:\\s")[1];
			line = line.substring(0, line.indexOf("\n"));
			System.out.println("  Problem Occured while loading configuration: line: "+line);
			//TODO: add further debugging details?
		}
		return config;
	}

	private void initXstreamAliases(XStream xstream) throws TerminationException {
		try {
			xstream.alias(Class.forName(configurationClassName).getSimpleName(), Class.forName(configurationClassName));
			xstream.alias(Class.forName(configurationProfileClassName).getSimpleName(), Class.forName(configurationProfileClassName));
		} catch (ClassNotFoundException e) {
			System.out.println("  Problem occurred while initiating XStream. Class not found: "+e.getMessage());
			throw new TerminationException("Class not found: "+e.getMessage());
		}
	}

	private void writeConfig(ToolConfiguration config, String filename) throws TerminationException {
		System.out.println("  Writing XML configuration to: "+filename);
		XStream xstream = new XStream();
		initXstreamAliases(xstream);
		String xml = xstream.toXML(config);
		OutputStreamWriter oos;
		//write out an empty file
		MiscTools.writeFile(filename, "");
		try {
			oos = new OutputStreamWriter (new FileOutputStream(filename));
			oos.write(xml);
			oos.close();
		} catch (FileNotFoundException e) {
			System.out.println("  ERROR: File could not be found! (\""+filename+"\")");
			throw new TerminationException(e.getMessage());
		} catch (IOException e) {
			System.out.println("  IO ERROR: Could not write to file! (\""+filename+"\")");
			throw new TerminationException(e.getMessage());
		}
	}

	public ConfigurationProfile selectProfile(String specifiedProfileName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		// add all default implicit profile
		ConfigurationProfile selectedProfile = null;
		ConfigurationProfile allEnabledProfile = (ConfigurationProfile) Class.forName(configurationProfileClassName).newInstance();
		allEnabledProfile.setProfileName("all");
		allEnabledProfile.setProfileVersion(getToolVersion());
		config.getConfigurationProfiles().add(allEnabledProfile);
		if (specifiedProfileName != null){
			System.out.println("  Selecting profile \"" + specifiedProfileName
					+ "\"...");
			selectedProfile = activateProfile(specifiedProfileName);
		}
		if (selectedProfile == null) {
			if (specifiedProfileName != null) {
				System.out.println("  Profile \""
						+ specifiedProfileName
						+ "\" not found in configuration. Trying default profile \""
						+ config.getDefaultConfigurationProfile() + "\".");
			} else {
				System.out.println("  No Profile specified. Trying default profile \""
						+ config.getDefaultConfigurationProfile() + "\".");
			}

			specifiedProfileName = config.getDefaultConfigurationProfile();
			selectedProfile = activateProfile(specifiedProfileName);

		}
		if (selectedProfile == null) {
			System.out.println("  Profile \""
					+ specifiedProfileName
					+ "\" not found in configuration. Auto-selecting \"all\" profile.");
			selectedProfile = allEnabledProfile;
		}
		
		return selectedProfile;
	}

	// --------------------------------------------------------------------------
	//TODO: Extract configuration management and possibly abstract it for reusability
	private ConfigurationProfile activateProfile(String specifiedProfile) {
		ConfigurationProfile activatedProfile = null;
		
		for (int i = 0; i < config.getConfigurationProfiles().size(); i++) {
			ConfigurationProfile configurationProfile = (ConfigurationProfile) config
					.getConfigurationProfiles().get(i);
			if (configurationProfile.getProfileName().toLowerCase().equals(
					specifiedProfile.toLowerCase())) {
				System.out.println("  Activating profile \"" + specifiedProfile
						+ "\"...");
				activatedProfile = configurationProfile;
			}
		}
		return activatedProfile;
	}


	public void setToolVersion(String toolVersion) {
		this.toolVersion = toolVersion;
	}


	public String getToolVersion() {
		return toolVersion;
	}

}
