using System;
using System.Net;
using System.Web.Services.Protocols;
using HL7ResultsSampleCodeCS.HL7ResultsWebService;
using Microsoft.VisualBasic;

namespace HL7ResultsSampleCodeCS
{
	/// <summary>
	/// Class HL7ResultsSample requests lab order (HL7 messages) from the MedPlus
	/// Hub platform.
	///
	/// Upon its construction, this class will exercise the functions relating to the
	/// HL7 Results Service, which includes:
	///
	/// 1. Performing a new results search (batch size 5),
	///    for all provider accounts the hub user has access to,
	///    acknowledging the results, and getting more results.
	///
	/// 2. Getting an array of provider accounts for the hub user.
	///    This represents a subset of provider accounts.
	///
	/// 3. Performing a new results search (batch size 5),
	///    with a single provider account, 
	///    acknowledging the results, and getting more results.
	/// 
	/// 4. Performing a date range search (batch size 5), 
	///    acknowledging the results, and getting more results.
	///    
	/// </summary>
	public class HL7ResultsSample
	{
		// USERNAME designates the Hub user name used to
		// authenticate to the Hub web service
		private const string USERNAME = "test";       

		// PASSWORD designates the password associated with
		// the Hub user when authenticating to the web service
		private const string PASSWORD = "customer1";   

		// WEB_SERVICE_URL designates the end point used to
		// connect to the Hub's HL7 Results web service
		private const string WEB_SERVICE_URL = "https://cert.hub.care360.com/resultsHub/observations/hl7";

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

		private ResultsService proxy;
		private const String MAX_MESSAGES = "5";

		public HL7ResultsSample()
		{
			proxy = getWebServiceObject();

            // EXAMPLE 1:
            // 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(null, MAX_MESSAGES, "", "");
			Console.WriteLine("Done with date range results search.");

			// EXAMPLE 2:
			// Perform a new results search using just one provider account.  
			// Return max of 5 results.
			String[] providerAccountsArray;
			providerAccountsArray = getProviderAccounts();
			Console.WriteLine("Performing new results search...");
			getResults(providerAccountsArray, MAX_MESSAGES, "", "");
			Console.WriteLine("Done with date range results search.");

            // EXAMPLE 3:
            // Date range search using all provider accounts.  Max of 5 results, will 
            // acknowledge those results and then get more until they're all processed
			Console.WriteLine("Performing limited date range search...");
			getResults(providerAccountsArray, MAX_MESSAGES, "01/01/2004", "01/01/2009");
			Console.WriteLine("Done with date range results search.");
		}

		private ResultsService getWebServiceObject() 
		{
			ResultsService webService;

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

			if (!(webService == null)) 
			{
				//------------------------------------------------------------------
				//STEP 2: 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;

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

			return webService;
		}


		// The buildResultsRequest function will take the parameters for a results request and gernerate the object
		// to pass to the web method
		private ResultsRequest buildResultsRequest(String[] myProviderAccounts, string maxMessages, string startDate, string endDate)
		{
			ResultsRequest uReq = new ResultsRequest();
			string[] asProvAccts = null;

			try
			{
				//------------------------------------------------------------------
				//STEP 1: SET PROVIDER ACCOUNTS
				//------------------------------------------------------------------
				if (myProviderAccounts != null)
				{
					uReq.providerAccountIds = asProvAccts;
				}

				//------------------------------------------------------------------
				//STEP 2: SET MAXIMUM NUMBER OF MESSAGES TO RETRIEVE AT ONCE
				//------------------------------------------------------------------
				if (maxMessages.Length > 0)
				{
					uReq.maxMessages = int.Parse(maxMessages);
				}

				//------------------------------------------------------------------
				//STEP 3: SET DATE RANGE TO GET RESULTS
				//------------------------------------------------------------------
				if (endDate.Length > 0)
				{
					uReq.endDate = endDate;
				}

				if (startDate.Length > 0 )
				{
					uReq.startDate = startDate;
				}

				return uReq;
			}
			catch (Exception ex)
			{
				throw ex;
			}

		}

		public HL7Message[] GenerateHL7AckMessages(HL7ResultsResponse results_response)
		{
			// Generate an array of HL7 messages for ACK'ing passed in response object.
			HL7Message[] ack_messages = new HL7Message[results_response.HL7Messages.Length];
			int i=0;
			foreach (HL7Message hl7_message in results_response.HL7Messages)
			{
				ack_messages[i] = this.GenerateHL7AckMessage(hl7_message);
				i++;
			}
			return ack_messages;
		}

		public HL7Message GenerateHL7AckMessage(HL7Message hl7_message_in)
		{
			// Create HL7 ACK message for specified input HL7 result message.
			HL7Message hl7_message_return = new HL7Message();
			System.Object[] msg_params = new System.Object[7];
			// Unencode HL7 message bytes into string.
			byte[] message_bytes = hl7_message_in.message;
			string message_string = System.Text.Encoding.UTF8.GetString(message_bytes);

			// Split string message into segments.
			string[] message_segments = message_string.Split('\r');
			// Header is first line.
			string header = message_segments[0];
			// Now split the header on pipe char.
			string[] header_fields = header.Split('|');
			
			// Define message parameters for format statement.
			msg_params[0] = header_fields[4]; // receiving app
			msg_params[1] = header_fields[5]; // receiving fac
			msg_params[2] = header_fields[2]; // sending app
			msg_params[3] = header_fields[3]; // sending fac
			msg_params[4] = System.DateTime.Now; // time stamp
			msg_params[5] = (System.DateTime.Now.Ticks - 621355968000000000) / 10000; // some unique ID
			msg_params[6] = header_fields[9]; // message control ID

			// Write the ACK message being returned to console.
			string message_string_ret = String.Format(ACK_MESSAGE_FORMAT, msg_params);

			// Encode HL7 ack message into bytes, assign to return message.
			byte[] message_UTF8 = System.Text.Encoding.UTF8.GetBytes(message_string_ret);
			hl7_message_return.message = message_UTF8;
			
			// NOTE: Message control ID is plain text (for debugging purposes).
			hl7_message_return.controlId = header_fields[9].ToString();
			return hl7_message_return;
		}

		private void getResults(String[] myProviderAccounts, String maxMessages, String startDate, String endDate) 
		{
			bool bInitialRequest = true;
			bool bContinue = true;
			string requestID = "";
			HL7ResultsResponse myHL7Response = null;
			ResultsRequest hl7ResultsRequest;

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

					//------------------------------------------------------------------
					//STEP 1: GENERATE RESULTS REQUEST OBJECT
					//------------------------------------------------------------------
					hl7ResultsRequest = buildResultsRequest(myProviderAccounts, maxMessages, startDate, endDate);

					//--------------------------------------------------------------
					//STEP 2: SUBMIT REQUEST TO THE HUB
					//--------------------------------------------------------------
					myHL7Response = proxy.getHL7Results(hl7ResultsRequest);

					if (myHL7Response == null) 
					{
						Console.WriteLine("Failed to properly call getHL7Results WebService method.");
					}
				}
				else
				{
					Console.WriteLine("Making another pass...");
					myHL7Response = proxy.getMoreHL7Results(requestID);
				}

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

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

					HL7Message[] ahl7HL7ACKMessages = this.GenerateHL7AckMessages(myHL7Response);

					Console.WriteLine("Acknowledging HL7 Messages...");
					proxy.acknowledgeHL7Results(requestID, ahl7HL7ACKMessages);

					bContinue = (bool)myHL7Response.isMore;
				}			
			}
		}

		// The getProviderAccounts method will call the Hub and get a list
		// of provider accounts the hub user has access to.  It then places
		// the first one in a String array and returns the array, which can 
		// be passed to the getHL7Results method to get results for a subset 
		// of the provider accounts the hub user has access to.
		private String[] getProviderAccounts() 
		{
			String[] asProvAccts = null;

			try 
			{
				Console.WriteLine("Getting Provider Accounts...");
				ProviderAccount[] myProviderAccounts = proxy.getProviderAccounts();

				if (myProviderAccounts != null)
				{
					if (myProviderAccounts.Length > 0) 
					{
						foreach (ProviderAccount myAccount in myProviderAccounts)
						{
							Console.WriteLine("ID: " + myAccount.accountId + ", Name: " + myAccount.accountName + ", Provider: " + myAccount.provider.name);
						}

						asProvAccts = new String[1];
						asProvAccts[0] = myProviderAccounts[0].accountId.Trim();
					}
				}

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

		}
	}
}
