import java.net.*;
import java.io.*;
import java.util.*;

/**
 * This DigitalComponent is a very funny one. You can connect any DigitalComponent to its pins and states
 * changes will be notified to an other DigitalComponent which is connected to an other TcpIpComponent
 * with which the first one is connected through a TCP/IP socket.
 * <BR>You can this way use PICDebugger over several (virtual)computers or with several JVM on the same
 * computer. And you can even interface it with a program not written in Java.
 * <BR>You can read the source of this class <A HREF="./TcpIpComponent.java.html"> here</A>.
 */
public class TcpIpComponent extends DigitalComponent_std implements DigitalComponent {

	/**
	 * Build a TcpIpComponent with n pins setting it as either a server or a client. The different thing
	 * is that the server must run when the client is started.
	 * @param n The number of pins of the TcpIpComponent (both TcpIpComponent which are connected to
	 * each other must have the same pin number).
	 * @param Port The port the TcpIpComponent should (wait on)/(connect to).
	 * @param Host The host it should connect to (if set to be a client).
	 * @param IsServer If true then the instance will be a server waiting for a client on port Port.
	 */
	public TcpIpComponent(int n, int Port, String Host, boolean IsServer) {
		super(n);

		notifyVector = new Vector();

		_port = Port;
		Socket sock = null;
		rcvt = null;

		if(IsServer) {
			try {
				ServerSocket ssock = new ServerSocket(Port);
				sock = ssock.accept();
			}
			catch (IOException ioe ) {
				System.out.println("Socket server not created !");
			}
		}
		else {
			try {
				sock = new Socket (Host, Port);
			}
			catch (SocketException se) {
				System.out.println("Socket not created !");
			}
			catch (IOException ioe) {
				System.out.println("Socket not created !");
			}
		}

		setStreamsAndStartThread(sock);
	}

	public void setStreamsAndStartThread(Socket sock) {
		if(sock != null) {
			try {
				in  = new BufferedReader(new InputStreamReader(sock.getInputStream()));
				out  = new PrintStream(sock.getOutputStream());
			}
			catch (IOException ioe) {
				System.out.println("Steams not connected to socket !");
			}

			rcvt = new ReceivingThread();
			rcvt.start();
		}
		else {
			in = null;
			out = null;
		}
	}

	protected void setPinStateDontNotifyRemote(int p, boolean state) {
		super.setPinState(p, state);
	}

/*	protected void restartSocketServer() { // does not work yet
		t.interrupt();

		Socket sock = null;

		try {
			ServerSocket ssock = new ServerSocket(_port);
			sock = ssock.accept();
		}
		catch (IOException ioe ) {
			System.out.println("Socket server not created !");
		}

		setStreamsAndStartThread(sock);
	}*/

	public void setPinStateDontNotify(int p, boolean state) {
		super.setPinStateDontNotify(p, state);
		if(out != null)
			out.println("set PIN : " + Utils.hex(p, 5, 5) + " to state : " + state);
		if(!Thread.currentThread().getName().equals("ReceivingThread")) {
			StopWaiting = false;
			while(!StopWaiting);
		}
	}
	public void stopWaiting() {
		StopWaiting = true;
	}

	public void notifyOneCycle() {
		if(out != null)
			out.println("notified");
	}

	public void addComponentToNotify(DigitalComponent c) {
		notifyVector.addElement(c);
	}
	
	protected void notifyAllOneCycle() {
		int i, n = notifyVector.size();

		for(i = 0 ; i < n ; i++)
			((DigitalComponent)notifyVector.elementAt(i)).notifyOneCycle();
	}


	protected class ReceivingThread extends Thread {

		public ReceivingThread() {
			super("ReceivingThread");
		}

		public void run() {
			String msg;

			while(true) {
				try {
					msg = in.readLine();
					if(msg.equals("StopWaiting"))
						stopWaiting();
					if(msg.equals("notified"))
						notifyAllOneCycle();
					else if(msg.startsWith("set PIN : ")) {
						msg = msg.substring(10);
						int p = Utils.hex2num(msg);
						msg = msg.substring(msg.indexOf(" to state : ") + 12);
						if(msg.equals("true"))
							setPinStateDontNotifyRemote(p, true);
						else
							setPinStateDontNotifyRemote(p, false);
						out.println("StopWaiting");
					}
				}
				catch (IOException ioe) {
// Does not work yet					restartSocketServer();
				}
			}
		}
	};


	protected Vector notifyVector;
	protected BufferedReader in;
	protected PrintStream out;
	protected ReceivingThread rcvt;
	protected int _port;
	protected boolean StopWaiting;
}