package org.bench4q.recorder.httpcapture;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;

public class Config {
	private static Config config = null;
	private static final Log log = LogFactory.getLog(Config.class);
	private static Properties props;
	private String pythonPath;
	private int port = 8090;
	private boolean debug;
	private boolean quiet;
	private String exist;
	private String replace;
	private List<String> includeList;
	private List<String> excludeList;
	private List<Pattern> includePatterns = new ArrayList<Pattern>();
	private List<Pattern> excludePatterns = new ArrayList<Pattern>();
	private String scriptArg;
	private ProxySettings proxySettings;
	private static String defDriverPkgName = "com.bitmechanic.maxq";
	private static String defValidatorPkgName = "com.bitmechanic.maxq";

	static {
		Layout layout = new PatternLayout("%d [%t] %-5p %c - %m%n");
		Logger.getRootLogger().addAppender(
				new ConsoleAppender(layout, "System.err"));
	}

	public static Config getConfig() {
		if (config == null)
			initConfig();
		return config;
	}

	public static void initConfig() {
		config = new Config();
		String maxqDir = System.getProperty("maxq.dir");
		String pathSep = System.getProperty("file.separator");
		// "\\src\\main\\resources\\org\\bench4q\\master\\config\\httpCaptureConfig\\maxq.properties";
		String propertiesFileName = "org" + pathSep + "bench4q" + pathSep
				+ "master" + pathSep + "config" + pathSep + "httpCaptureConfig"
				+ pathSep + "maxq.properties";

		InputStream propertiesStream = Config.class.getClassLoader()
				.getResourceAsStream(propertiesFileName);
		props = new Properties();
		if (propertiesStream != null) {
			try {
				props.load(propertiesStream);
				log.debug("Generator: "
						+ props.getProperty("generator.classnames"));
			} catch (IOException e) {
				throw new ExceptionInInitializerError(e);
			}

		}

		config.addPythonPath(maxqDir + pathSep + "jython");

		String path = config.getProperty("python.path");
		if (path != null) {
			config.addPythonPath(path);
		}

		String portStr = props.getProperty("local.proxy.port");
		if (portStr != null)
			config.port = Integer.parseInt(portStr);
	}

	public void completeInit() throws Utils.UserException {
		Level level = this.quiet ? Level.WARN : this.debug ? Level.ALL
				: Level.INFO;
		Logger.getRootLogger().setLevel(level);

		System.setProperty("org.apache.commons.logging.simplelog.defaultlog",
				"warn");

		Properties ppref = new Properties();
		if (getPythonPath() != null)
			ppref.put("python.path", getPythonPath());
		// PythonInterpreter.initialize(System.getProperties(), ppref,
		// new String[0]);

		String host = getProperty("remote.proxy.host");
		Integer port = getPropertyInt("remote.proxy.port");
		if ((host == null ? 1 : 0) != (port == null ? 1 : 0))
			throw new Utils.UserException(
					"Not using proxy server.  You must set both remote.proxy.host and remote.proxy.port.");
		if ((host != null) && (port != null)) {
			this.proxySettings = new ProxySettings();
			this.proxySettings.host = host;
			this.proxySettings.port = port.intValue();
			log.info("Proxying requests via " + this.proxySettings.host + ":"
					+ Integer.toString(this.proxySettings.port));
		}
	}

	public String getProperty(String prop) {
		return (String) props.get(prop);
	}

	public String getProperty(String prop, String def) {
		String val = getProperty(prop);
		return (val == null) || (val.equals("")) ? def : val;
	}

	public void setProperty(String property, String value) {
		props.setProperty(property, value);
	}

	public Integer getPropertyInt(String prop) throws Utils.UserException {
		String s = getProperty(prop);
		if (s == null)
			return null;
		try {
			return new Integer(s);
		} catch (NumberFormatException e) {
		}
		throw new Utils.UserException(prop + " property must be an integer");
	}

	public void addPythonPath(String path) {
		if (this.pythonPath == null)
			this.pythonPath = path;
		else
			this.pythonPath = (this.pythonPath
					+ System.getProperty("path.separator") + path);
	}

	public String getPythonPath() {
		return this.pythonPath;
	}

	public void setPort(int pt) {
		this.port = pt;
	}

	public int getPort() {
		return this.port;
	}

	public void setDebug(boolean d) {
		this.debug = d;
	}

	public boolean isDebug() {
		return this.debug;
	}

	public void setQuiet(boolean q) {
		this.quiet = q;
	}

	public boolean isQuiet() {
		return this.quiet;
	}

	public void setExist(String e) {
		this.exist = e;
	}

	public String getExist() {
		return this.exist;
	}

	public void setReplace(String e) {
		this.replace = e;
	}

	public String getReplace() {
		return this.replace;
	}

	public void setIncludePatterns(String[] pats) {
		this.includeList = Arrays.asList(pats);
		createPatterns(this.includeList, this.includePatterns);
	}

	public void setExcludePatterns(String[] pats) {
		this.excludeList = Arrays.asList(pats);
		createPatterns(this.excludeList, this.excludePatterns);
	}

	public List<Pattern> getIncludePatterns() {
		return this.includePatterns;
	}

	public List<Pattern> getExcludePatterns() {
		return this.excludePatterns;
	}

	public void setScriptArg(String arg) {
		this.scriptArg = arg;
	}

	public String getScriptArg() {
		return this.scriptArg;
	}

	public static String getDriverPkgName() {
		return System.getProperty("maxq.driverpkgname", defDriverPkgName);
	}

	public static String getValidatorPkgName() {
		return System.getProperty("maxq.validatorpkgname", defValidatorPkgName);
	}

	public static Log getTestLogger() {
		return LogFactory.getLog("com.bitmechanic.maxq.testrun");
	}

	public ProxySettings getProxySettings() {
		return this.proxySettings;
	}

	public void setProxySettings(ProxySettings proxySettings) {
		this.proxySettings = proxySettings;
	}

	private void createPatterns(List<String> list,
			List<Pattern> excludePatterns2) {
		Iterator<String> iter = list.iterator();
		excludePatterns2.clear();
		while (iter.hasNext())
			try {
				Pattern p = Pattern.compile(iter.next().toString());
				excludePatterns2.add(p);
			} catch (PatternSyntaxException e) {
				log.error(e);
			}
	}

	public static class ProxySettings {
		String host;
		int port;

		public String getHost() {
			return this.host;
		}

		public void setHost(String host) {
			this.host = host;
		}

		public int getPort() {
			return this.port;
		}

		public void setPort(int port) {
			this.port = port;
		}
	}
}
