import java.net.*;
import java.io.*;
import java.beans.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
/**
 * NetworkHandler handles the XMLSocket and executes network actions
 * The NetworkHandler handles both the network messages as well as 
 * message construction and message parsing. It will send events 
 * to the GUI to draw as well as accepts commands from the GUI layer.
 */
public class NetworkHandler implements PropertyChangeListener {
	/**
	 * The NetworkHandler singleton
	 * */
	private static NetworkHandler nh = null;
	/**
	 * The XMLSocket (send and recieve messages)
	 * */
	private XMLSocket client = null;
	/**
	 * the host to connect to
	 * */
	static public InetAddress currentHost = null;
	/**
	 * the port to connect to 
	 * */
	static public int currentPort = 6783;
	/**
	 * The host connected to
	 * */
	private InetAddress host = null;
	/**
	 * The port connected to 
	 * */
	private int port = 0;
	/**
	 * Has the host info changed?
	 * */
	private static boolean newHost = false;
	/**
	 * set the current host with address i
	 * @param i the address of the host to connect to
	 * */
	public static void setCurrentHost(InetAddress i) {
		currentHost = i;
		newHost = true;
	}
	/**
	 * set the current port with  i
	 * @param i the port of the host to connect to
	 * */
	public static void setCurrentPort(int i) {
		currentPort = i;
		newHost = true;
	}
	/**
	 * Get's the current Host
	 * */
	public static InetAddress getCurrentHost() {
		return currentHost;
	}
	/**
	 * gets the current port
	 * */
	public static int getCurrentPort() {
		return currentPort;
	}
	/**
	 * Singleton, returns a network handler handling view.
	 * @param view the ViewWindow which recieves events from the NetworkHandler
	 * */
	public static NetworkHandler networkHandler(ViewWindow view) {
		if (nh == null) {
			if (currentHost == null) {
				try {
					currentHost = InetAddress.getLocalHost();
				} catch (java.net.UnknownHostException uhe) {
					System.err.println("LocalAddress Not Found!");
					System.exit(1);
				}
			}
			nh = new NetworkHandler(view,currentHost,currentPort);
		}
		return nh;
	}
	/**
	 * ViewWindow to send messages to
	 * */
	private ViewWindow window;
	/**
	 * private Constructor using view host and port
	 * @param view the ViewWindow to send messages
	 * @param host the host to connect to
	 * @param port the port of the remote host
	 * */
	private NetworkHandler(ViewWindow view,InetAddress host,int port) {
		this.host = host;	
		this.port = port;
		this.window = view;
	}
	/**
	 * This connects to the server (ensures the connection)
	 * */
	private void _connect() throws IOException {
		if (newHost && client!=null) {
			client.close();
			newHost = false;
		}
		if (client==null) {
			client = new XMLSocket(new Socket(host,port));
			client.startThread();
			client.addPropertyChangeListener(this);
			newHost = false;
		} 
	}
	/**
	 * disconnect from the Connection conn
	 * Fully Disconnect a Connection.
	 * @param conn the connection to disconnect
	 * @throws IOException if there is an IOException (e.g. can't send a message)
	 * */
	public void disconnect(Connection conn) throws IOException {
		_connect();
		String out = "<disconnect><connection>"+conn.getID()+"</connection></disconnect>";
		client.sendMessage(out);
	}
	/**
	 * Unpatches and patch between connections "connect1" and "connect2"
	 * @param connIn the connection to unpatch From
	 * @param connOut the connection to unpatch To
	 * @throws IOException if there is an IOException (e.g. can't send a message)
	 * */
	public void unPatch(Connection connIn,Connection connOut)throws IOException  {
		_connect();
		client.sendMessage("<unPatch><from>"+connIn.getID()+"</from><to>"+connOut.getID()+"</to></unPatch>");

	}
	/**
	 * Sends Connector a message to patch the connection represented by 
	 * "connect1" to the connection represented by "connect2". 
	 * "connect1" serves as an Input Connection while connect2 serves as 
	 * an Output Connection.
	 * @param connIn the connection to patch From
	 * @param connOut the connection to patch To
	 * @throws IOException if there is an IOException (e.g. can't send a message)
	 * */
	public void patch(Connection connIn,Connection connOut) throws IOException{
		_connect();
		client.sendMessage("<patch><from>"+connIn.getID()+"</from><to>"+connOut.getID()+"</to></patch>");

	}
	/**
	 * sends the get last update time message and returns value in a callback.
	 * @throws IOException if there is an IOException (e.g. can't send a message)
	 * */
	public void getLastUpdateTime() throws IOException {
		getLastUpdateTime(0);
	}
	/**
	 * sends the get last update time message and returns value in a callback.
	 * @param lastTime the lastTime we asked.
	 * @throws IOException if there is an IOException (e.g. can't send a message)
	 * */
	public void getLastUpdateTime(int lastTime) throws IOException {
		_connect();
		client.sendMessage("<changedSince><from>"+lastTime+"</time></changedSince>");
	}
	/**
	 * sends the get last update time message and returns value in a callback.
	 * @throws IOException if there is an IOException (e.g. can't send a message)
	 * */
	public void getView() throws IOException {
		_connect();
		String out = "<request>update</request>";
		client.sendMessage(out);
	}
	/**
	 * propertyChange listener accepts messages from XMLSocket
	 * @param evt the event
	 * */
	public void propertyChange(PropertyChangeEvent evt) {
		XMLString value  = (XMLString)evt.getNewValue();
		messageHandle(value);
	}
	/**
	 * sends the shutdown message to the server
	 * @throws IOException if there is an IOException (e.g. can't send a message)
	 * */
	public void shutDown() throws IOException {
		_connect();
		client.sendMessage("<shutdown></shutdown>");
	}
	/**
	 * MessagesHandler, handles XML messages.
	 * */
	public void messageHandle(XMLString value) {
		String name = value.getName();
		try {
			if (name.equals("error")) {
				String xml = value.getXML();
				ErrorParser ep = new ErrorParser();
				client.parseXML(ep,xml);
				window.handleError(ep.getError());
			} else if (name.equals("update")) {
				String xml = value.getXML();
				UpdateParser up = new UpdateParser();
				client.parseXML(up,xml);
				Object [] obj =  (Object[])up.getViewObjects();
				window.updateView(obj);
			} else if (name.equals("lastChange")) {
				String xml = value.getXML();
				ChangedParser cp = new ChangedParser();
				client.parseXML(cp,xml);
				int update = cp.getTime();
				window.checkUpdate(update);
			} else if (name.equals("disconnect")) {
				window.handleError("Disconnected");
			} else {
				throw new IOException("Unknown Message: "+name);
			}
		} catch (SAXException e) {
			window.handleError(e.toString());
		} catch (IOException e) {
			window.handleError(e.toString());
		}
	}
}

