package gov.va.med.esr.messaging.xml;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.person.Name;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.id.PersonIdEntityKey;
import gov.va.med.esr.messaging.constants.HL7Constants;
import gov.va.med.esr.messaging.service.MessageProcessServiceUtil;
import gov.va.med.esr.messaging.util.AbstractMessagingTest;
import gov.va.med.esr.service.PersonService;

import gov.va.med.fw.hl7.BatchMessage;
import gov.va.med.fw.hl7.InvalidMessageException;
import gov.va.med.fw.hl7.Message;
import gov.va.med.fw.util.StopWatchLogger;
import gov.va.med.fw.util.builder.BuilderException;

/**
 * XMLSchemaTest created on Dec 6, 2006
 * @author DNS   LEV
 */
public class XMLSchemaTest extends AbstractMessagingTest {
	protected static final String BAD_ORUZ07_FILE = "BADORUZ07.txt";
	 // The number of threads (i.e. simulatenous calls to PSIM) to use
	   public static final int NUM_THREADS = 100;

	   // Approximate number of seconds until we timeout the JUnit.  Make sure this value is high enough
	   // to let the number of threads above finish successfully.
	   public static final int TIMEOUT_SECS = 1800;

	   // The approximate number of seconds to wait before displaying additional debugging output
	   // regarding the threads
	   public static final int DEBUG_INFO_SECS = 15;

	   // Number of milliseconds to wait in between starting threads
	   public static final int THREAD_START_DELAY_MILLIS = 50;
	
   /**
    * A default constructor
    * @param name
    */
   public XMLSchemaTest(String name) {
      super(name);
   }

   /**
    * Add HL7Schema web folder as class folder to
    * make this work. Make sure no BHS/BTS
    * 
    * @throws Exception
    */
   public void testTransform() throws Exception {
	   String msg = createTestMessageFromFile( ORUZ07_FILE );
	   XmlFormatter formatter = (XmlFormatter)applicationContext.getBean( "inbound.oruz07.xmlFormatter" );
	   String output = formatter.build( msg );
	   logger.info( "Transformed and validated message " );
	   logger.info( output );
   }
   
   public void testTransformOfBadZ07() throws Exception {
	   String msg = createTestMessageFromFile( BAD_ORUZ07_FILE );
	   XmlFormatter formatter = (XmlFormatter)applicationContext.getBean( "inbound.oruz07.xmlFormatter" );
	   String output = formatter.build( msg );
	   logger.info( "Transformed and validated message " );
	   logger.info( output );
   }   
   
  
   public void testMultipleFormattingOperations() throws Exception
   {
       Iterator iter = null;
       ArrayList<Thread> threadList = new ArrayList<Thread>();
	   XmlFormatter formatter = (XmlFormatter)applicationContext.getBean( "inbound.oruz07.xmlFormatter" );
	   
       // Spawn a bunch of threads for testing the getting of a valie Person
       for (int i = 0; i < NUM_THREADS; i++) {
    	   Thread formatterThread = null;
    	   if ( (i % 2) != 0) {
    		   BatchMessage batchMessage = new BatchMessage(createTestMessageFromFile(ORUZ07_FILE),
    				   HL7Constants.ORUZ07);
    		   List messages = batchMessage.getMessages();    	    		   
    		   for (Iterator itr = messages.iterator(); itr.hasNext();)
    		   {
    			   Message message = (Message) itr.next();
    			   formatterThread = new TestXmlFormatterThread(formatter, logger, message, "GOOD MESSAGE");
    		   }
    	   }
    	   else {
    		   BatchMessage batchMessage = new BatchMessage(createTestMessageFromFile(BAD_ORUZ07_FILE),
    				   HL7Constants.ORUZ07);
    		   List messages = batchMessage.getMessages();    	    		   
    		   for (Iterator itr = messages.iterator(); itr.hasNext();)
    		   {
    			   Message message = (Message) itr.next();
    			   formatterThread = new TestXmlFormatterThread(formatter, logger, message, "BAD MESSAGE");
    		   }    		   
    	   }
    	   threadList.add(formatterThread);
    	   if (formatterThread != null) {
    		   formatterThread.start();
    	   }
    	   Thread.sleep(THREAD_START_DELAY_MILLIS);
       }
       boolean finished = false;
       int timeoutCount = 0;
       int threadsNotCompleteCount = 0;
       
       while (!finished) {
			// // Increment the timeout count in seconds
			timeoutCount++;
			//
			threadsNotCompleteCount = 0;
			boolean threadsCompleted = true;
			for (Iterator iterator = threadList.iterator(); iterator.hasNext();) {
				TestXmlFormatterThread thread = (TestXmlFormatterThread) iterator
						.next();
				if (thread.isAlive()) {
					threadsCompleted = false;
					threadsNotCompleteCount++;
				}
			}

			// If all the threads are completed, then we are finished
			if (threadsCompleted) {
				finished = true;
			}

			if (!finished) {
				// Timeout the JUnit if we have waited too long
				if (timeoutCount >= TIMEOUT_SECS) {
					finished = true;
				}

				// Display summary debugging information every so often
				if (timeoutCount % DEBUG_INFO_SECS == 0) {
					displaySummaryInfo(timeoutCount, threadList.size(),
							threadsNotCompleteCount);
				}

				// Sleep for 1 second before we try again
				try {
					Thread.sleep(1000);
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		}

       // If we get here, the test completed successfully
   }


   protected void displaySummaryInfo(int totalSeconds, int totalThreads, int threadsNotCompleted)
   {
       System.out.println("Statistics after " + totalSeconds + " seconds.");
       int threadsComplete = totalThreads - threadsNotCompleted;
       System.out.println("Total threads: " + totalThreads);
       System.out.println("Threads complete: " + threadsComplete);
       System.out.println("Threads not complete: " + threadsNotCompleted);
       System.out.println(
           "Percent of threads complete: " + ((int)((float)threadsComplete / (float)totalThreads * 100)) + "%");
       System.out.println();
   }
}

class TestXmlFormatterThread extends Thread
{
	private XmlFormatter formatter = null;
	private Message message = null;
	private String context = null;	
	private Log logger = null;

	public TestXmlFormatterThread(XmlFormatter formatter, Log logger, Message message, String context)
	{
		this.formatter = formatter;
		this.message = message;
		this.logger = logger;
		this.context = context;
	}

	protected final String formatMessage(Message message) throws BuilderException {

		StopWatchLogger watch = null;
		if( logger.isDebugEnabled() ) {
			watch = new StopWatchLogger("AbstractMessagingService formatMessage");
			if (watch != null) {
				watch.start();
			}
		}

		String formattedMesssage = doGetFormattedMessageBody(message);

		if( logger.isDebugEnabled() ) {
			String messageId = null;
			String messageType = null;
			try {
				messageId = message.getMessageID();
				messageType = message.getType();
			}
			catch( InvalidMessageException e ) {}
			if (watch != null) {
				watch.stopAndLog( "Total time to format and validate message with message Id "
						+ messageId + " and message type " + messageType );
			}
		}
		return formattedMesssage;
	}
	protected String doGetFormattedMessageBody(Message message) throws BuilderException {
		return MessageProcessServiceUtil.getFormattedMessageBody( 
				formatter.build(message.getMessageData() ) );
	}
	@Override
	public void run()
	{
		try  {
			System.out.println("Initiating call ...");
			String output = this.formatMessage(message);
			logger.info( "Transformed and validated message " );
			logger.info( output );

			System.out.println("no error for=" + context);//			System.out.println("PersonService call to re-retrieve the person returned.");
		} catch (Exception ex) 		{
			System.out.println("got error for=" + context);			
		}
	}   
}