/*
 * Created on 2004-6-19
 *
 * To change the template for this generated file go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
package org.huihoo.willow.client;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.rmi.MarshalledObject;
import java.rmi.RemoteException;
import java.util.Hashtable;

import javax.naming.InvalidNameException;
import javax.naming.NamingException;
import javax.naming.ServiceUnavailableException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import workflow.WorkflowException;
import workflow.client.WorkflowClient;
import workflow.client.LiveClient;
import workflow.live.LiveService;

/**
 * @author reic
 *
 * To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
public class LiveClientImpl implements LiveClient, java.io.Serializable
{
	private static Log log = LogFactory.getLog(LiveClientImpl.class);

	protected Naming namingServer;
	protected Hashtable env;

	protected String sessionID;
	
	//-----------------------------------------------------------constructor
	public LiveClientImpl(Hashtable environment) throws WorkflowException, NamingException
	{
		init(environment);		
	}

	//------------------------------------------------------------public	
	public String getSessionID() throws WorkflowException
	{
		return sessionID;
	}
	
	public int getMaxInactiveInterval()throws WorkflowException
	{
		try
		{
			return namingServer.getMaxInactiveInterval(sessionID);
		}
		catch (RemoteException ex)
		{
			ex.printStackTrace();
			throw new WorkflowException(ex);
		}
	}

	public void setMaxInactiveInterval(int interval)throws WorkflowException
	{
		try
		{
			namingServer.setMaxInactiveInterval(sessionID,interval);
		}
		catch (RemoteException ex)
		{
			ex.printStackTrace();
			throw new WorkflowException(ex);
		}
	}
	
	public void  keepAlive() throws WorkflowException
	{
		try
		{
			namingServer.keepAlive(sessionID);
		}
		catch (RemoteException ex)
		{
			ex.printStackTrace();
			throw new WorkflowException(ex);
		}
	}
	
	public LiveService findService(String workflowID) throws WorkflowException
	{
		try
		{
			LiveService workflow = null;

			if (namingServer != null)
			{
				workflow = namingServer.findService(workflowID, sessionID);

				if (workflow != null)
				{
					return workflow;
				}
			}
		}
		catch (Exception ex)
		{
			ex.printStackTrace(System.err);
			throw new WorkflowException(ex);
		}

		return null;
	}

	public LiveService[] findServices() throws WorkflowException
	{
		LiveService[] workflows = new LiveService[0];
		try
		{
			if (namingServer != null)
			{
				workflows = namingServer.findServices(sessionID);
			}
		}
		catch (Exception ex)
		{
			throw new WorkflowException(ex);
		}

		return workflows;
	}
	
	public void    disconnect() throws WorkflowException
	{
		try
		{
			namingServer.disconnect(sessionID);
		}
		catch (RemoteException ex)
		{
			throw new WorkflowException(ex);
		}
	}
	
	//------------------------------------------------------------
	private void init(Hashtable refEnv) throws WorkflowException, NamingException
	{
		if (refEnv == null)
		{
			refEnv = new Hashtable();
		}

		this.env = (Hashtable) refEnv.clone();

		String host = "localhost";
		int port = 2126;

		// Locate naming service
		String provider_url = (String) refEnv.get(WorkflowClient.PROVIDER_URL);
		
		if (provider_url != null && provider_url.length() > 0)
		{
			String serverInfo = parseServerInfo(provider_url);
			
			if (serverInfo != null)
			{
				int colon = serverInfo.indexOf(':');
				if (colon < 0)
				{
					host = serverInfo;
				}
				else
				{
					host = serverInfo.substring(0, colon).trim();
					try
					{
						port = Integer.parseInt(serverInfo.substring(colon + 1).trim());
					}
					catch (Exception ex)
					{
						// Use default;
					}
				}
				
				try
				{
					// Get server from connection
					namingServer = getServer(host, port, refEnv);
					
					String username=(String)env.get(WorkflowClient.SECURITY_PRINCIPAL);
					String password=(String)env.get(WorkflowClient.SECURITY_CREDENTIALS);
					this.sessionID=namingServer.authenticate(username,password);
				}
				catch (Exception e)
				{
					log.warn("Failed to get server  " + host + ":" + port, e);
					namingServer = null;
				}
			}

		} //~end if (provider_url != null && provider_url.length() > 0)
	}

	static Naming getServer(String host, int port, Hashtable env) throws WorkflowException
	{
		String hostKey = host + ":" + port;

		Naming server = null;

		try
		{
			Socket socket = null;

			try
			{
				InetAddress localAddr = null;
				int localPort = 0;

				String localAddrStr = (String) env.get(WorkflowClient.LOCAL_BINDING_ADDRESS);
				String localPortStr = (String) env.get(WorkflowClient.LOCAL_BINDING_PORT);

				if (localAddrStr != null)
				{
					localAddr = InetAddress.getByName(localAddrStr);
				}
				if (localPortStr != null)
				{
					localPort = Integer.parseInt(localPortStr);
				}
				socket = new Socket(host, port, localAddr, localPort);
			}
			catch (IOException e)
			{
				NamingException ex =
					new ServiceUnavailableException("Failed to connect to server " + hostKey);
				ex.setRootCause(e);
				throw ex;
			}

			// Get stub from naming server
			BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
			ObjectInputStream in = new ObjectInputStream(bis);
			MarshalledObject stub = (MarshalledObject) in.readObject();
			
			server = (Naming) stub.get();
			socket.close();

			return server;

		}
		catch (Exception ex)
		{
			ex.printStackTrace(System.err);
			throw new WorkflowException(ex);
		}
	}

	static String parseServerInfo(String provider_url) throws InvalidNameException
	{
		String serverInfo = null;

		if (provider_url!=null && provider_url.length()>0)
		{
			int schemeLength = 0;
			
			if (provider_url.startsWith("workflow:"))
			{				
				schemeLength = "workflow:".length();
			}

			if (schemeLength > 0)
			{
				serverInfo = provider_url.substring(schemeLength);
				if (serverInfo!=null && serverInfo.length() >0)
				{
					while(serverInfo.charAt(0)=='/')
					{
						if(serverInfo.length()==1)
						{
							serverInfo=null;
							break;
						}
						serverInfo=serverInfo.substring(1);
					}	
					
					if (serverInfo!=null && serverInfo.length() >0)
					{
						int pos=serverInfo.indexOf("/");
						if(pos>0)
						{
							serverInfo=serverInfo.substring(0,pos);
						}
					}				
				}
			}
		}

		return serverInfo;
	}

}
