using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using CIA_CSS;
using IndianHealthService.BMXNet.Model;
using System.Windows.Forms;
using IndianHealthService.BMXNet.Services;

namespace IndianHealthService.BMXNet.EHR.Model
{
    internal class CiaSession : CiaObject, LocalSession, LocalEventService
    {

        private CIA_CSS.ICSS_SessionEvents _comComponent = null;

        public CIA_CSS.ICSS_SessionEvents ComComponent
        {
            get { return _comComponent; }
            set { _comComponent = value; }
        }

        public void Teardown()
        {

            if (this.CssSession != null)
            {
                this.ReleaseComObject(this.CssSession);
                this.CssSession = null;
            } 
            if (this.CssEncounter != null)
            {
                this.ReleaseComObject(this.CssEncounter);
                this.CssEncounter = null;
            }

            if (this.CssPatient != null)
            {
                this.ReleaseComObject(this.CssPatient);
                this.CssPatient = null;
           }

            if (this.CssUser != null)
            {
                this.ReleaseComObject(this.CssUser);
                this.CssUser = null;
            }

            this.PatientContext = null;
        }


        protected void ReleaseComObject(object o)
        {     
            int countLeft=Marshal.ReleaseComObject(o);        
        }

        private CSS_User.ICSS_User _cssUser;

        public CSS_User.ICSS_User CssUser
        {
            get { return _cssUser; }
            set { _cssUser = value; }
        }
 
        private CSS_Patient.ICSS_Patient _cssPatient;

        public CSS_Patient.ICSS_Patient CssPatient
        {
            get { return _cssPatient; }
            set
            {
                _cssPatient = value;

            }
        }

        private CSS_Encounter.ICSS_Encounter _cssEncounter;

        public CSS_Encounter.ICSS_Encounter CssEncounter
        {

            get { return _cssEncounter; }
            set
            {
                _cssEncounter = value;
            }
        }
      
        private CSS_Site.ICSS_Site _cssSite;

        public CSS_Site.ICSS_Site CssSite
        {
            get { return _cssSite; }
            set { _cssSite = value; }
        }

        public CiaSession(ICSS_Session aCssSession, IEhrEvents eventSource)
        {
            this.CssSession = aCssSession;
            this.CssPatient = this.CssSession.FindServiceByProgID("CSS_Patient.Patient") as CSS_Patient.ICSS_Patient;
            this.CssEncounter = this.CssSession.FindServiceByProgID("CSS_Encounter.Encounter") as CSS_Encounter.ICSS_Encounter;
            this.CssUser = this.CssSession.FindServiceByProgID("CSS_User.User") as CSS_User.ICSS_User;
            this.CssSite = this.CssSession.FindServiceByProgID("CSS_Site.Site") as CSS_Site.ICSS_Site;
            
            this._patientContext = CiaContext.CreateFrom(this, eventSource);
        }

        private ICSS_Session _cssSession;

        public ICSS_Session CssSession
        {
            get { return _cssSession; }
            set { _cssSession = value; }
        }

        private CiaContext _patientContext = null;

        internal CiaContext PatientContext
        {
            get { return _patientContext; }
            set { _patientContext = value; }
        }

        public Context Context
        {
            get
            {
                return (Context)this.PatientContext;
            }
        }

        public User NewUser(String aName, String anIen, Division aDivision)
        {
            CiaUser user = new CiaUser();
            user.Ien = anIen;
            user.Name = aName;
            user.FriendDivision = aDivision;
            return user;
        }   

        private User _user = null;

        public User User
        {
            get
            {
                if ((_user == null) && (this.CssPatient != null))
                {               
                    CiaUser user = new CiaUser();                  
                    user.Ien = this.CssUser.Handle.ToString();
                    user.Name = this.CssUser.Name;
                    user.RemoteSession = this.RemoteSession;
                
                    CiaDivision division = new CiaDivision();

                    if (this.CssSite != null)
                    {
                        division.Name = this.CssSite.LongName;
                        division.Ien = this.CssSite.Handle.ToString();
                    }
                    else
                    {
                        division.Name = "Unable to access division";
                        division.Ien = "";
                    }
              
                    user.FriendDivision = (Division)division;

                    if (user.Name==null || user.Name.Length == 0)
                    {
                        return (User)user;
                    }
                    else
                    {
                        this._user = (User)user;
                    }

                    /*
                        answer.Ien = aSession.CssPatient.Handle.ToString();
                        answer.PatientName = aSession.CssPatient.Name;
                        answer.Sex = aSession.CssPatient.Sex;
                        answer.Dob = aSession.CssPatient.DOB;
                        answer.AdminDate = aSession.CssPatient.AdmitDate;
                        answer.Age = aSession.CssPatient.Age;
                        answer.Ssn = aSession.CssPatient.SSN;
                     */
                }

                return _user;
            }
        }

        private int _traceHandle = 0;

        protected int TraceHandle
        {
            get { return _traceHandle; }
            set { _traceHandle = value; }
        }

        public void TraceAll(String someContent, String aHeader)
        {

        }

        public void TraceAll(String someContent)
        {

        }

        public void TraceBegin(String traceClass, String traceType)
        {

        }

        public void TraceHeader(String aString)
        {

        }

        public void Trace(String aString)
        {

        }

        #region Session Members

        public event EventHandler<LocalEventArgs> ApplicationEvent;

        public void FireLocalEvent(string anEventName, string someDetails)
        {
            try
            {
                this.CssSession.EventFireLocal(anEventName, someDetails);
            }
            catch (Exception aProblem)
            {
                throw new Exception("Unable to raise local event.", aProblem);                
            }
        }

        public void FireRemoteEvent(string anEventName, string someDetails)
        {
        }

        public bool HasSubscribers(string anEventName)
        {
            try
            {
                return this.CssSession.EventHasSubscribers(anEventName);
            }
            catch (Exception aProblem)
            {
                throw new Exception("Unable to subscribe to event.", aProblem);                
            }
        }

        public void Subscribe(string anEventName)
        {
            try
            {
                this.CssSession.EventSubscribe(anEventName,(ICSS_SessionEvents)this.ComComponent);
            }
            catch (Exception aProblem)
            {
                throw new Exception("Unable to subscribe to event.",aProblem);
            }
        }


        public void Unsubscribe(string anEventName)
        {
            try
            {
                this.CssSession.EventUnsubscribe(anEventName, (ICSS_SessionEvents)this.ComComponent);
            }
            catch (Exception aProblem)
            {
                throw new Exception("Unable to unsubscribe to event.", aProblem);
            }
        }

        
        EventHandler refreshRequested;

        public event EventHandler RefreshRequested
        {
            add
            {
                if (refreshRequested == null)
                {
                    this.Subscribe("REFRESH");
                }

                refreshRequested = (EventHandler)Delegate.Combine(refreshRequested, value);


            }
            remove
            {
                refreshRequested = (EventHandler)Delegate.Remove(refreshRequested, value);
                if (refreshRequested.GetInvocationList().Length == 0)
                {
                    this.Unsubscribe("REFRESH");
                }
            }
        }

  
        #endregion

        public void IncomingEventCallback(String anEventType, String aStub)
        {
            //Cascade "well-known" events here
            if ("REFRESH".Equals(anEventType))
            {
                this.FireRefreshRequestedEvent();
            }
            else
            {
                this.FireApplicationEvent(anEventType, aStub);
            }
        }

        private void FireRefreshRequestedEvent()
        {
            if (this.refreshRequested != null)
            {
                this.refreshRequested.Invoke(this, new EventArgs());
            }
        }


        private void FireApplicationEvent(String anEventType, String someDetails)
        {
            LocalEventArgs args = new LocalEventArgs();
            args.EventType = anEventType;
            args.Details = someDetails;

            if (this.ApplicationEvent != null)
            {
                try
                {
                    this.ApplicationEvent.Invoke(this, args);
                }
                catch
                {
                    //TODO: Decide what to do in case of error.  For now, do not 
                    //disrupt work.
                }
            }
        }

        #region Session Members

        public bool IsLogging
        {
            get { return this.CssSession.TraceMode; }
            set { this.CssSession.TraceMode = value; }
        }


        public void Log(string aClass, string aCategory, params string[] lines)
        {
            if (this.CssSession.TraceMode)
            {
                int handle = this.CssSession.TraceBegin(aClass, aCategory);
                this.LogLines(handle, lines);
                this.CssSession.TraceEnd(handle);
            }
        }


        public void Log(string aClass, string aCategory, Exception anException, params string[] lines)
        {
            if (this.CssSession.TraceMode)
            {
                int handle = this.CssSession.TraceBegin(aClass, aCategory);

                this.LogLines(handle, lines);
                this.LogException(handle, anException);        

                this.CssSession.TraceEnd(handle);
            }
        }

        protected void LogLines(int aHandle, params string[] someLines)
        {
            foreach (string each in someLines)
            {
                String message = null;

                if (each.StartsWith("***"))
                {
                    String header = each.Substring(3, each.IndexOf("***", 4) - 3).Trim();
                    message = each.Substring(3 + 3 + header.Length - 1).Trim();

                    if (header.Length > 0)
                    {
                        this.CssSession.TraceAdd(aHandle, header, true);
                    }
                }
                else
                {
                    message = each;
                }

                if (message.Length > 0)
                {
                    this.CssSession.TraceAdd(aHandle, message, false);
                }
            }
        }

        protected void LogException(int aHandle, Exception anException)
        {
            this.CssSession.TraceAdd(aHandle, "Exception" + ((anException.InnerException != null) ? " (w/Inner)" : ""), true);
            this.CssSession.TraceAdd(aHandle, anException.Message, false);
            this.CssSession.TraceAdd(aHandle, anException.StackTrace, false);
            if (anException.InnerException != null)
            {
                this.CssSession.TraceAdd(aHandle, "Inner Exception", true);
                this.CssSession.TraceAdd(aHandle, anException.InnerException.Message, false);
                this.CssSession.TraceAdd(aHandle, anException.InnerException.StackTrace, false);
            }

        }
        #endregion

        #region Session Members


        /// <summary>
        /// Component is responsible for VueCentric/CSS cleanup
        /// </summary>
        public void Close()
        {
           
        }

        #endregion


        public LocalEventService EventServices
        {
            get { return this; }
        }



        #region LocalSession Members


        public void Notify(String aTitle, Exception anException)
        {
            MessageBox.Show(anException.Message, aTitle);

        }

        public void Notify(String aTitle, String aMessage)
        {
            MessageBox.Show(aMessage, aTitle);
        }

        #endregion

        #region LocalEventService Members


        public void TriggerEvent(string anEventName, string someDetails)
        {
            this.FireLocalEvent(anEventName, someDetails);
        }

        #endregion
    }
}