

package gov.va.med.cds.socket.server;


import static org.junit.Assert.assertTrue;
import gov.va.med.cds.testharness.sql.ExecuteException;
import gov.va.med.cds.testharness.sql.OracleSqlPlusConnectionStringBuilder;
import gov.va.med.cds.testharness.sql.SqlPlusRunner;
import gov.va.med.cds.testharness.xml.Assert;
import gov.va.med.cds.util.MllpUtil;
import gov.va.med.cds.util.StreamUtil;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.core.JdbcTemplate;


public class MultiThreadedLabClientConnector
    extends
        Thread

{

    private static Log LOGGER = LogFactory.getLog( MultiThreadedClientConnector.class );

    private static final String MLFRMD_MSG_FLG_TRUE_RQST_ID = "503163428104LN-SDK-001";
    private static final String WRONG_SNDING_APP_RQST_ID = "503163428104LN-SDK-005";
    private static final String WRONG_SNDING_APP_SITEID_RQST_ID = "503163428104LN-SDK-006";
    private static final String WRONG_WRONG_SITEID_RQST_ID = "503163428104LN-SDK-007";
    private static final String MLFRMD_MSG_FLAG_FALSE_RQST_ID = "503163428104LN-SDK-FLAG-FALSE-008";

    private static final String MLFRMD_MSG_FLG_TRUE_RESPONSE = "MSA^CA^503163428104LN-SDK-001";

    private static final String MLFRMD_MSG_WRONG_SNDING_APP_RESPONSE = "MSA^CR^503163428104LN-SDK-005^Access denied for combination of HDRPRES and LA7HDR for /MSH-3 and /MSH-5";

    private static final String MLFRMD_MSG_WRONG_SNDING_APP_SITE_ID_RESPONSE = "MSA^CR^503163428104LN-SDK-006^Access denied for combination of HDRPRES and LA7HDR for /MSH-3 and /MSH-5";

    private static final String MLFRMD_MSG_WRONG_SITE_ID_RESPONSE = "MSA^CA^503163428104LN-SDK-007";

    private static final String MLFRMD_MSG_FLG_FALSE_RESPONSE = "MSA^CA^503163428104LN-SDK-FLAG-FALSE-008";

    private static final String PORT = "5002";

    private static final String LBPORT = "6031";

    private static ResourceLoader loader = new DefaultResourceLoader();
    private static final int THREAD_SLEEP_TIME = 3000;

    private JdbcTemplate hdrJdbcTemplate;


    @Override
    public void run( )
    {
        LOGGER.info( "Starting : " + Thread.currentThread().getId() );
        MinaClientHandlerDataTest minaClientHandlerDataTest = null;
        String socketAdapterResponse = null;
        try
        {

            dbSetup();

            //Default flag value is true; comment out when the flag is false
            testWithFlagTrue();
            
            //uncomment out when the flag is false
            //testWithFlagFalse();

        }
        catch ( IOException e )
        {
            e.printStackTrace();
        }
        catch ( InterruptedException e )
        {
            e.printStackTrace();
        }
        catch ( Exception ex )
        {
            ex.printStackTrace();
        }
        LOGGER.info( "Ending : " + Thread.currentThread().getId() );
    }


    private void testWithFlagTrue( )
        throws IOException,
            InterruptedException
    {
        MinaClientHandlerDataTest minaClientHandlerDataTest;
        String socketAdapterResponse;
        
        //test with malformed altona hl7 msg - with flag as true in startup.sh
        //minaClientHandlerDataTest = new MinaClientHandlerDataTest( getMessage() );
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(getMessage());
        

        connectToServer( minaClientHandlerDataTest );
        Thread.sleep( THREAD_SLEEP_TIME );
        socketAdapterResponse = minaClientHandlerDataTest.getResponse();
        assertTrue( socketAdapterResponse.contains( MLFRMD_MSG_FLG_TRUE_RESPONSE ) );

        Thread.sleep( THREAD_SLEEP_TIME );

        verifyLabMsgInHdr( MLFRMD_MSG_FLG_TRUE_RQST_ID, 19 );
        verifyLabMsgInAuditClobStr( MLFRMD_MSG_FLG_TRUE_RQST_ID, 38 );
        verifyLabMsgInCdsAppLog( MLFRMD_MSG_FLG_TRUE_RQST_ID, 0 );

        //test with malformed altona hl7 msg with wrong sending app
        //minaClientHandlerDataTest = new MinaClientHandlerDataTest( this.getMessageWithWrongSendingApp() );
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(this.getMessageWithWrongSendingApp());

        connectToServer( minaClientHandlerDataTest );
        Thread.sleep( THREAD_SLEEP_TIME );
        socketAdapterResponse = minaClientHandlerDataTest.getResponse();
        assertTrue( socketAdapterResponse.contains( MLFRMD_MSG_WRONG_SNDING_APP_RESPONSE ) );

        Thread.sleep( THREAD_SLEEP_TIME );

        verifyLabMsgInHdr( WRONG_SNDING_APP_RQST_ID, 0 );
        verifyLabMsgInAuditClobStr( WRONG_SNDING_APP_RQST_ID, 0 );
        verifyLabMsgInCdsAppLog( WRONG_SNDING_APP_RQST_ID, 0 );

        //test with malformed altona hl7 msg with wrong sending app and wrong site id

        //minaClientHandlerDataTest = new MinaClientHandlerDataTest( this.getMessageWithWrongSendingAppNSiteId() );
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(this.getMessageWithWrongSendingAppNSiteId());

        connectToServer( minaClientHandlerDataTest );
        Thread.sleep( THREAD_SLEEP_TIME );
        socketAdapterResponse = minaClientHandlerDataTest.getResponse();
        assertTrue( socketAdapterResponse.contains( MLFRMD_MSG_WRONG_SNDING_APP_SITE_ID_RESPONSE ) );

        Thread.sleep( THREAD_SLEEP_TIME );

        verifyLabMsgInHdr( WRONG_SNDING_APP_SITEID_RQST_ID, 0 );
        verifyLabMsgInAuditClobStr( WRONG_SNDING_APP_SITEID_RQST_ID, 0 );
        verifyLabMsgInCdsAppLog( WRONG_SNDING_APP_SITEID_RQST_ID, 0 );

        //test with malformed altona hl7 msg with wrong site id

        //minaClientHandlerDataTest = new MinaClientHandlerDataTest( this.getMessageWithWrongSiteId() );
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(this.getMessageWithWrongSiteId());

        connectToServer( minaClientHandlerDataTest );
        Thread.sleep( THREAD_SLEEP_TIME );
        socketAdapterResponse = minaClientHandlerDataTest.getResponse();
        assertTrue( socketAdapterResponse.contains( MLFRMD_MSG_WRONG_SITE_ID_RESPONSE ) );

        Thread.sleep( THREAD_SLEEP_TIME );

        verifyLabMsgInHdr( WRONG_WRONG_SITEID_RQST_ID, 0 );
        verifyLabMsgInAuditClobStr( WRONG_WRONG_SITEID_RQST_ID, 1 );
        verifyLabMsgInCdsAppLog( WRONG_WRONG_SITEID_RQST_ID, 4 );
    }


    private void testWithFlagFalse( )
        throws IOException,
            InterruptedException
    {
        MinaClientHandlerDataTest minaClientHandlerDataTest;
        String socketAdapterResponse;

        //test with malformed altona hl7 msg - when the flag is false in startup.sh
        //minaClientHandlerDataTest = new MinaClientHandlerDataTest( getFlagFalseMessage() );
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(this.getFlagFalseMessage());

        connectToServer( minaClientHandlerDataTest );
        Thread.sleep( THREAD_SLEEP_TIME );
        socketAdapterResponse = minaClientHandlerDataTest.getResponse();
        assertTrue( socketAdapterResponse.contains( MLFRMD_MSG_FLG_FALSE_RESPONSE ) );
        
        Thread.sleep( THREAD_SLEEP_TIME );

        verifyLabMsgInHdr( MLFRMD_MSG_FLAG_FALSE_RQST_ID, 0 );
        verifyLabMsgInAuditClobStr( MLFRMD_MSG_FLAG_FALSE_RQST_ID, 1 );
        verifyLabMsgInCdsAppLog( MLFRMD_MSG_FLAG_FALSE_RQST_ID, 4 );

        //test with malformed altona hl7 msg with wrong sending app
        //minaClientHandlerDataTest = new MinaClientHandlerDataTest( this.getMessageWithWrongSendingApp() );
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(this.getMessageWithWrongSendingApp());

        connectToServer( minaClientHandlerDataTest );
        Thread.sleep( THREAD_SLEEP_TIME );
        socketAdapterResponse = minaClientHandlerDataTest.getResponse();
        assertTrue( socketAdapterResponse.contains( MLFRMD_MSG_WRONG_SNDING_APP_RESPONSE ) );

        Thread.sleep( THREAD_SLEEP_TIME );

        verifyLabMsgInHdr( WRONG_SNDING_APP_RQST_ID, 0 );
        verifyLabMsgInAuditClobStr( WRONG_SNDING_APP_RQST_ID, 0 );
        verifyLabMsgInCdsAppLog( WRONG_SNDING_APP_RQST_ID, 0 );

        //test with malformed altona hl7 msg with wrong sending app and wrong site id

        //minaClientHandlerDataTest = new MinaClientHandlerDataTest( this.getMessageWithWrongSendingAppNSiteId() );
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(this.getMessageWithWrongSendingAppNSiteId());

        connectToServer( minaClientHandlerDataTest );
        Thread.sleep( THREAD_SLEEP_TIME );
        socketAdapterResponse = minaClientHandlerDataTest.getResponse();
        assertTrue( socketAdapterResponse.contains( MLFRMD_MSG_WRONG_SNDING_APP_SITE_ID_RESPONSE ) );

        Thread.sleep( THREAD_SLEEP_TIME );

        verifyLabMsgInHdr( WRONG_SNDING_APP_SITEID_RQST_ID, 0 );
        verifyLabMsgInAuditClobStr( WRONG_SNDING_APP_SITEID_RQST_ID, 0 );
        verifyLabMsgInCdsAppLog( WRONG_SNDING_APP_SITEID_RQST_ID, 0 );

        //test with malformed altona hl7 msg with wrong site id

        //minaClientHandlerDataTest = new MinaClientHandlerDataTest( this.getMessageWithWrongSiteId() );
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(this.getMessageWithWrongSendingAppNSiteId());

        connectToServer( minaClientHandlerDataTest );
        Thread.sleep( THREAD_SLEEP_TIME );
        socketAdapterResponse = minaClientHandlerDataTest.getResponse();
        assertTrue( socketAdapterResponse.contains( MLFRMD_MSG_WRONG_SITE_ID_RESPONSE ) );

        Thread.sleep( THREAD_SLEEP_TIME );
        
        verifyLabMsgInHdr( WRONG_WRONG_SITEID_RQST_ID, 0 );
        verifyLabMsgInAuditClobStr( WRONG_WRONG_SITEID_RQST_ID, 1 );
        verifyLabMsgInCdsAppLog( WRONG_WRONG_SITEID_RQST_ID, 4 );
    }


    private void dbSetup( )
        throws ExecuteException
    {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:gov/va/med/cds/socket/config/hdr2DataSourceContext.xml" );

        OracleSqlPlusConnectionStringBuilder labSchemaOracleSqlPlusConnectionStringBuilder = ( OracleSqlPlusConnectionStringBuilder )ctx
                        .getBean( "labSchemaOracleSqlPlusConnectionStringBuilder" );
        OracleSqlPlusConnectionStringBuilder monitorSchemaOracleSqlPlusConnectionStringBuilder = ( OracleSqlPlusConnectionStringBuilder )ctx
                        .getBean( "monitorSchemaOracleSqlPlusConnectionStringBuilder" );

        SqlPlusRunner sqlPlusRunner = ( SqlPlusRunner )ctx.getBean( "sqlPlusRunner" );
        String hdrSchemaName = ( String )ctx.getBean( "hdrSchemaName" );
        hdrJdbcTemplate = ( JdbcTemplate )ctx.getBean( "hdrJdbcTemplate" );

        sqlPlusRunner.execute( monitorSchemaOracleSqlPlusConnectionStringBuilder.getConnectionString(),
                        "src/test/resources/sql/grant_insert_permission_audit_clob_str_hdr.sql", hdrSchemaName );

        sqlPlusRunner.execute( labSchemaOracleSqlPlusConnectionStringBuilder.getConnectionString(),
                        "src/test/resources/sql/grant_insert_permission_lab_hdr.sql", hdrSchemaName );

        deleteLabMsgInCdsAppLogr( MLFRMD_MSG_FLG_TRUE_RQST_ID );
        deleteLabMsgInCdsAppLogr( WRONG_SNDING_APP_RQST_ID );
        deleteLabMsgInCdsAppLogr( WRONG_SNDING_APP_SITEID_RQST_ID );
        deleteLabMsgInCdsAppLogr( WRONG_WRONG_SITEID_RQST_ID );
        deleteLabMsgInCdsAppLogr( MLFRMD_MSG_FLAG_FALSE_RQST_ID );

        deleteLabMsgInAuditClobStr( MLFRMD_MSG_FLG_TRUE_RQST_ID );
        deleteLabMsgInAuditClobStr( WRONG_SNDING_APP_RQST_ID );
        deleteLabMsgInAuditClobStr( WRONG_SNDING_APP_SITEID_RQST_ID );
        deleteLabMsgInAuditClobStr( WRONG_WRONG_SITEID_RQST_ID );
        deleteLabMsgInAuditClobStr( MLFRMD_MSG_FLAG_FALSE_RQST_ID );

    }


    private void verifyLabMsgInHdr( String requestId, int cnt )
    {

        StringBuilder sqlBuilder = new StringBuilder();

        sqlBuilder.append( "SELECT COUNT(*) FROM LAB_TEST_PRM " );
        sqlBuilder.append( " WHERE REQUEST_ID = '" );
        sqlBuilder.append( requestId );
        sqlBuilder.append( "'" );

        int retVal = hdrJdbcTemplate.queryForObject( sqlBuilder.toString(), Integer.class );

        Assert.assertTrue( retVal == cnt );
    }


    private void verifyLabMsgInAuditClobStr( String requestId, int cnt )
    {

        StringBuilder sqlBuilder = new StringBuilder();

        sqlBuilder.append( "SELECT COUNT(*) FROM AUDIT_CLOB_STR " );
        sqlBuilder.append( " WHERE REQUEST_ID = '" );
        sqlBuilder.append( requestId );
        sqlBuilder.append( "'" );

        int retVal = hdrJdbcTemplate.queryForObject( sqlBuilder.toString(), Integer.class );

        Assert.assertTrue( retVal >= cnt );

    }


    private void verifyLabMsgInCdsAppLog( String requestId, int cnt )
    {

        StringBuilder sqlBuilder = new StringBuilder();

        sqlBuilder.append( "SELECT COUNT(*) FROM CDS_APPLICATION_LOG " );
        sqlBuilder.append( " WHERE REQUEST_ID = '" );
        sqlBuilder.append( requestId );
        sqlBuilder.append( "'" );

        int retVal = hdrJdbcTemplate.queryForObject( sqlBuilder.toString(), Integer.class );

        Assert.assertTrue( retVal >= cnt );

    }
 

    private void deleteLabMsgInAuditClobStr( String requestId )
    {

        StringBuilder sqlBuilder = new StringBuilder();

        sqlBuilder.append( "DELETE FROM AUDIT_CLOB_STR " );
        sqlBuilder.append( " WHERE REQUEST_ID = '" );
        sqlBuilder.append( requestId );
        sqlBuilder.append( "'" );

        this.hdrJdbcTemplate.update( sqlBuilder.toString() );

    }


    private void deleteLabMsgInCdsAppLogr( String requestId )
    {

        StringBuilder sqlBuilder = new StringBuilder();

        sqlBuilder.append( "DELETE FROM CDS_APPLICATION_LOG " );
        sqlBuilder.append( " WHERE REQUEST_ID = '" );
        sqlBuilder.append( requestId );
        sqlBuilder.append( "'" );

        this.hdrJdbcTemplate.update( sqlBuilder.toString() );

    }


    private IoBuffer getFlagFalseMessage( )
        throws IOException
    {
        String hl7Er7Message = null;

        Resource resource = loader.getResource( "classpath:gov/va/med/cds/socket/server/labForFalseFlag.er7" );

        hl7Er7Message = StreamUtil.streamToString( resource.getInputStream() );

        ByteBuffer buffer = encodeToMllp( hl7Er7Message );

        IoBuffer messageIoBuffer = IoBuffer.allocate( buffer.limit() );

        int index = 0;

        while ( buffer.hasRemaining() && ( index < buffer.limit() ) )
        {
            messageIoBuffer.put( index, buffer.get( index ) );

            index++ ;
        }

        return messageIoBuffer;
    }


    private IoBuffer getMessage( )
        throws IOException
    {
        String hl7Er7Message = null;

        Resource resource = loader.getResource( "classpath:gov/va/med/cds/socket/server/lab.er7" );

        hl7Er7Message = StreamUtil.streamToString( resource.getInputStream() );

        ByteBuffer buffer = encodeToMllp( hl7Er7Message );

        IoBuffer messageIoBuffer = IoBuffer.allocate( buffer.limit() );
        
        int index = 0;

        while ( buffer.hasRemaining() && ( index < buffer.limit() ) )
        {
            messageIoBuffer.put( index, buffer.get( index ) );

            index++ ;
        }

        return messageIoBuffer;
    }


    private IoBuffer getMessageWithWrongSiteId( )
        throws IOException
    {
        String hl7Er7Message = null;

        Resource resource = loader.getResource( "classpath:gov/va/med/cds/socket/server/lab_wrongSiteId.er7" );

        hl7Er7Message = StreamUtil.streamToString( resource.getInputStream() );

        ByteBuffer buffer = encodeToMllp( hl7Er7Message );

        IoBuffer messageIoBuffer = IoBuffer.allocate( buffer.limit() );

        int index = 0;

        while ( buffer.hasRemaining() && ( index < buffer.limit() ) )
        {
            messageIoBuffer.put( index, buffer.get( index ) );

            index++ ;
        }

        return messageIoBuffer;
    }


    private IoBuffer getMessageWithWrongSendingApp( )
        throws IOException
    {
        String hl7Er7Message = null;

        Resource resource = loader.getResource( "classpath:gov/va/med/cds/socket/server/lab_wrongSendingApp.er7" );

        hl7Er7Message = StreamUtil.streamToString( resource.getInputStream() );

        ByteBuffer buffer = encodeToMllp( hl7Er7Message );

        IoBuffer messageIoBuffer = IoBuffer.allocate( buffer.limit() );

        int index = 0;

        while ( buffer.hasRemaining() && ( index < buffer.limit() ) )
        {
            messageIoBuffer.put( index, buffer.get( index ) );

            index++ ;
        }

        return messageIoBuffer;
    }


    private IoBuffer getMessageWithWrongSendingAppNSiteId( )
        throws IOException
    {
        String hl7Er7Message = null;

        Resource resource = loader.getResource( "classpath:gov/va/med/cds/socket/server/lab_wrongSendingAppNSiteid.er7" );

        hl7Er7Message = StreamUtil.streamToString( resource.getInputStream() );

        ByteBuffer buffer = encodeToMllp( hl7Er7Message );

        IoBuffer messageIoBuffer = IoBuffer.allocate( buffer.limit() );

        int index = 0;

        while ( buffer.hasRemaining() && ( index < buffer.limit() ) )
        {
            messageIoBuffer.put( index, buffer.get( index ) );

            index++ ;
        }

        return messageIoBuffer;
    }


    private ByteBuffer encodeToMllp( String hl7Er7Message )
    {
        ByteBuffer buffer = ByteBuffer.allocate( hl7Er7Message.toString().getBytes().length );

        buffer.put( hl7Er7Message.toString().getBytes() );

        return MllpUtil.encode( buffer, Charset.forName( "ISO-8859-1" ), Charset.defaultCharset() );
    }


    private String getHl7er7( )
    {

        String hl7er7Record = null;

        BufferedReader bufferedReader = null;

        StringBuilder record = new StringBuilder();

        try
        {
            bufferedReader = new BufferedReader( new FileReader( "src/test/java/gov/va/med/cds/socket/server/rx_002.er7" ) );

            hl7er7Record = bufferedReader.readLine();

            while ( null != hl7er7Record )
            {
                record.append( hl7er7Record );

                hl7er7Record = bufferedReader.readLine();

            }

            bufferedReader.close();
        }
        catch ( FileNotFoundException e )
        {
            e.printStackTrace();
        }
        catch ( IOException e )
        {
            e.printStackTrace();
        }

        return new String( encodeToMllp( record.toString() ).array() );
    }


    private void connectToServer( MinaClientHandlerDataTest minaClientHandlerDataTest )
        throws InterruptedException,
            IOException
    {
        IoConnector connector = new NioSocketConnector();
        connector.getFilterChain().addLast( "logger", new LoggingFilter() );
        connector.setHandler( minaClientHandlerDataTest );
        connector.connect( new InetSocketAddress( Integer.parseInt( "5001" ) ) );
        connector.setConnectTimeoutMillis( 1000 );
    }


    public static void main( String args[] )
    {

        MultiThreadedLabClientConnector multiThreadedClientConnector = new MultiThreadedLabClientConnector();

        multiThreadedClientConnector.start();

    }

}
