package bramble.node.master;

import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

import bramble.configuration.BrambleConfiguration;
import bramble.networking.Handshake;
import bramble.networking.JobResponseData;
import bramble.networking.ListenerServer;
import bramble.networking.Message;
import bramble.node.controller.ControllerNode;
import bramble.node.controller.IControllerNode;
import bramble.webserver.WebAPI;
import bramble.webserver.WebServer;

public class MasterNode<T extends IMasterNodeRunner> implements Runnable, Cloneable {
	
	private final Message incomingData;
	private final T masterNodeRunner;
	private final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
	private ControllerNode controllerNode;
	
	public MasterNode(T masterNodeRunner){		
		this.incomingData = null;
		this.masterNodeRunner = masterNodeRunner;			
	}
	
	public MasterNode(T masterNodeRunner, IControllerNode controllerNodeRunner){
		this(masterNodeRunner);
		this.controllerNode = new ControllerNode(controllerNodeRunner);
	}
	
	public MasterNode(T masterNodeRunner, Message incomingData){
		this.incomingData = incomingData;
		this.masterNodeRunner = masterNodeRunner;
	}
	
	private MasterNode(T masterNodeRunner, Message incomingData, ControllerNode jobSetup){
		this(masterNodeRunner, incomingData);
		this.controllerNode = jobSetup;
	}
	
	@Override
	public MasterNode<T> clone(){
		return new MasterNode<T>(this.masterNodeRunner, this.incomingData, this.controllerNode);
	}
	
	private void listen(ListenerServer listenerServer){
		try {
			Message data = listenerServer.listen();	
			if(data != null && data instanceof Message){
				parseIncomingData(data);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void listenForever() {
		ListenerServer listenerServer;
		try {
			listenerServer = new ListenerServer(BrambleConfiguration.MASTER_PORT);
		} catch (IOException e) {
			System.out.println("Can't set up more than one listener on the same port.");
			return;
		}
		
		while(true){
			listen(listenerServer);
		}
	}
	
	synchronized private final void parse(Handshake handshake){
		controllerNode.registerSlaveNodeByHandshake(handshake);
	}
	
	synchronized private final void parse(Message incomingData){
			if(incomingData instanceof JobResponseData){
				controllerNode.jobFinished(((JobResponseData) incomingData));
				masterNodeRunner.parse((JobResponseData) incomingData);
			} else if (incomingData instanceof Handshake){
				parse((Handshake) incomingData);
			} else {
				WebAPI.publishMessage("Got passed a wierd object... " + (incomingData).getClass());
			}
	}
		
	/**
	 * Sets the data for the message parser to parse
	 * 
	 * @param incomingData - the data to be parsed
	 */
	private void parseIncomingData(Message incomingData){
		MasterNode<T> newThreadMasterNode = new MasterNode<T>(masterNodeRunner, incomingData, controllerNode);
		executor.execute(newThreadMasterNode);
	}
	
	/**
	 * Runs the parser. 
	 *
	 * Clients should override parse(JobResponseData) instead of run()
	 */
	@Override
	public void run() {
		if (this.incomingData != null){
			parse(this.incomingData);
		}
	}
	
	synchronized public void setJobSetupRunner(IControllerNode jobSetupRunner){
		this.controllerNode = new ControllerNode(jobSetupRunner);
	}
	
	synchronized public void startControllerNodeRunner(){
		try{
			executor.execute(this.controllerNode);
		} catch (NullPointerException e){
			System.out.println("Couldn't start the controller node runner - check it was set before being run");
		}
	}
	
	public void startWebServer(){
		executor.execute(new WebServer());
	}
}

@@ -1334,24 +1334,36 @@
 lerNode 
-jobSetup
+controllerNodeRunner
 )%7B%0A%09%09thi
@@ -1421,24 +1421,36 @@
 rNode = 
-jobSetup
+controllerNodeRunner
 ;%0A%09%7D%0A%09%0A%09
@@ -3232,32 +3232,161 @@
 Data);%0A%09%09%7D%0A%09%7D%0A%09%0A
+%09/**%0A%09 * Sets a new controller node runner to use.%0A%09 * %0A%09 * @param controllerNodeRunner a new controller node runner to use%0A%09 */%0A
 %09synchronized pu
@@ -3402,16 +3402,22 @@
  set
-JobSetup
+ControllerNode
 Runn
@@ -3435,24 +3435,30 @@
 lerNode 
-jobSetup
+controllerNode
 Runner)%7B
@@ -3505,29 +3505,234 @@
 ode(
-jobSetupRunner);%0A%09%7D%0A%09
+controllerNodeRunner);%0A%09%7D%0A%09%0A%09/**%0A%09 * Starts the controller node runner in a new thread.%0A%09 * %0A%09 * Must be set before this method is called - either in the constructor,%0A%09 * or by explicitly calling setControllerNodeRunner()%0A%09 */
 %0A%09sy
@@ -3973,32 +3973,95 @@
 run%22);%0A%09%09%7D%0A%09%7D%0A%09%0A
+%09/**%0A%09 * Starts a new webserver, which serves the web API%0A%09 */%0A
 %09public void sta

Add javadoc, tidy up method names