using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Web.Services.Protocols;
using RetrieveResultsSampleCodeCS.RetrieveResultsWebService;

public class RetrieveResultsSample
{

	// Constants that define the three result service types.
	private const string RESULT_SERVICE_TYPE_HL7 = "HL7";
	private const string RESULT_SERVICE_TYPE_PRINTABLE = "Printable";
	private const string RESULT_SERVICE_TYPE_OBSERVATION = "Observation";

    // Constants that define request parameter names.
	private const string REQUEST_PARAMETER_NAME_MAX_MESSAGES = "maxMessages";
	private const string REQUEST_PARAMETER_NAME_MESSAGE_CONTROL_ID = "messageControlId";
	private const string REQUEST_PARAMETER_NAME_PROVIDER_ACCT_ID = "providerAcctId";

	// The HL7/Observation ACK message template - The following "template" is
	// used in constructing an ACK request message. After calling getResults()
	// for HL7 or Observation results, you will acknowledge receipt of the
	// results by calling acknowledgeResults(). The acknowledgeResults() request
	// contains an acknowledge message constructed from the HL7 message
	// retrieved.
	// 
	// 0 = sending application (field MSH.03) 
	// 1 = sending facility (field MSH.04) 
	// 2 = receiving application (field MSH.05) 
	// 3 = receiving facility (field MSH.06) 
	// 4 = date time (field MSH.07) 
	// 5 = message control id (field MSH.10) 
	// 6 = hl7 version (field MSH.12)
	// 7 = message control id of message (field MSA.02)

	const string ACK_MESSAGE_FORMAT = "MSH|^~\\&|{0}|{1}|{2}|{3}|{4:yyyyMMddHHmm}||ACK|{5:#####}|D|{6}\rMSA|CA|{7}\r";

    // USERNAME designates the Hub user name used to
	// authenticate to the Hub web service
    private const string USERNAME = "HORI3670test";
	
    // PASSWORD designates the password associated with
	// the Hub user when authenticating to the web service
    private const string PASSWORD = "03hori3670";
	
    // WEB_SERVICE_URL designates the end point used to
	// connect to the Hub's Printbale Results web service
	private const string WEB_SERVICE_URL = "https://cert.hub.care360.com/results/retrieval/service";

    // Max number of messages to retrieve
	private const string MAX_MESSAGES = "5";

	// chose "observation", "printable", or "hl7" using the constants above
	private string resultServiceType = RESULT_SERVICE_TYPE_PRINTABLE;

	private RetrieveResultsService proxy;

	public RetrieveResultsSample()
	{
		try {
			proxy = getWebServiceObject();

			// Perform a new results search using all provider accounts.  
			// This is the most common scenario for developing vendors.
			// Return max of 5 results.
			Console.WriteLine("Performing new results search...");
			getResults();
			Console.WriteLine("Done with new results search.");

		} catch (Exception ex) {
			Console.WriteLine(ex.Message);

		}
	}

	private RetrieveResultsService getWebServiceObject()
	{
		RetrieveResultsService webService = default(RetrieveResultsService);

		try {
			//-------------------------------------------------------
			//STEP 1: CREATE WEB SERVICE REFERENCE
			//-------------------------------------------------------
			webService = new RetrieveResultsService();
		} catch (Exception except) {
			Console.Write(except.StackTrace);
			webService = null;
			throw except;
		}


		if ((webService != null)) {
			//------------------------------------------------------------------
			//STEP 2: SET WEB SERVICE URL
			//------------------------------------------------------------------
			if (WEB_SERVICE_URL.Length > 0) {
				webService.Url = WEB_SERVICE_URL;
			}

			//------------------------------------------------------------------
			//STEP 3: CREATE CREDENTIALS OBJECT CONTAINING USERNAME AND PASSWORD
			//------------------------------------------------------------------
			webService.Credentials = new NetworkCredential(USERNAME, PASSWORD);

			// By default the client will attempt to call the web service without 
			// sending the credentials. If the server returns a 401 HTTP response, 
			// the client makes the call again with the credentials. If we set 
			// PreAuthenticate to True, the client will include the credentials on 
			// the initial call and eliminate this extra processing.
			//
			// This should probably be set in the web service object's constructor,
			// but has been included here to demonstrate.
			webService.PreAuthenticate = true;
		}

		return webService;
	}

    /**
     * The getResults method will:
     * 
     * 1. On the first pass, create a RetrieveResultsRequest object and call 
     *    getResults() to get the RetrieveResultsResponse object containing 
     *    the first batch of results. 
     * 2. Generate an acknowledgment message for the returned results (if any). 
     * 3. Acknowledge the results with the acknowledgeResults method. 
     * 4. Output the number of results acknowledged on this pass. 
     * 5. If more results are available, call getResults() again. Repeat from step 2.
     */
	private void getResults()
	{
		bool bInitialRequest = true;
		bool bContinue = true;
		int numResults = 0;
		string requestID = "";
		retrieveResultsResponse myRetrieveResponse = null;
		retrieveResultsRequest myRetrieveResultsRequest = null;

		while (bContinue) {
			if (bInitialRequest) {
				bInitialRequest = false;
				Console.WriteLine("Making a first pass...");

				//------------------------------------------------------------------
				//STEP 1: GENERATE RESULTS REQUEST OBJECT
				//------------------------------------------------------------------
				myRetrieveResultsRequest = buildResultsRequest();

				//--------------------------------------------------------------
				//STEP 2: SUBMIT REQUEST TO THE HUB
				//--------------------------------------------------------------
				myRetrieveResponse = proxy.getResults(myRetrieveResultsRequest);

				if (myRetrieveResponse == null) {
					Console.WriteLine("Failed to properly call getResults WebService method.");
				}
			} else {
				// If there are more results available, fetch more.
				Console.WriteLine("Making another pass...");
				myRetrieveResponse = proxy.getResults(myRetrieveResultsRequest);

				if (myRetrieveResponse == null) {
					Console.WriteLine("Failed to properly call getResults WebService method.");
				}
			}

			requestID = myRetrieveResponse.requestId;
			Console.WriteLine("Request ID: " + requestID);

			if (myRetrieveResponse.results == null) {
				// empty array, no HL7 messages returned
				Console.WriteLine("# of Messages on Pass: 0");
				bContinue = false;
			} else {
				numResults = numResults + myRetrieveResponse.results.Length;
				Console.Write("# of Messages on Pass: ");
				Console.WriteLine(myRetrieveResponse.results.Length);

				// send Acks
				if (numResults > 0) {

                    // save PDF files (optional)
                    writeResultDataToFile(myRetrieveResponse.results); 

					// STEP 3: BUILD ACKNOWLEDGEMENT
					ackResultsRequest acks = buildAcknowledgment(myRetrieveResponse);

					// STEP 4: SEND ACKNOWLEDGEMENT
					proxy.acknowledgeResults(acks);


					// STEP 5: Print the number of messages ACKed
					Console.WriteLine("# of Messages acknowledged: " + numResults.ToString());


					bContinue = myRetrieveResponse.isMore;
				} else {
					bContinue = false;
				}
			}
		}

		Console.WriteLine("Total results found: " + numResults.ToString());
	}

	// The getWebServicePrintableMessage function will take the parameters for a results request and gernerate the object
	// to pass to the web method
	private retrieveResultsRequest buildResultsRequest()
	{
		retrieveResultsRequest uReq = new retrieveResultsRequest();

		//------------------------------------------------------------------
		// STEP 1: SET UP REQUEST PARAMETERS
		//------------------------------------------------------------------
		if ((MAX_MESSAGES != null)) {
			requestParameter[] requestParams = new requestParameter[1];
			requestParameter requestParameter = new requestParameter();
			requestParameter.parameterName = REQUEST_PARAMETER_NAME_MAX_MESSAGES;
			requestParameter.parameterValue = MAX_MESSAGES;
			requestParams[0] = requestParameter;
			uReq.requestParameters = requestParams;
		}

		//------------------------------------------------------------------
		// STEP 2: SET SERVICE TYPE (observation, hl7, printable)
		//------------------------------------------------------------------
		uReq.resultServiceType = resultServiceType;

		return uReq;
	}

    /**
     * Constructs a message to acknowledge a set of results.
     */
	private ackResultsRequest buildAcknowledgment(retrieveResultsResponse response)
	{

		ackResultsRequest acknowledgment = new ackResultsRequest();

		acknowledgment.requestId = response.requestId;
		acknowledgment.resultServiceType = resultServiceType;

		Console.WriteLine("Acknowledging " + response.results.Length + " " + resultServiceType + " results.");

		int i = 0;
		
		if ((resultServiceType == RESULT_SERVICE_TYPE_HL7) | (resultServiceType == RESULT_SERVICE_TYPE_OBSERVATION)) {
			// acknowlegement for HL7 or Observation

			int numResults = response.results.Length;
			hl7Message[] ackMessages = new hl7Message[numResults];
			acknowledgment.ackMessages = ackMessages;

			foreach ( result result in response.results) {
				Console.WriteLine("Message: " + i.ToString());
				Console.WriteLine("---------");

				string currentHL7 = System.Text.Encoding.UTF8.GetString(result.HL7Message.message);
				int pdfCount = 0;
				if ((result.documents != null)) {
					pdfCount = result.documents.Length;
				}


				Console.WriteLine("The HL7 Message:\n" + currentHL7);
				Console.WriteLine("There are " + pdfCount.ToString() + " attached PDFs.\n\n");

				hl7Message ackMessage = buildHl7ObsAckMessage(currentHL7);

				if ((resultServiceType == RESULT_SERVICE_TYPE_OBSERVATION)) {
					ackMessage.controlId = result.resultId;
				}

				acknowledgment.ackMessages[i] = ackMessage;
				i += 1;
			}
		} else {
			// acknowlegement for Printable
			requestParameter[] requestParams = new requestParameter[2];

			requestParams[0] = new requestParameter();
			requestParams[0].parameterName = "providerAcctId";
			requestParams[0].parameterValue = response.results[0].documents[0].resultInfo.providerAcctId;


			requestParams[1] = new requestParameter();
			requestParams[1].parameterName = "messageControlId";
			requestParams[1].parameterValue = response.results[0].documents[0].resultInfo.messageControlId;

			acknowledgment.requestParameters = requestParams;

			Console.WriteLine("Printable ACK: providerAcctId = " + requestParams[0].parameterValue + ", messageControlId = " + requestParams[1].parameterValue);

		}

		return acknowledgment;
	}

    // Build an HL7 Ack message for a single result
	public hl7Message buildHl7ObsAckMessage(string sHL7Message)
	{
		string[] asHL7Segs = null;
		string sHL7Header = null;
		string[] asHL7HdrElements = null;
		object[] asHL7ACKMessageParams = null;
		string sMessage = null;
		byte[] abMessage = null;
		asHL7ACKMessageParams = new object[8];

		//Seed random generator for ACK Message Id generation.

		hl7Message hl7Message = new hl7Message();
		// Split HL7 message into lines on the newline character.
		asHL7Segs = sHL7Message.Split('\r');
		// Assign the header using first line.
		sHL7Header = asHL7Segs[0];
		// Split the header into elements using the pipe character for this example - Note
		// that other characters can be used as separators.
		asHL7HdrElements = sHL7Header.Split('|');
		// Format this ACK message and add to array.
		asHL7ACKMessageParams[0] = asHL7HdrElements[4];
		// receiving app
		asHL7ACKMessageParams[1] = asHL7HdrElements[5];
		// receiving fac
		// When HL7 2.5.1 {2} needs to be sending fac
		asHL7ACKMessageParams[2] = asHL7HdrElements[2];
		// sending app
		asHL7ACKMessageParams[3] = asHL7HdrElements[3];
		// sending fac
		asHL7ACKMessageParams[4] = System.DateTime.Now;
		// set ACK time to current time
        asHL7ACKMessageParams[5] = (System.DateTime.Now.Ticks - 621355968000000000) / 10000;
		// generate a random unique(id)
		asHL7ACKMessageParams[6] = asHL7HdrElements[11];
		// HL7 version of HL7 message
		asHL7ACKMessageParams[7] = asHL7HdrElements[9];
		// control Id of HL7 message
		sMessage = string.Format(ACK_MESSAGE_FORMAT, asHL7ACKMessageParams);
		abMessage = System.Text.Encoding.UTF8.GetBytes(sMessage);
		hl7Message.message = abMessage;
		// Easier to track for debug purposes.
		hl7Message.controlId = asHL7HdrElements[9];

		return hl7Message;
	}

    // Write the PDF out to a file
    private void writeResultDataToFile(result[] results)
    {
        try
        {
            foreach (result theResult in results)
            {
                if ((theResult.documents != null) && (theResult.documents.Length > 0))
                {
                    foreach (resultDocument document in theResult.documents)
                    {
                        // Open a file with the file name provided in the response object.  
                        FileStream fs = new FileStream(document.documentId + "-" + document.fileName, FileMode.OpenOrCreate);
                        BinaryWriter bw = new BinaryWriter(fs);		//  Use a BinaryWriter to copy the Byte array to a file
                        Console.WriteLine("Writing " + fs.Name + " to disk");
                        bw.Write(document.documentData);
                        bw.Close();
                        fs.Close();
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

}

