using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Collections;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;

using DataAccess;
using ResmedAVXHelper;

namespace RevampResmedService
{
    /// <summary>
    /// List of XferItem that is going to be threaded
    /// </summary>
    class CResmedXferList : ArrayList
    {
        /// <summary>
        /// Status Code
        /// </summary>
        long m_lStatusCode = 0;

        /// <summary>
        /// Status Comment
        /// </summary>
        string m_strStatusComment = String.Empty;

        /// <summary>
        /// Resmed AVX base address
        /// </summary>
        string m_strBaseAddress = String.Empty;

        /// <summary>
        /// Resmed AVX credentials
        /// </summary>
        string m_strCredentials = String.Empty;

        /// <summary>
        /// Construct and init Xfer list
        /// </summary>
        /// <param name="strBaseAddress"></param>
        /// <param name="strCredentials"></param>
        public CResmedXferList(string strBaseAddress, string strCredentials)
        {
            //set the lists initial capacity
            this.Capacity = 100;

            m_lStatusCode = 0;
            m_strStatusComment = String.Empty;

            m_strBaseAddress = strBaseAddress;
            m_strCredentials = strCredentials;

        }

        /// <summary>
        /// Thansfer list to the thread pool.
        /// </summary>
        /// <param name="handle"></param>
        public void QueueTransfer(ref ManualResetEvent handle)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(this.DoWork), handle);
        }

        /// <summary>
        /// do the actual work of assigning the checklist
        /// </summary>
        /// <param name="state"></param>
        public void DoWork(Object state)
        {
            ProcessXferItems().Wait();

            //lets inform the main thread that this method has finished the execution.
            ManualResetEvent waitHandle = (ManualResetEvent)state;
            waitHandle.Set();
        }

        /// <summary>
        /// Process each xfer item.
        /// It will be doing async call to Resmed AVX API
        /// </summary>
        private async Task ProcessXferItems()
        {
            CLogEvent evt = new CLogEvent();
            string strMsg = String.Empty;

            CDataConnection cnn = null;
            string strConnectionString = String.Empty;
            bool bAudit = true;

            string strURI = String.Empty;
            CLookupMainResponse mainRespLookPatient = null;

            //status info
            m_lStatusCode = 0;
            m_strStatusComment = String.Empty;

            //connection settings
            try
            {
                string strUseCnnConfig = "DBConnString_" + ConfigurationManager.AppSettings["APP_TARGET_ENV"];
                strConnectionString = ConfigurationManager.ConnectionStrings[strUseCnnConfig].ConnectionString;
                bAudit = (ConfigurationManager.AppSettings["AUDIT"] == "1") ? true : false;
            }
            catch (ConfigurationErrorsException e)
            {
                if (e != null)
                {
                    m_lStatusCode = 1;
                    m_strStatusComment = evt.EventMessages[(int)CLogEvent.EventMessage.nERROR_THREAD_CONFIG_FILE];

                    evt.LogEvent(CLogEvent.EventMessage.nERROR_THREAD_CONFIG_FILE, EventLogEntryType.Error);
                    return;
                }
            }

            //connect to data source
            cnn = new CDataConnection();
            if (!cnn.Connect(strConnectionString, (int)DataConnectionType.Oracle, bAudit))
            {
                m_lStatusCode = 1;
                m_strStatusComment = evt.EventMessages[(int)CLogEvent.EventMessage.nERROR_CONNECTING_DB];

                evt.LogEvent(CLogEvent.EventMessage.nERROR_CONNECTING_DB, EventLogEntryType.Error);
                return;
            }
            else
            {
                //START PROCESSING xfer items...

                //CALL service to get data...
                using (var client = new HttpClient())
                {
                    client.BaseAddress = new Uri(m_strBaseAddress);
                    client.DefaultRequestHeaders.Accept.Clear();
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", m_strCredentials);

                    HttpResponseMessage response = null;

                    try
                    {
                        //for each xfer item do a call to the resmed avx api and process the response
                        for (int i = 0; i < this.Count; i++)
                        {
                            CResmedXferItem xfer = (CResmedXferItem)this[i];
                            if (xfer != null)
                            {
                                //build URI
                                strURI = "v1/patients/{0}/data/sessions?noOfDays={1}&endDate={2}";
                                strURI = String.Format(strURI, xfer.ExternalID, xfer.DaysGap, xfer.ProcessDate);

                                //evt.EventMessages[(int)CLogEvent.EventMessage.nDETAIL_MESSAGE] = strURI;
                                //evt.LogEvent(CLogEvent.EventMessage.nDETAIL_MESSAGE, EventLogEntryType.Information);

                                response = await client.GetAsync(strURI);
                                if (response.IsSuccessStatusCode)
                                {
                                    try
                                    {
                                        mainRespLookPatient = await response.Content.ReadAsAsync<CLookupMainResponse>();
                                        
                                        if (ValidateLookupResponse(xfer.ExternalID, ref mainRespLookPatient))
                                        {
                                            //process cpap data received
                                            ProcessCPAPData(xfer.PatientID, xfer.ExternalID, mainRespLookPatient.Response.patientData.sessionData, ref cnn, ref evt);
                                        }
                                        else
                                        {
                                            if (m_lStatusCode == 1)
                                            {
                                                //m_strStatusComment = evt.EventMessages[(int)CLogEvent.EventMessage.nINVALID_FORMAT_RESPONSE];
                                                //evt.LogEvent(CLogEvent.EventMessage.nINVALID_FORMAT_RESPONSE, EventLogEntryType.Warning);

                                                evt.EventMessages[(int)CLogEvent.EventMessage.nDETAIL_MESSAGE] = evt.EventMessages[(int)CLogEvent.EventMessage.nINVALID_FORMAT_RESPONSE] + " " + m_strStatusComment + " Reference " + xfer.ExternalID + ".";
                                                evt.LogEvent(CLogEvent.EventMessage.nDETAIL_MESSAGE, EventLogEntryType.Warning);
                                            }
                                        }
                                    }
                                    catch (Exception e)
                                    {
                                        if (e != null)
                                        {
                                            m_lStatusCode = 1;
                                            m_strStatusComment = evt.EventMessages[(int)CLogEvent.EventMessage.nERROR_READING_ASYNC];
                                            evt.LogEvent(CLogEvent.EventMessage.nERROR_READING_ASYNC, EventLogEntryType.Error);
                                        }
                                    }
                                }
                                else
                                {
                                    m_lStatusCode = 1;
                                    //strMsg= "RESMED AVX> " + response.ReasonPhrase;
                                    m_strStatusComment = evt.EventMessages[(int)CLogEvent.EventMessage.nUNSUCCESS_RESPONSE];
                                    evt.LogEvent(CLogEvent.EventMessage.nUNSUCCESS_RESPONSE, EventLogEntryType.Error);
                                }
                            }
                        }
                    }
                    catch (System.Net.Http.HttpRequestException e)
                    {
                        if (e != null)
                        {
                            m_lStatusCode = 1;
                            strMsg = "HttpRequestException";
                            //if (!String.IsNullOrEmpty(e.Message))
                            //{
                            //    strMsg += e.Message;
                            //}
                            //if (e.InnerException != null)
                            //{
                            //    if (!String.IsNullOrEmpty(e.InnerException.Message))
                            //    {
                            //        strMsg += " " + e.InnerException.Message;
                            //    }
                            //}
                            m_strStatusComment = strMsg;

                            evt.EventMessages[(int)CLogEvent.EventMessage.nDETAIL_MESSAGE] = strMsg;
                            evt.LogEvent(CLogEvent.EventMessage.nDETAIL_MESSAGE, EventLogEntryType.Error);
                        }
                    }
                }

                //close connection
                cnn.Close();

            }

        }

        /// <summary>
        /// Validate response from Resmed AVX API.
        /// </summary>
        /// <param name="strExternalID"></param>
        /// <param name="mainRespLookPatient"></param>
        /// <returns></returns>
        private bool ValidateLookupResponse(string strExternalID,
                                            ref CLookupMainResponse mainRespLookPatient)
        {
            bool bValid = false;

            m_lStatusCode = 0;
            m_strStatusComment = String.Empty;

            //process CPAP data
            if (mainRespLookPatient != null)
            {
                if (mainRespLookPatient.Response != null)
                {
                    if (mainRespLookPatient.Response.responseDesc == "SUCCESS")
                    {
                        if (mainRespLookPatient.Response.patientData != null)
                        {
                            if (mainRespLookPatient.Response.patientData.ecn == strExternalID)
                            {
                                if (mainRespLookPatient.Response.patientData.sessionData != null)
                                {
                                    bValid = true;
                                }
                                else
                                {
                                    m_lStatusCode = 1;
                                    m_strStatusComment = "No session data received.";
                                    bValid = false;
                                }
                            }
                            else
                            {
                                m_lStatusCode = 1;
                                m_strStatusComment = "ECN from response did not match.";
                                bValid = false;
                            }
                        }
                        else
                        {
                            //It's normal that the PatientData object is null. This happend when there is no data for the specify date or period in Resmed.
                            //No need to show a warning for this.
                            //m_lStatusCode = 1;
                            //m_strStatusComment = "No patient data received";
                            m_lStatusCode = -1; //Status Code = -1 will be ignored for the event log
                            bValid = false;
                        }
                    }
                    else
                    {
                        m_lStatusCode = 1;
                        m_strStatusComment = "RESMED AVX> " + mainRespLookPatient.Response.responseDesc;
                        bValid = false;
                    }
                }
                else
                {
                    m_lStatusCode = 1;
                    m_strStatusComment = "Incomplete response receive (level 2)";
                    bValid = false;
                }
            }
            else
            {
                m_lStatusCode = 1;
                m_strStatusComment = "Incomplete response receive (level 1)";
                bValid = false;
            }

            return bValid;
        }

        /// <summary>
        /// Process CPAP data
        /// </summary>
        /// <param name="strPatientID"></param>
        /// <param name="strExternalID"></param>
        /// <param name="lstSessionData"></param>
        /// <param name="cnn"></param>
        /// <param name="evt"></param>
        private void ProcessCPAPData(string strPatientID,
                                     string strExternalID,
                                     List<CSessionData> lstSessionData,
                                     ref CDataConnection cnn,
                                     ref CLogEvent evt)
        {
            long lRecordCount = 0; //new records count
            long lResult = 0;

            string strMsg = String.Empty;

            //process all 
            foreach (CSessionData session in lstSessionData)
            {
                CCPAP cpap = new CCPAP();

                //PatientID
                cpap.PatientID = strPatientID;

                //External ID
                cpap.ExternalID = strExternalID;

                //Therapy Date
                cpap.TherapyDate = session.sessionDate;

                //ReceiptTime
                cpap.ReceiptTime = session.receiptTime;

                //Device Serial No
                if (session.deviceID != null)
                {
                    cpap.DeviceMode = session.deviceID.mode;

                    if (session.deviceID.device != null)
                    {
                        cpap.DeviceSerialNo = session.deviceID.device.serialNo;
                        cpap.DeviceType = session.deviceID.device.deviceType;
                        cpap.DeviceTypeDesc = session.deviceID.device.deviceTypeDesc;
                    }
                }

                //Settings
                if (session.set != null)
                {
                    cpap.SettingPress = session.set.press;
                    cpap.SettingMaxPress = session.set.maxPress;
                    cpap.SettingMinPress = session.set.minPress;
                    cpap.SettingEPRType = session.set.EPRType;
                    cpap.SettingEPRLevel = session.set.EPRLevel;
                    cpap.SettingODIThreshold = session.set.ODIThreshold;
                    cpap.SettingIBREnable = session.set.iBREnable;
                    cpap.SettingEPAPAutoEnable = session.set.EPAPAutoEnable;
                    cpap.SettingIPAP = session.set.IPAP;
                    cpap.SettingEPAP = session.set.EPAP;
                    cpap.SettingAutoSetResponse = session.set.autosetResponse;
                    cpap.SettingMinEPAP = session.set.minEPAP;
                    cpap.SettingMaxEPAP = session.set.maxEPAP;
                    cpap.SettingMinPS = session.set.minPS;
                    cpap.SettingMaxPS = session.set.maxPS;
                }

                //Clinical metrics
                if (session.clinicalMetrics != null)
                {
                    cpap.SpontTriggBreaths = session.clinicalMetrics.spontTriggBreaths;
                    cpap.SpontCycledBreaths = session.clinicalMetrics.spontCycledBreaths;

                    if (session.clinicalMetrics.tgtIPAP != null)
                    {
                        cpap.TGTIPAP_95 = session.clinicalMetrics.tgtIPAP.ninetyfive;
                        cpap.TGTIPAP_50 = session.clinicalMetrics.tgtIPAP.median;
                        cpap.TGTIPAP_MAX = session.clinicalMetrics.tgtIPAP.max;
                    }

                    if (session.clinicalMetrics.tgtEPAP != null)
                    {
                        cpap.TGTEPAP_95 = session.clinicalMetrics.tgtEPAP.ninetyfive;
                        cpap.TGTEPAP_50 = session.clinicalMetrics.tgtEPAP.median;
                        cpap.TGTEPAP_MAX = session.clinicalMetrics.tgtEPAP.max;
                    }

                    if (session.clinicalMetrics.leak != null)
                    {
                        cpap.LEAK_95 = session.clinicalMetrics.leak.ninetyfive;
                        cpap.LEAK_50 = session.clinicalMetrics.leak.median;
                        cpap.LEAK_MAX = session.clinicalMetrics.leak.max;
                    }

                    if (session.clinicalMetrics.respRate != null)
                    {
                        cpap.RESPRATE_95 = session.clinicalMetrics.respRate.ninetyfive;
                        cpap.RESPRATE_50 = session.clinicalMetrics.respRate.median;
                        cpap.RESPRATE_MAX = session.clinicalMetrics.respRate.max;
                    }

                    if (session.clinicalMetrics.ieRatio != null)
                    {
                        cpap.IERATIO_95 = session.clinicalMetrics.ieRatio.ninetyfive;
                        cpap.IERATIO_50 = session.clinicalMetrics.ieRatio.median;
                        cpap.IERATIO_MAX = session.clinicalMetrics.ieRatio.max;
                    }

                    if (session.clinicalMetrics.minuteVent != null)
                    {
                        cpap.MINUTEVENT_95 = session.clinicalMetrics.minuteVent.ninetyfive;
                        cpap.MINUTEVENT_50 = session.clinicalMetrics.minuteVent.median;
                        cpap.MINUTEVENT_MAX = session.clinicalMetrics.minuteVent.max;
                    }

                    if (session.clinicalMetrics.tidalVol != null)
                    {
                        cpap.TIDALVOL_95 = session.clinicalMetrics.tidalVol.ninetyfive;
                        cpap.TIDALVOL_50 = session.clinicalMetrics.tidalVol.median;
                        cpap.TIDALVOL_MAX = session.clinicalMetrics.tidalVol.max;
                    }

                    if (session.clinicalMetrics.SPO2 != null)
                    {
                        cpap.SPO2_95 = session.clinicalMetrics.SPO2.ninetyfive;
                        cpap.SPO2_50 = session.clinicalMetrics.SPO2.median;
                        cpap.SPO2_MIN = session.clinicalMetrics.SPO2.min;
                        cpap.SPO2_minBelow88 = session.clinicalMetrics.SPO2.minutesBelow88percent;
                        cpap.SPO2_secBelowDYNAT = session.clinicalMetrics.SPO2.secondsBelowDynamicThreshold;
                    }

                    if (session.clinicalMetrics.alveolarVentilation != null)
                    {
                        cpap.ALVEOLARVENT_95 = session.clinicalMetrics.alveolarVentilation.ninetyfive;
                        cpap.ALVEOLARVENT_50 = session.clinicalMetrics.alveolarVentilation.median;
                        cpap.ALVEOLARVENT_MAX = session.clinicalMetrics.alveolarVentilation.max;
                    }

                }

                //Respiratory event indeces: AHI, HI, OAI, CAI, UAI
                if (session.respEvents != null)
                {
                    cpap.AHI = session.respEvents.AHI;
                    cpap.HI = session.respEvents.HI;
                    cpap.AI = session.respEvents.AI;
                    cpap.OAI = session.respEvents.OAI;
                    cpap.CAI = session.respEvents.CAI;
                    cpap.UAI = session.respEvents.UAI;
                    cpap.ODI = session.respEvents.ODI;
                    cpap.CSR = session.respEvents.CSR;
                    cpap.RERA = session.respEvents.RERA;
                }

                //Patient Interface
                if (session.patientInterface != null)
                {
                    cpap.Humidifier = session.patientInterface.humidifier;
                    cpap.HeatedTube = session.patientInterface.heatedTube;
                    cpap.AMBHumidity = session.patientInterface.ambHumidity;
                }



                //Usage
                if (session.usage != null)
                {
                    //Minutes of Use
                    cpap.MinOfUse = session.usage.duration;

                    //Mask On/Off
                    if (session.usage.maskOn != null && session.usage.maskOff != null)
                    {
                        if (session.usage.maskOn.Count == session.usage.maskOff.Count)
                        {
                            for (int idx = 0; idx < session.usage.maskOn.Count; idx++)
                            {
                                CCPAPUsage cpapUsage = new CCPAPUsage();
                                cpapUsage.MaskOn = session.usage.maskOn[idx];
                                cpapUsage.MaskOff = session.usage.maskOff[idx];
                                cpap.Usage.Add(cpapUsage);
                            }
                        }
                    }

                }

                //save
                if (cpap.MinOfUse != "0")
                {
                    lResult = cpap.Save(ref cnn, ref m_lStatusCode, ref m_strStatusComment);
                    if (m_lStatusCode != 0)
                    {
                        evt.LogEvent(CLogEvent.EventMessage.nERROR_CPAP_SAVE_FAILED, EventLogEntryType.Error);
                        return;
                    }
                    else
                    {
                        lRecordCount += lResult;
                    }
                }

            }

            //rsp.recordCount = lRecordCount;
        }

    }
}
