

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 MultiThreadedHTHClientConnector
    extends
        Thread

{

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

    private static final String MLFRMD_MSG_RQST_ID = "201503261012T-SDK-001";
    private static final String WRONG_ICN_RQST_ID = "201503261012T-SDK-NO-ICN";
    private static final String WRONG_SITEID_RQST_ID = "201503261012T-SDK-NO-FACILITY-ID";
    private static final String WRONG_WRONG_QUSTION_RQST_ID = "201503261012T-SDK-NO_QUESTION_NBR";
    private static final String MLFRMD_MSG_ANS_RQST_ID = "201503261012T-SDK-NO_ANS";

    private static final String MLFRMD_MSG_RESPONSE = "MSA^CA^";

    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();

            testSurvey();
            testDMP();

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


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

        //test with good  hl7 msg
 //       minaClientHandlerDataTest = new MinaClientHandlerDataTest( this.getMessage() );
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(getMessage());
        
        connectToServer( minaClientHandlerDataTest );
        Thread.sleep( THREAD_SLEEP_TIME );
        socketAdapterResponse = minaClientHandlerDataTest.getResponse();
        assertTrue( socketAdapterResponse.contains( MLFRMD_MSG_RESPONSE ) );

        Thread.sleep( THREAD_SLEEP_TIME );

        verifyHTHMsgInHdr( MLFRMD_MSG_RQST_ID, 1 );
        verifyHTHMsgInAuditClobStr( MLFRMD_MSG_RQST_ID, 1 );
        verifyHTHMsgInCdsAppLog( MLFRMD_MSG_RQST_ID, 0 );

        //test with malformed  hl7 msg with wrong sending app and no site id
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(this.getMessageWithNoFacilityId());
        
       // minaClientHandlerDataTest = new MinaClientHandlerDataTest( this.getMessageWithNoFacilityId() );

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

        Thread.sleep( THREAD_SLEEP_TIME );

        verifyHTHMsgInHdr( WRONG_SITEID_RQST_ID, 0 );
        verifyHTHMsgInAuditClobStr( WRONG_SITEID_RQST_ID, 0 );
        verifyHTHMsgInCdsAppLog( WRONG_SITEID_RQST_ID, 2 );

        //test with malformed  hl7 msg with no icn
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(this.getMessageWithNoPid());
        
        //minaClientHandlerDataTest = new MinaClientHandlerDataTest( this.getMessageWithNoPid() );

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

        Thread.sleep( THREAD_SLEEP_TIME );

        verifyHTHMsgInHdr( WRONG_ICN_RQST_ID, 0 );
        verifyHTHMsgInAuditClobStr( WRONG_ICN_RQST_ID, 0 );
        verifyHTHMsgInCdsAppLog( WRONG_ICN_RQST_ID, 4 );

        //test with malformed  hl7 msg with no question
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(this.getMessageWithNoQuestion());
        
        //minaClientHandlerDataTest = new MinaClientHandlerDataTest( this.getMessageWithNoQuestion() );

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

        Thread.sleep( THREAD_SLEEP_TIME );

        verifyHTHMsgInHdr( WRONG_WRONG_QUSTION_RQST_ID, 0 );
        verifyHTHMsgInAuditClobStr( WRONG_WRONG_QUSTION_RQST_ID, 0 );
        verifyHTHMsgInCdsAppLog( WRONG_WRONG_QUSTION_RQST_ID, 4 );

        //test with malformed  hl7 msg with no answer

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

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

        Thread.sleep( THREAD_SLEEP_TIME );

        verifyHTHMsgInHdr( MLFRMD_MSG_ANS_RQST_ID, 0 );
        verifyHTHMsgInAuditClobStr( MLFRMD_MSG_ANS_RQST_ID, 0 );
        verifyHTHMsgInCdsAppLog( MLFRMD_MSG_ANS_RQST_ID, 4 );
    }


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

        //test with malformed msg
        minaClientHandlerDataTest = new MinaClientHandlerDataTest();
        minaClientHandlerDataTest.setValues(this.getDMPMessage());
        
        //minaClientHandlerDataTest = new MinaClientHandlerDataTest( this.getDMPMessage() );

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

        Thread.sleep( THREAD_SLEEP_TIME );

        verifyDMPHdr( MLFRMD_MSG_RQST_ID, 0 );
        verifyHTHMsgInAuditClobStr( MLFRMD_MSG_RQST_ID, 0 );
        verifyHTHMsgInCdsAppLog( MLFRMD_MSG_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 );

        deleteHTHMsgInCdsAppLogr( MLFRMD_MSG_RQST_ID );
        deleteHTHMsgInCdsAppLogr( WRONG_ICN_RQST_ID );
        deleteHTHMsgInCdsAppLogr( WRONG_SITEID_RQST_ID );
        deleteHTHMsgInCdsAppLogr( WRONG_WRONG_QUSTION_RQST_ID );
        deleteHTHMsgInCdsAppLogr( MLFRMD_MSG_ANS_RQST_ID );

        deleteHTHMsgInAuditClobStr( MLFRMD_MSG_RQST_ID );
        deleteHTHMsgInAuditClobStr( WRONG_ICN_RQST_ID );
        deleteHTHMsgInAuditClobStr( WRONG_SITEID_RQST_ID );
        deleteHTHMsgInAuditClobStr( WRONG_WRONG_QUSTION_RQST_ID );
        deleteHTHMsgInAuditClobStr( MLFRMD_MSG_ANS_RQST_ID );
    }


    private void verifyHTHMsgInHdr( String requestId, int cnt )
    {

        StringBuilder sqlBuilder = new StringBuilder();

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

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

        Assert.assertTrue( retVal == cnt );
    }


    private void verifyDMPHdr( String requestId, int cnt )
    {

        StringBuilder sqlBuilder = new StringBuilder();

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

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

        Assert.assertTrue( retVal == cnt );
    }


    private void verifyHTHMsgInAuditClobStr( 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 verifyHTHMsgInCdsAppLog( 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 verifyHTHMsgInAckLog( String requestId, int cnt )
    {

        StringBuilder sqlBuilder = new StringBuilder();

        sqlBuilder.append( "SELECT COUNT(*) FROM AUDIT_HL7_AA_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 deleteHTHMsgInAuditClobStr( 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 deleteHTHMsgInCdsAppLogr( 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/hth.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 getDMPMessage( )
        throws IOException
    {
        String hl7Er7Message = null;

        Resource resource = loader.getResource( "classpath:gov/va/med/cds/socket/server/dmp.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 getMessageWithNoAns( )
        throws IOException
    {
        String hl7Er7Message = null;

        Resource resource = loader.getResource( "classpath:gov/va/med/cds/socket/server/hth_no_ans.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 getMessageWithNoQuestion( )
        throws IOException
    {
        String hl7Er7Message = null;

        Resource resource = loader.getResource( "classpath:gov/va/med/cds/socket/server/hth_no_question.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 getMessageWithNoFacilityId( )
        throws IOException
    {
        String hl7Er7Message = null;

        Resource resource = loader.getResource( "classpath:gov/va/med/cds/socket/server/hth_no_facilityId.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 getMessageWithNoPid( )
        throws IOException
    {
        String hl7Er7Message = null;

        Resource resource = loader.getResource( "classpath:gov/va/med/cds/socket/server/hth_no_pid.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[] )
    {

        MultiThreadedHTHClientConnector multiThreadedClientConnector = new MultiThreadedHTHClientConnector();

        multiThreadedClientConnector.start();

    }

}
