package net.oni2;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.Proxy.Type;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.StaxDriver;

/**
 * @author Christian Illy
 */
public class ProxySettings implements Serializable {
	private static final long serialVersionUID = 7764207071836441954L;

	private static ProxySettings instance = new ProxySettings();

	private boolean useProxy = false;
	private String hostOrIp = "";
	private int port = 8080;

	private transient boolean validated = false;
	private transient boolean validatedOk = false;

	/**
	 * Get the proxy for URL connections. Validates connection first if not
	 * validated yet
	 * 
	 * @return Proxy
	 */
	public Proxy getProxy() {
		if (!useProxy)
			return Proxy.NO_PROXY;
		if (!validated)
			validate();
		if (!validatedOk)
			return Proxy.NO_PROXY;

		return new Proxy(Type.HTTP, new InetSocketAddress(hostOrIp, port));
	}

	/**
	 * Check if a connection to the given proxy can be established
	 * 
	 * @return Data entered and connection Ok?
	 */
	public boolean validate() {
		if (validated) {
			return validatedOk;
		} else {
			validated = true;
			if (isValid()) {
				try {
					URLConnection con = new URL(String.format("http://%s:%d",
							hostOrIp, port)).openConnection();
					con.setConnectTimeout(1000);
					con.connect();
					validatedOk = true;
					return true;
				} catch (MalformedURLException e) {
				} catch (SocketTimeoutException e) {
				} catch (IOException e) {
				}
			}
			validatedOk = false;
			return false;
		}
	}

	/**
	 * @return Is data for both host and port entered?
	 */
	public boolean isValid() {
		return (hostOrIp != null) && (port > 0);
	}

	/**
	 * @return Should a proxy be used for connections?
	 */
	public boolean isUseProxy() {
		return useProxy;
	}

	/**
	 * @param use
	 *            Should a proxy be used for connections?
	 */
	public void setUseProxy(boolean use) {
		this.useProxy = use;
	}

	/**
	 * @return Hostname or IP of host to use as proxy
	 */
	public String getHostOrIp() {
		return hostOrIp;
	}

	/**
	 * @param hoi
	 *            Hostname or IP of host to use as proxy
	 */
	public void setHostOrIp(String hoi) {
		validated = false;
		this.hostOrIp = hoi;
	}

	/**
	 * @return Port number proxy is listening on
	 */
	public int getPort() {
		return port;
	}

	/**
	 * @param port
	 *            Port number proxy is listening on
	 */
	public void setPort(int port) {
		validated = false;
		this.port = port;
	}

	/**
	 * Get the singleton instance
	 * 
	 * @return Singleton instance
	 */
	public static ProxySettings getInstance() {
		return instance;
	}

	private static XStream getXStream() {
		XStream xs = new XStream(new StaxDriver());
		xs.alias("Proxy", ProxySettings.class);
		return xs;
	}

	/**
	 * Serializes the settings to disk
	 * 
	 * @param settingsFile
	 *            File to write to
	 */
	public void serializeToFile(File settingsFile) {
		try {
			FileOutputStream fos = new FileOutputStream(settingsFile);
			XStream xs = getXStream();
			xs.toXML(this, fos);
			fos.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Deserializes the settings from disk
	 * 
	 * @param settingsFile
	 *            File to read from
	 */
	public static void deserializeFromFile(File settingsFile) {
		try {
			FileInputStream fis = new FileInputStream(settingsFile);
			XStream xs = getXStream();
			Object obj = xs.fromXML(fis);
			if (obj instanceof ProxySettings)
				instance = (ProxySettings) obj;
			fis.close();
		} catch (FileNotFoundException e) {
		} catch (IOException e) {
		}
	}

}
