package cdac.medinfo.sdk.hl7282.hl7net.communication;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Locale;

import cdac.medinfo.sdk.hl7282.exceptions.ConnectionClosedException;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IBufferReceiver;
import cdac.medinfo.sdk.hl7282.hl7net.commons.IConnectionListener;
import cdac.medinfo.sdk.hl7282.hl7net.commons.INetworkAgent;
import cdac.medinfo.sdk.hl7282.utils.HL7Config;
import cdac.medinfo.sdk.hl7282.utils.HL7Logger;
import cdac.medinfo.sdk.hl7282.utils.ResourceBundleWrapper;


/**
 * ServerSideNetworkAgent acts as proxy between underlying Network Layer & User Interface Layer.
 * It establishes communication between ClientSession & Socket.
 */
public class ServerSideNetworkAgent extends Thread implements INetworkAgent   
{
	

	private IBufferReceiver listener = null;
	private Socket clientSocket;
	private InputStream socketInputstream;
	private OutputStream socketOutputstream;
	private int iReadMaxLength;
	private char messageDelimiter = '\r'; 
	private IConnectionListener objConnectionListener = null;

	/**
	 * Instantiates ServerSideNetworkAgent with specified Socket
	 * @param socket the socket
	 * @throws IOException  Signals that an IO Exception has occurred.
	 */
	public ServerSideNetworkAgent(Socket socket) throws IOException
	{
		super("ServerSideNetworkAgent");
		clientSocket = socket;
		socketInputstream = clientSocket.getInputStream();
		socketOutputstream = clientSocket.getOutputStream();
	}

	/**
	 * Run Method.
	 */
	public void run()
	{
		try 
		{
			read();
		} 
		catch (InterruptedException e) 
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHLogger = HL7Logger.getLogger();
				objHLogger.log(e.getMessage());
			}
		}	
		catch(SocketTimeoutException ste)
		{
			if (HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();		
				if(null != clientSocket)
				{
					objHL7Logger.log(ResourceBundleWrapper.getValue("warnings", "SocketReadTimeout", new Locale(HL7Config.createInstance().getLocale()))
							+"["+
							ResourceBundleWrapper.getValue("warnings", "IPAddress",new Locale(HL7Config.createInstance().getLocale()))+"= "+ ((InetSocketAddress)clientSocket.getRemoteSocketAddress()).getAddress().getHostAddress()+" , "+
							ResourceBundleWrapper.getValue("warnings", "Port",new Locale(HL7Config.createInstance().getLocale())) + "= "+((InetSocketAddress)clientSocket.getRemoteSocketAddress()).getPort()+"]");
				}
				objHL7Logger.log(ste.getMessage());
			}
		}
		catch (IOException e) 
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHLogger = HL7Logger.getLogger();
				objHLogger.log(e.getMessage());
			}
		}
		catch (ConnectionClosedException e) 
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHLogger = HL7Logger.getLogger();
				objHLogger.log(e.getMessage());
			}
		}
		catch (Exception e) 
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHLogger = HL7Logger.getLogger();
				objHLogger.log(e.getMessage());
			}
		}
		objConnectionListener.closeConnection();
	}

	/**
	 * Sets max length limit of reading and writing. 
	 * @param iReadLength value of Reading max length
	 * @param iWriteLength value of Writing max length
	 */
	public void setReadWriteLength(int iReadLength,int iWriteLength)
	{
		iReadMaxLength = iReadLength;
		//iWriteMaxLength = iWriteLength;
	}

	/**
	 * Reads Data From Input Stream of Underlying Socket with Packet Size of 1MB.
	 * @throws InterruptedException thrown when reading method is interrupted
	 */
	public void read()  throws InterruptedException, IOException
	{
		ByteArrayOutputStream msgBuffer = new ByteArrayOutputStream();
			int iBytesRead = 0,iBytesWritten = 0;
			byte buffer[] = null;			
			boolean status = false;
			boolean isClosed = false;

			while(!isClosed && clientSocket != null && clientSocket.isConnected() && socketInputstream != null && !clientSocket.isInputShutdown())
			{
				if(iReadMaxLength != 0)
					buffer = new byte[iReadMaxLength];
				else
					buffer = new byte[1024];

				if(status && msgBuffer.size() > 0 && socketInputstream.available()== 0)
				{
					iBytesRead = 0;
					//read last two bytes of msgBuffer to confirm message delimiter character
					byte[] messageBufferBytes = msgBuffer.toByteArray();
					char ch= (char)messageBufferBytes[messageBufferBytes.length-1];
					if(ch == this.messageDelimiter && listener.onMessageBufferReceived(msgBuffer.toByteArray()))
					{
						status = false;
						buffer = null;
						msgBuffer = new ByteArrayOutputStream();
						continue;
					}
				}

				if(socketInputstream != null)
					iBytesRead = socketInputstream.read(buffer,0, buffer.length);
				else
				{
					throw new ConnectionClosedException();
				}

				if(iBytesRead < 0)
				{
					throw new ConnectionClosedException();
				}
				else
				{
					if(buffer.length > 0)
					{
						msgBuffer.write(buffer,0,iBytesRead);
						iBytesWritten += iBytesRead;
						status = true;
					}
				}
			}

				
	}
	/**
	 * Writes data on Output Stream of Underlying Socket.
	 * @param buffer the buffer to be written
	 */
	public void write(byte[] buffer) throws IOException
	{
		socketOutputstream.write(buffer);
		socketOutputstream.flush();
	}

	/**
	 * Sets IBufferReceiver on AuxAgent.
	 * @param objIMessageBufferReceiver - obj of MessageBufferReceiver
	 */
	public void addBufferReceiver(IBufferReceiver objIMessageBufferReceiver)
	{
		listener = objIMessageBufferReceiver;
	}
	
	
	/**
	 * Sets ConnectionListener.
	 * @param IConnectionListener--object of ConnectionListener
	 */
	public void setConnectionListener(IConnectionListener objIConnectionListener)
	{
		this.objConnectionListener = objIConnectionListener;
	}


	/**
	 * Closes the socket.
	 */
	public void close() throws IOException
	{
		if(clientSocket != null)
			clientSocket.close();
		socketInputstream = null;
		socketOutputstream = null;
		clientSocket = null;
		listener = null;
	}
}
