package de.ugoe.cs.swe.memos;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Properties;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class SettingsManager {
	private File settingsFile;
	private Properties settings = new Properties();
	public boolean settingsFileAlreadyExisted = true;

	// Top Secret
	private byte[] rawKey = new byte[] { 37, -120, 123, -105, 4, 91, -107, -41,
			15, -107, 79, 60, 43, 78, -66, 28 };

	private static SettingsManager instance;

	public static SettingsManager getInstance() {
		if (instance == null)
			instance = new SettingsManager();
		return instance;
	}

	private SettingsManager() {
		String SettingsPfad = System.getProperty("user.home")
				+ System.getProperty("file.separator") + ".snm";
		settingsFile = new File(SettingsPfad);
		loadSettingsFromFile();
	}

	private String byteArrayToHex(byte input[]) {
		String result = "";

		for (int i = 0; i < input.length; i++) {
			if (((int) input[i] & 0xff) < 0x10)
				result += "0";
			result += Long.toString((int) input[i] & 0xff, 16);
		}
		return result;
	}

	private byte[] hexStringToByteArray(String s) {
		int len = s.length();
		byte[] data = new byte[len / 2];
		for (int i = 0; i < len; i += 2) {
			data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character
					.digit(s.charAt(i + 1), 16));
		}
		return data;
	}

	// TODO: Improve

	private String encryptPassword(String password) {
		try {
			SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
			Cipher cipher = Cipher.getInstance("AES");
			cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
			byte[] encrypted = cipher.doFinal(password.getBytes());
			return new String(byteArrayToHex(encrypted));
		} catch (Exception E) {
			Utils
					.showError("Verschlüsseln des Datenbankpassworts fehlgeschlagen");
			return "Encrypt failed";
		}
	}

	private String decryptPassword(String encrypted) {
		try {
			byte[] input = hexStringToByteArray(encrypted);
			SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
			Cipher cipher = Cipher.getInstance("AES");
			cipher.init(Cipher.DECRYPT_MODE, skeySpec);
			byte[] original = cipher.doFinal(input);
			String originalString = new String(original);
			return originalString;
		} catch (Exception E) {
			Utils
					.showError("Entschlüsseln des Datenbankpassworts fehlgeschlagen");
			return "Decrypt failed";
		}
	}

	public void setUsername(String newvalue) {
		settings.setProperty("username", newvalue);
	}

	public void setPassword(String newvalue) {
		settings.setProperty("password", encryptPassword(newvalue));
	}

	public void setImport_Path(String newvalue) {
		settings.setProperty("import_path", newvalue);
	}

	public void setExport_Path(String newvalue) {
		settings.setProperty("export_path", newvalue);
	}

	public void setDatabaseURI(String newvalue) {
		settings.setProperty("database_uri", newvalue);
	}

	public String getUsername() {
		return settings.getProperty("username");
	}

	public String getPassword() {
		return decryptPassword(settings.getProperty("password"));
	}

	public String getImport_Path() {
		return settings.getProperty("import_path");
	}

	public String getExport_Path() {
		return settings.getProperty("export_path");
	}

	public String getDatabaseURI() {
		return settings.getProperty("database_uri");
	}

	public void loadSettingsFromFile() {
		checkAndCreateFile();
		FileInputStream fs;
		try {
			fs = new FileInputStream(settingsFile);
			settings.loadFromXML(fs);
		} catch (Exception e) {
			Utils.showError("Fehler beim Laden der Einstellungen.");
		}
	}

	public void saveSettingsToFile() {
		OutputStream fs;
		try {
			fs = new FileOutputStream(settingsFile);
			settings.storeToXML(fs, null);
		} catch (Exception e) {
			Utils.showError("Fehler beim Speichern der Einstellungen.");
		}
	}

	private void checkAndCreateFile() {
		if (!settingsFile.exists()) {
			settingsFileAlreadyExisted = false;
			// Set to xpprakt-defaults
			settings.setProperty("username", "xprakt2");
			settings.setProperty("password", encryptPassword("huttraeger"));
			settings
					.setProperty("import_path", System.getProperty("user.home"));
			settings
					.setProperty("export_path", System.getProperty("user.home"));
			settings.setProperty("database_uri",
					"www.trex.informatik.uni-goettingen.de/xprakt2");
			settings.setProperty("timeout", "30");
			settings.setProperty("dbtype", "mysql");
			settings.setProperty("hsqlfilename", "snm.db");
			settings.setProperty("sslenabled", "false");
			saveSettingsToFile();
		}
	}

	public void setLockTimeout(Long newvalue) {
		settings.setProperty("timeout", newvalue.toString());
	}

	public DatabaseType getDBType() {
		return settings.getProperty("dbtype").equals("mysql") ? DatabaseType.MySQL
				: DatabaseType.HSQL;
	}

	public void setDBType(DatabaseType dbType) {
		settings.setProperty("dbtype", dbType == DatabaseType.MySQL ? "mysql"
				: "hsql");
	}

	public Long getLockTimeout() {
		return new Long(settings.getProperty("timeout"));
	}

	public enum DatabaseType {
		MySQL, HSQL;
	}

	public boolean getSSLEnabled() {
		return Boolean
				.parseBoolean(settings.getProperty("sslenabled", "false"));
	}

	public void setSSLEnabled(boolean useSSL) {
		settings.setProperty("sslenabled", String.valueOf(useSSL));
	}

	public String getHSQLFilename() {
		// Filename is relative to some startup path...
		return settings.getProperty("hsqlfilename", "snm.db");
	}

	public void setHSQLFilename(String filename) {
		settings.setProperty("hsqlfilename", filename);
	}

	public String getSSLKeyName() {
		return settings.getProperty("keyfilename", System
				.getProperty("user.home")
				+ "/truststore.key");
	}

	public void setSSLKeyName(String filename) {
		settings.setProperty("keyfilename", filename);
	}
}
