﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using CIA_CSS;
using IndianHealthService.BMXNet.EHR.Model;

namespace IndianHealthService.BMXNet.EHR
{
    /// <summary>
    /// The EhrFramework integrates the COM-based EHR/VueCentric framework with BMXNET.
    /// A component that uses BMXNET for communications, events, and context can be implemented to
    /// be hosted within the EHR/VueCentric framework by creating a subclass of UserControl and including 
    /// the a EHR integration partial class similar to the following:
    /// <code>
    /// 
    ///partial class YourEHRComponent : CSS_Patient.ICSS_PatientEvents, CSS_Encounter.ICSS_EncounterEvents, CIA_CSS.ICSS_SessionEvents, IEhrEvents, IEhrComponent
    ///{
    ///
    /// #region Copy Region into partial EHR file plus include interfaces noted above
    ///
    /// protected override void Dispose(bool disposing)
    /// {
    ///     this.TeardownFramework();
    ///
    ///     if (disposing &amp;&amp; (components != null))
    ///     {
    ///         components.Dispose();
    ///     }
    ///     base.Dispose(disposing);
    /// }
    ///
    /// public LocalSession LocalSession
    /// {
    ///     get { return this.Framework.LocalSession; }
    /// }
    ///
    /// public Context Context
    /// {
    ///     get { return this.LocalSession.Context; }
    /// }
    ///
    /// public RemoteSession RemoteSession
    /// {
    ///     get { return this.Framework.RemoteSession; }
    /// }
    ///
    /// private String _helpString = "";
    ///
    /// [ComVisible(true)]
    /// public String HelpString
    /// {
    ///     get { return _helpString; }
    ///     set { _helpString = value; }
    /// }
    ///
    /// private String _helpContext = "";
    /// 
    /// [ComVisible(true)]
    /// public String HelpContext
    /// {
    ///     get { return _helpContext; }
    ///     set { _helpContext = value; }
    /// }

    ///        private String _helpFile = "";

    /// [ComVisible(true)]
    /// public String HelpFile
    /// {
    ///     get { return _helpFile; }
    ///     set { _helpFile = value; }
    /// }
    ///
    /// public virtual void AttachToEhr(object aConsumer)
    /// {
    ///     this.SetupFramework();
    ///     this.Framework.AttachTo(aConsumer);
    ///
    ///     if (this.HelpFile.Length == 0)
    ///     {
    ///         this.HelpFileAssemblyName(aConsumer.GetType());
    ///     }
    /// }
    ///
    /// private EhrFramework _framework = null;
    ///
    /// public EhrFramework Framework
    /// {
    ///     get { return _framework; }
    ///     set { _framework = value; }
    /// }
    ///
    /// protected void SetupFramework()
    /// {
    ///     this.Framework = EhrFramework.On(new CIA_CSS.CSS_ServerClass().Session, (IEhrEvents)this, (CIA_CSS.ICSS_SessionEvents)this);
    /// }
    ///
    ///protected void TeardownFramework()
    ///{
    ///    if (this.Framework != null)
    ///    {
    ///        this.Framework.Close();
    ///    }
    ///}
    ///
    ///public event EventHandler&lt;ContextChangedArgs&gt; ContextChanged;
    ///public event EventHandler&lt;ContextChangingArgs&gt; ContextChanging;
    ///
    /// void CSS_Encounter.ICSS_EncounterEvents.Canceled()
    /// {
    /// }
    ///
    /// void CSS_Encounter.ICSS_EncounterEvents.Committed()
    /// {
    /// try
    /// {
    /// if (this.ContextChanged != null)
    /// {
    /// ContextChangedArgs args = new ContextChangedArgs();
    /// args.IsVisitChange = true;
    /// args.AfterContext = this.Context;
    /// this.ContextChanged.Invoke(this, args);
    /// }
    /// }
    /// catch
    /// {
    /// }
    /// }
    ///
    /// string CSS_Encounter.ICSS_EncounterEvents.Pending(bool Silent)
    /// {
    ///     try
    ///     {
    ///         if (this.ContextChanging == null)
    ///         {
    ///             return "";
    ///         }
    ///         else
    ///         {
    ///             ContextChangingArgs args = new ContextChangingArgs();
    ///             args.BeforeContext = this.Context;
    ///             args.IsVisitChange = true;
    ///             args.Cancel = false;
    ///             this.ContextChanging.Invoke(this, args);
    ///
    ///             return args.Cancel ? "Cancel" : "";
    ///         }
    ///     }
    ///     catch
    ///     {
    ///         return "";
    ///     }
    /// }
    ///
    ///void CSS_Patient.ICSS_PatientEvents.Canceled()
    ///{
    ///}
    ///
    ///void CSS_Patient.ICSS_PatientEvents.Committed()
    ///{
    ///try
    ///{
    ///if (this.ContextChanged != null)
    ///{
    ///ContextChangedArgs args = new ContextChangedArgs();
    ///args.IsPatientChange = true;
    ///args.AfterContext = this.Context;
    ///this.ContextChanged.Invoke(this, args);
    ///}
    ///}
    ///catch
    ///{
    ///}
    ///}
    ///
    /// string CSS_Patient.ICSS_PatientEvents.Pending(bool Silent)
    /// {
    ///     try
    ///     {
    ///         if (this.ContextChanging == null)
    ///         {
    ///             return "";
    ///         }
    ///         else
    ///         {
    ///             ContextChangingArgs args = new ContextChangingArgs();
    ///             args.BeforeContext = this.Context;
    ///             args.IsPatientChange = true;
    ///             args.Cancel = false;
    ///             this.ContextChanging.Invoke(this, args);
    ///
    ///             return args.Cancel ? "Cancel" : "";
    ///         }
    ///     }
    ///     catch
    ///     {
    ///         return "";
    ///     }
    ///}
    ///
    /// protected virtual void UnhandledException(Exception aProblem)
    /// {
    ///     MessageBox.Show(aProblem.Message + "  \n\n\n" + aProblem.StackTrace, "Exception");
    /// }
    ///
    /// protected void HelpFileAssemblyName(Type aType)
    /// {
    ///     this.HelpFile = aType.Assembly.GetName().Name + ".chm";
    /// }
    ///
    /// public void EventCallback(string anEvent, string aStub)
    /// {
    ///     this.Framework.IncomingEventCallback(anEvent, aStub);
    /// }
    ///
    /// public void RPCCallback(int Handle, string Data)
    /// {
    /// }
    ///
    /// public void RPCCallbackError(int Handle, int ErrorCode, string ErrorText)
    /// {
    /// }
    ///
    /// #endregion
    ///
    /// }
    ///
    ///
    /// //Copy this interface too    
    /// public interface IEhrComponent : IDisposable
    /// {
    ///     String HelpFile { get; }
    ///     String HelpContext { get; }
    ///     String HelpString { get; }
    /// }
    /// }
    /// 
    /// </code> 
    /// <para>
    /// See the BMXNET SDK IndianHealthService.BMXNet.Example.CrossComponent.EHR Project as an example.
    /// </para>
    /// </summary>
    public class EhrFramework 
    {
    
        /// <summary>
        /// Used to create the single EhrFramework instance used for each EHR/VueCentric-based component
        /// </summary>
        /// <remarks>
        /// Use the provided partial class to construct a UserControl-based component to host in the EHR/VueCentric framework
        /// </remarks>
        /// <param name="aCssSession">A COM instance of CSS_Session. The EhrFramework will Release() it</param>
        /// <param name="anEventSource">The event source bridge to pass events from your COM-Visible control to the EhrFramework</param>
        /// <param name="sessionEvents">A COM instance of ICSS_SessionEvents. The EhrFramework will Release() it</param>
        /// <returns></returns>
        public static EhrFramework On(CIA_CSS.CSS_Session aCssSession, IEhrEvents anEventSource, CIA_CSS.ICSS_SessionEvents sessionEvents)
        {
            EhrFramework answer = new EhrFramework();
            answer.Setup(aCssSession, anEventSource, sessionEvents);
            return answer;
        }

        private CSS_Session _cssSession = null;

        protected CSS_Session CssSession
        {
            get { return _cssSession; }
            set { _cssSession = value; }
        }

        private LocalSession _localSession = null;

        /// <summary>
        /// Access the LocalSession.
        /// <remarks>
        /// This is used by the partial class to prime your LocalSessionConsumer implementing control
        /// </remarks>
        /// </summary>
        public LocalSession LocalSession
        {
            get { return _localSession; }
            set { _localSession = value; }
        }

        private RemoteSession _remoteSession = null;

        /// <summary>
        /// Access the primary RemoteSession.
        /// <remarks>
        /// This is used by the partial class to prime your RemoteSessionConsumer implementing control
        /// </remarks>
        /// </summary>
        public RemoteSession RemoteSession
        {
            get { return _remoteSession; }
            set { _remoteSession = value; }
        }
        private BMXNetBroker _broker = null;

        internal BMXNetBroker Broker
        {
            get { return _broker; }
            set { _broker = value; }
        }


        protected void Setup(CIA_CSS.CSS_Session aCssSession, IEhrEvents anEventSource, CIA_CSS.ICSS_SessionEvents sessionEvents)
        {

            this.CssSession = aCssSession;

            CiaSession ciaSession = new CiaSession(aCssSession, anEventSource);
            ciaSession.ComComponent = sessionEvents;

            this.LocalSession = ciaSession;

            this.Broker = BMXNetEhrBroker.Open(ciaSession.CssSession, ciaSession.CssUser, ciaSession.User);
            this.RemoteSession = this.Broker.PrimaryRemoteSession;
            ciaSession.RemoteSession = this.Broker.PrimaryRemoteSession;

        }

        /// <summary>
        /// Close the EhrFramework
        /// <remarks>
        /// Releases all COM objects
        /// </remarks>
        /// </summary>
        public void Close()
        {
            if (this.LocalSession != null)
            {
                ((CiaSession)this.LocalSession).Teardown();
                this.LocalSession = null;
            }
        }

        /// <summary>
        /// Forward an incoming event from the COM object to the EhrFramework
        /// </summary>
        /// <param name="anEventType">This string corresponds to well-known/shared EventTypes used to identify 
        /// different events published and subscribed within a local desktop application (WinFramework)
        /// or in the EHR.</param>
        /// <param name="someDetails">This is an optional peice of data with some event-specific details.</param>
        public void IncomingEventCallback(string anEventType, string someDetails)
        {
            ((CiaSession)this.LocalSession).IncomingEventCallback(anEventType, someDetails);
        }


        /// <summary>
        /// Attach the services to the aConsumer.  This is basically a push of services versus a service lookup.
        /// aConsumer will be offered to cosume:
        /// <list>
        /// LocalSession via LocalConsumer
        /// RemoteSession via RemoteSessionConsumer
        /// RemoteSessionPool via RemoteSessionPoolConsumer
        /// </list>
        /// </summary>
        /// <param name="aConsumer">An object optionally implementing LocalConsumer, RemoteSessionConsumer, and/or RemoteSessionPoolConsumer</param>
        public void AttachTo(object aConsumer)
        {
            LocalConsumer localConsumer = aConsumer as LocalConsumer;
            if (localConsumer != null)
            {
                localConsumer.LocalSession = this.LocalSession;
            }

            RemoteSessionConsumer remoteConsumer = aConsumer as RemoteSessionConsumer;
            if (remoteConsumer != null)
            {
                remoteConsumer.RemoteSession = this.RemoteSession;
            }

            RemoteSessionPoolConsumer remotePoolConsumer = aConsumer as RemoteSessionPoolConsumer;
            if (remotePoolConsumer != null)
            {
                remotePoolConsumer.RemoteSessionPool = this.Broker.RemoteSessionPool;
            }

            if (this.LocalSession == null)
                throw new Exception("BMX: Unable to establish application model");

            if (this.LocalSession.Context == null)
                throw new Exception("BMX: Unable to establish context");
        }
    }
}