package CTL;

import CTL.Comm.*;
import CTL.Streams.*;
import CTL.Types.*;

import java.util.*;

/** Communication process */
public class Process
{
	/** Location of the remote communication partner */
	protected Location loc;

	private Env env = null;
	private Thread thr = null;
	private Timer t = new Timer();
	private boolean daemon = false;
	private int remote_id = -1;
	private boolean needsCleaner = true;
	
	public int ID ()
	{
		return remote_id;
	}

	/** Generate a new Process
	 * @param loc Location
	 */
	public Process (Location loc)
	{
		this(loc, null);
	}
	
	/** Generate a new Process
	 * @param loc Location
	 * @param args Commandline arguments
	 */
	public Process (Location loc, String[] args)
	{
		int port = 0;
		env = Env.newEnv();
		this.loc = loc;

		if (loc == null)
		{
			env.log.msg(Logger.WARN, "Location is empty.");
			return;
		}

		if (loc.linkage() == Location.LIB)
			return;

		if (loc.linkage() == Location.CLIB)
		{
			if (!loc.pathExists())
			{
				System.out.println("Could not load native library.");
				System.out.println(loc.path()+": No such file or directory.");
				System.exit(1);
			}
			return;
		}

		String debug 	= System.getenv("CTL_DEBUGGER");
		String debugger = System.getenv("CTL_DBG");

		if (debug != null)
		{
			try
			{
				StringTokenizer tok = new StringTokenizer(debug, ":");
				env.debugger = new PeerID(tok.nextToken(), 
					Integer.parseInt(tok.nextToken()));
			}
			catch (Exception e)
			{
				RUtil.except(e);
			}
		}

		if ((args != null) && (args.length >= 1))
		{
			for (String arg : args)
			{
				if (arg.startsWith("-L"))
				{
					try
					{
						StringTokenizer tok = new StringTokenizer(arg, " ");
						tok.nextToken();
						port = Integer.parseInt(tok.nextToken());
					}
					catch (Exception e)
					{
						RUtil.except(e);
					}
				}
			}
		}
		
		try
		{
			if (loc.path() == null)
			{
				remote_id = 0;
				// TODO: Use the possible custom port
				env.grp = new Group(loc.host(), loc.port(), 1, 2, 
					loc.linkage(), env);
				daemon = true;
			}
			else
			{
				remote_id = 1;
				env.grp = new Group(loc.host(), loc.port(), 0, 2, 
					loc.linkage(), port, env);
				startService(loc, env.comm[remote_id]);
				env.comm[remote_id].accept();
				env.grp.writeVersion(env.comm[remote_id]);
				env.grp.HS_recv();
				env.log.msg(Logger.INFO, 
					"Service started in "+Timer.timestr(t.stop2())+" seconds");
			}

			Thread thr = new Thread(env.grp);
			thr.start();
			while (!env.grp.running());
		}
		catch (Exception e)
		{
			RUtil.except(e);
			throw new RuntimeException("Could not create link.");
		}

		if (needsCleaner)
		{
			needsCleaner = false;
			Cleaner cl = new Cleaner(Thread.currentThread());
			cl.start();
		}

		env.procs.add(this);
	}

	/** Check if the process is actually running
	 * @return True if running, false otherwise.
	 **/
	public boolean isAlive ()
	{
		if (thr != null)
			return thr.isAlive();
		return true;
	}

	/** Start the remote communication service
	 * @param loc Location
	 */
	private void startService (Location loc, Communicator comm)
	{
		SSHv2 cli = new SSHv2(loc, comm, env);
		thr = new Thread(cli);
		thr.start();
	}

	/** Stop the remote communication service */
	public void stopService ()
	{
		stopService(ID(), daemon);
	}
	
	public void stopService (int you, boolean dmn)
	{
		try
		{
			if (dmn)
			{
				BuffyOut out = new BuffyOut();
				Header head = new Header(0, Remote.EOC, env.grp.myInfo());
				out.o.serialWrite(head);
				out.o.flush();
				env.comm[you].send(out.getBytes());
			}
			else
			{
				if (env.grp == null)
					return;
				Header head = new Header(0, Remote.RMI, env.grp.myInfo());
				FID fid = new FID((short)3, "");
				IStream2 args = new IStream2();
				args.write(env.grp.myInfo().pid());
				args.write("EOF.");
				env.grp.terminate();
				env.log.msg(Logger.DBG2, "Terminating remote service.");
				//System.out.println("ID "+you+": "+env.grp.grp[you]);
				Remote.call(env.comm[you], head, env.grp.grp[you].id(),
					fid, args, new rPointer(0, env.grp.myInfo()));
			}
			
			env.comm[you].close();
		} 
		catch (Exception e) 
		{
			if (!(e instanceof java.net.SocketException))
				e.printStackTrace();
		}

		t.stop();
		if (Globals.verbose)
		{
			System.out.println("Total execution time "+t+" seconds.");
			System.out.println("Clean termination.");
		}
		env.clean = true;
		env.procs.remove(this);
	}

	/** Retrieve the location of this process.
	 * @return Location */
	public Location loc ()
	{
		return loc;
	}

	/** Retrieve the local environment of this process
	 * @return Environment
	 */
	public Env getEnv ()
	{
		return env;
	}

	/** Retrieve a string representation of this object
	 * @return String
	 */
	public String toString ()
	{
		return loc.toString();
	}
}
