

package gov.va.med.cds.client.proxy;


import gov.va.med.cds.exception.ErrorCodeEnum;
import gov.va.med.cds.exception.SocketClientException;
import gov.va.med.cds.util.MllpUtil;

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

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Properties;


public abstract class AbstractMllpSocketBasedClientProxy
    implements
        NetworkClientProxyInterface

{
    private static Log logger = LogFactory.getLog( AbstractMllpSocketBasedClientProxy.class );

    IBlockingConnectionFactory blockingConnectionFactory = null;

    public GetReadWriteChannel getReadWriteChannel = null;

    public int port;


    public void setGetReadWriteChannel( GetReadWriteChannel getReadWriteChannel )
    {
        this.getReadWriteChannel = getReadWriteChannel;
    }


    public abstract ByteBuffer sendMessage( ByteBuffer message, Properties properties );


    public String sendMessage( String message, Properties properties )
    {
        // convert the message string to a ByteBuffer
        ByteBuffer msgBuffer = ByteBuffer.wrap( message.getBytes() );

        // call sendMessage with a ByteBuffer
        ByteBuffer respBuffer = sendMessage( msgBuffer, properties );

        // Convert the returned ByteBufer to string
        String response = new String( respBuffer.array() );

        // return the string
        return response;
    }


    public ByteBuffer sendMessage( ByteBuffer acknowledgement, String host )
    {
        ByteBuffer response = null;

        SocketChannel socketChannel = null;

        WritableByteChannel wrappedWritableByteChannel = null;

        ReadableByteChannel wrappedReadableByteChannel = null;

        try
        {
            socketChannel = blockingConnectionFactory.getConnection( host, port );

            wrappedWritableByteChannel = getReadWriteChannel.getWriteChannel( socketChannel );

            wrappedWritableByteChannel.write( MllpUtil.encode( acknowledgement, Charset.defaultCharset(), Charset.forName( "ISO-8859-1" ) ) );
                        
            ByteBuffer buffer = ByteBuffer.allocate( 2048 ); // allocate a 2K buffer to store the CA in.
 
            wrappedReadableByteChannel = getReadWriteChannel.getReadChannel( socketChannel );

            wrappedReadableByteChannel.read( buffer );
            
            buffer.flip();

            response = MllpUtil.decode( new ByteBuffer[] { buffer }, Charset.forName( "ISO-8859-1" ), Charset.defaultCharset() );
        }
        catch ( Exception e )
        {
            logger.error( "Socket timeout exception occurred when writing message ", e );
            throw new SocketClientException( ErrorCodeEnum.SOCKET_CLIENT_TIMEOUT_EXCEPTION, e );
        }
        finally
        {
            try
            {
                socketChannel.socket().close();
                socketChannel.close();
            }
            catch ( Exception e )
            {
                logger.error( "Error closing connection.", e );
            }
        }

        return response;
    }


    public void setBlockingConnectionFactory( IBlockingConnectionFactory blockingConnectionFactory )
    {
        this.blockingConnectionFactory = blockingConnectionFactory;
    }


    public void setPort( int port )
    {
        this.port = port;
    }

}
