package taskspaces.system;

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

/**
 * Represents a Node which consists of a Worker and Server thread. 
 * The Node starts Worker and Server threads enabling execution of 
 * Tasks and contains a set of public methods for manipulating the 
 * state of this Node and its threads. <br />
 * Useage: <code>java Runner <properties file> [additional resources] ... [additional resources] taskspaces.system.Node</code>
 * @author Matthew Keoshkerian
 * @version 2.0
 */
public class Node extends Communicator implements java.io.Serializable, Runnable
{
	/** Port number of the child Server. */
	int port=0;

	/** Maximum tasks to execute before exiting. */
	long maxTasks=0;

	/** Address of the registered Space. */
	String spaceAddress=null;

	/** The local IP address. */
	String ip=null;

	/** The local host name. */
	String host=null;

	/** The child TaskStore. */
	TaskStore taskStore;

	/** The child MessageStore. */
	MessageStore messageStore;

	/** Information for Agents. */
	Object agentInfo;

	/** Thread to hold Worker. */
	Thread workerThread;

	/** Thread to hold Server. */
	Thread serverThread;

	/** Incremental ID for Workers and Servers. */
	int id=1;

	/** Indicates if Worker is busy executing Task. */
	boolean busy;

	/** Default Worker port. */
	int defaultPort;

	/**
	 * Called by Runner to start a Node on a given host. 
	 * Starts a Worker and Server thread.
	 * @return void
	 */
	public void run()
	{
		try
		{
			// Initialize the Node's properties and variables.
			setProperties();
			setHost();
			setIP();
			
			// Create a new TaskStore and MessageStore for this Node.
			taskStore=new TaskStore();
			messageStore=new MessageStore();
			
			// Set the default log output.
            (new File("./logs/")).mkdir();
			logWriteName = "./logs/system.system";
			logPrefix = "Node";
			logWrite = new PrintWriter(new FileWriter(logWriteName, true));
				
			// Start the Server and Worker that will handle this Node's work.
			startServer();
			startWorker();
		} 
		catch(Exception e) {e.printStackTrace();}
	}

	/**
	 * Registers this Node with a given Space.
	 * @return void
	 */
	public void register()
	{
		try
		{
			// This is a Communicator method that send a message to the Space indicating its availability.
			registerNode(spaceAddress,port);
		} catch(Exception e) { e.printStackTrace(); }
	}

	/**
	 * Indicates the child worker is busy.
	 * @return void
	 */
	public void setBusy(boolean b) {this.busy=b;}

	/**
	 * Indicates if the Worker thread is executing a Task.
	 * @return <code>true</code> if busy.
	 */
	public boolean getBusy() {return busy;}
	
	/**
	 * Starts a Server.
	 * @return void
	 */
	public void startServer()
	{
		// Create a Server in a new Thread and start the process.
		serverThread=new Thread(new Server(taskStore,messageStore,id,this));
		serverThread.start();
		id++;
	}

	/**
	 * Starts a Worker.
	 * @return void
	 */
	public void startWorker()
	{
		// Create a Worker in a new Thread and start the process.
		workerThread=new Thread(new Worker(taskStore,id,this));
		workerThread.start();
		id++;
	}

	/**
	 * Restarts the Worker thread.
	 * @return void
	 */
	public void restart()
	{
		print("Resetting");
		// Inturrept and stop the Worker Thread.
		workerThread.interrupt();
		workerThread.stop(); // Deprecated. Try experimenting with other methods such as destroy.
		id=2;
		// Start the worker up again and set the Node to not busy.
		startWorker();
		setBusy(false);
	}

	/**
	 * Sets the address of the associated Space for this Node. 
	 * @return void
	 */
	public void setProperties() throws Exception
	{
		// Get the properties file address from the System property and load it (NB: Using Communicator's load algorithm).
		Properties p=load(System.getProperty("propsAddress"));
		// Grab the location of the spaces and parse them. Then use the first spaceAddress for the spaceAddress variable. This is the taskspace.
		String[] spaces=parseProperty(p.getProperty("spaces"));
		spaceAddress=spaces[0];
		// Determine the maximum allowed tasks by the properties file and set that as the max tasks value.
		long tasks=Long.parseLong(p.getProperty("maximum_tasks"));
		setMaxTasks(tasks);
		// Get the default worker port outlined by the properties file and set that as the default port.
		setDefaultPort(Integer.parseInt(p.getProperty("worker_port")));
	}

	/**
	 * Clears the messages from this Node's MessageStore. 
	 * @return void
	 */
	public void clearMessages() {messageStore.clear();}
  
	/**
	 * Returns an Object containing information for Agents. 
	 * @return contains information or executable.
	 */
	public Object getAgentInfo() {return agentInfo;} 
  
	/**
	 * Stores the given Object in this Node's agentInfo data
	 * member for retrieval.
	 * @param contains information or executable.
	 * @return void
	 */
	public void setAgentInfo(Object o) {this.agentInfo=o;}

	/**
	 * Returns a MessageStore reference for Tasks. 
	 * @return  this Node's MessageStore.
	 */
	public MessageStore getMessageStore() {return messageStore;}

	/**
	 * Returns a reference to this Node's TaskStore. 
	 * @return the local TaskStore.
	 */
	public TaskStore getTaskStore() {return taskStore;}

	/**
	 * Sets this Node's host member to the local host name. 
	 * @return void
	 */
	public void setHost() throws Exception {host=InetAddress.getLocalHost().getHostName();}

	/**
	 * Returns the local host name. 
	 * @return the host name.
	 */
	public String getHost() {return host;}

	/**
	 * Sets this Node's ip member to the host system's IP address. 
	 * @return void
	 */
	public void setIP() throws Exception
	{
		// Get all the IP addresses registered to this machine and take the first as the main IP.
		InetAddress[] i=InetAddress.getAllByName(host);
		ip=i[0].getHostAddress();
	}

	/**
	 * Returns the IP address of the host system. 
	 * @return the IP address.
	 */
	public String getIP() {return ip;}

	/**
	 * Sets the IP address of the registered Space. 
	 * @return void
	 */
	public void setSpace(String s) {this.spaceAddress=s;}

	/**
	 * Returns the IP address of the registered Space. 
	 * @return the Space's IP address.
	 */
	public String getSpaceAddress() {return spaceAddress;}

	/**
	 * Sets port to the port number of the local Server. 
	 * @return void
	 */
	public void setPort(int i) {this.port=i;}

	/**
	 * Returns the port number of the local Server. 
	 * @return the Server port number.
	 */
	public int getPort() {return port;}

	/**
	 * Returns the port number of the local Server. 
	 * @return the Server port number.
	 */
	public long getMaxTasks() {return maxTasks;}

	/**
	 * Sets the number of tasks before this Node will exit. 
	 * @param tasks the number of tasks to execute.
	 * @return void.
	 */
	public void setMaxTasks(long tasks)
	{
		maxTasks=tasks;
	}

	/**
	 * Sets the default port number of the local Server. 
	 * @return void
	 */
	public void setDefaultPort(int p)
	{
		defaultPort=p;
	}

	/**
	 * Returns the default port number of the local Server. 
	 * @return the default port number.
	 */
	public int getDefaultPort()
	{
		return defaultPort;
	}
}