using System;
using System.Collections.Generic;
using System.Text;

namespace IndianHealthService.BMXNet.Services
{
    internal class BMXRemoteSessionPool : RemoteSessionPool
    {

        private Log _log = new NullLog();

        public Log Log
        {
            get { return _log; }
            set { _log = value; }
        }

        public void Begin(BMXNetSessionConnectionSource aSource)
        {
            this.Source = aSource;
            this.AllowAllocation = true;
        }

        private bool _allowAllocation = false;

        public bool AllowAllocation
        {
            get { return _allowAllocation; }
            set { _allowAllocation = value; }
        }


        private int _maxSessions = 5;

        public int MaxSessions
        {
            get { return _maxSessions; }
            set { _maxSessions = value; }
        }

        public int IdleSessionCount
        {
            get
            {
                return this.IdleConnections.Count;
            }
        }

        public int AvailableSessionCount
        {
            get
            {
                return this.MaxSessions - this.AllocatedSessions.Count;
            }
        }


        private BMXNetSessionConnectionSource _source = null;

        internal BMXNetSessionConnectionSource Source
        {
            get { return _source; }
            set { _source = value; }
        }


        private List<BMXNetSessionConnection> _idleConnections = new List<BMXNetSessionConnection>();

        public List<BMXNetSessionConnection> IdleConnections
        {
            get { return _idleConnections; }
            set { _idleConnections = value; }
        }

        private List<BMXNetRemoteSession> _allocatedSessions = new List<BMXNetRemoteSession>();

        public List<BMXNetRemoteSession> AllocatedSessions
        {
            get { return _allocatedSessions; }
            set { _allocatedSessions = value; }
        }

        #region RemoteSessionPool Members

        public bool HasAvailableSession
        {
            get { return this.AvailableSessionCount > 0; }
        }
        public RemoteSession OpenSession()
        {
            return this.OpenSession(this.Log);
        }
      
        public RemoteSession OpenSession(Log aLog)
        {              
            if (this.HasAvailableSession && this.AllowAllocation)
            {
                BMXNetRemoteSession session = new BMXNetRemoteSession();             
                session.Log = aLog;
                if (this.IdleConnections.Count > 0)
                {
                    this.Log.Log("BMX NET", "Debug", "Open Idle Session ");

                    session.IsPrimary = (this.IdleSessionCount + this.AllocatedSessions.Count) == 1;
                    this.AllocatedSessions.Add(session);
                    BMXNetSessionConnection connection = this.IdleConnections[0];
                    connection.ReceiveTimeout = 0;
                    connection.SendTimeout = 0;
                    this.IdleConnections.Remove(connection);
                    session.Begin(connection, this);
                }
                else
                {
                    this.Log.Log("BMX NET", "Debug", "Open New Session ");
                    session.IsPrimary = (this.IdleSessionCount + this.AllocatedSessions.Count) == 0;
                    this.AllocatedSessions.Add(session);
                    session.Begin(this.Source.OpenConnection(), this);
                }

                if (!session.IsPrimary)
                {
                    session.PrimarySession(this.PrimarySession);
                }
                return (RemoteSession)session;           
            }
            else
            {
                this.Log.Log("BMX NET", "Info", "No Session Available");
                return null;
            }
        }


        private BMXNetRemoteSession PrimarySession
        {
            get
            {
                foreach (BMXNetRemoteSession each in this.AllocatedSessions)
                {
                    if (each.IsPrimary)
                    {
                        return each;
                    }
                }
                return null;
            }
        }

        private void ReleaseSession(RemoteSession aSession)
        {
            BMXNetRemoteSession bmxSession = aSession as BMXNetRemoteSession;

            if (bmxSession.IsPrimary)
            {
                this.Close();
            }
            else
            {
                bmxSession.Suspend(this);
                this.AllocatedSessions.Remove(bmxSession);
                this.IdleConnections.Add(bmxSession.SessionConnection);
            }
        }

        public void CloseSession(RemoteSession aSession)
        {
            if (aSession.IsPrimary)
            {
                this.TerminateAll();
            }
            else
            {
                this.ReleaseSession(aSession);
            }
        }

        public void Close()
        {
            this.AllowAllocation = false;
            foreach (BMXNetRemoteSession each in new List<BMXNetRemoteSession>(this.AllocatedSessions))
            {
                each.IsPrimary = false;
                this.ReleaseSession(each);
            }
            this.TerminateAll();
        }

        #endregion

 

        public void CloseAll()
        {
            List<BMXNetRemoteSession> toClose = new List<BMXNetRemoteSession>(this.AllocatedSessions);
            this.AllocatedSessions = new List<BMXNetRemoteSession>();
            foreach (BMXNetRemoteSession each in toClose)
            {
                this.CloseSession(each);
            }

        }


        private void TerminateAll()
        {
            this.AllowAllocation = false;

            this.CloseAll();
            this.TerminateIdleSessions();
        }


        /// <summary>
        /// Note: idle session connections are really being terminated.  We
        /// do not re-use BMXNetRemoteSession objects that wrap the session connection
        /// </summary>
        public void TerminateIdleSessions()
        {
            List<BMXNetSessionConnection> toTerminate = new List<BMXNetSessionConnection>(this.IdleConnections);
            this.IdleConnections = new List<BMXNetSessionConnection>();
            foreach (BMXNetSessionConnection each in toTerminate)
            {
                this.TerminateConnection(each);
            }
        }

        private void TerminateConnection(BMXNetSessionConnection aConnection)
        {
            this.Source.CloseConnection(aConnection);
        }


     
    }
}