using System;
using System.Data;
using System.Windows.Forms;
using IndianHealthService.BMXNet.Model;
using IndianHealthService.BMXNet.Ado;

namespace IndianHealthService.BMXNet.Services
{

    /// <summary>
    /// Extends BMX functionality for easier development and debugging.
    /// Copied from Component Framework project IndianHealthService.Xo.Framework.Rpms
    /// </summary>
    public class BMXNetRpcSingleThreadedSession : RemoteSession, RemoteEventService
    {

        private BMXNetBroker m_broker;

        public BMXNetBroker Broker
        {
            get { return m_broker; }
            set { m_broker = value; }
        }
        private BMXNetDataAdapter m_cachedDataAdapter;

        private String _debugLastRpcSignature;

        public String DebugLastRpcSignature
        {
            get { return _debugLastRpcSignature; }
            set { _debugLastRpcSignature = value; }
        }

        private String _debugLastResult;

        public String DebugLastResult
        {
            get { return _debugLastResult; }
            set { _debugLastResult = value; }
        }


        private String _rpcResult;


        public BMXNetRpcSingleThreadedSession()
        {
            this.m_cachedDataAdapter = new BMXNetDataAdapter();
        }

        public void AuthenticatedBroker(BMXNetBroker aBroker)
        {
            this.Broker = aBroker;
            this.StartPollingTimer();
        }

        private void StartPollingTimer()
        {
            this.PollingTimer = new System.Timers.Timer(this.EventPollingInterval);
            this.PollingTimer.Enabled = this.IsEventPollingEnabled;
            this.PollingTimer.Start();
        }

        public void AuthenticatedConnectionInfo(BMXNetBroker aBroker)
        {
            this.Broker = aBroker;
            this.StartPollingTimer();
        }

        public virtual String TransmitRPC(String rpcCommand, String rpcParameter, String context)
        {

            if (rpcParameter.Length > 32600)
            {
                throw new Exception("RPC parameter length exceeds maximum allowable size.");
            }

            this.DebugLastRpcSignature = rpcCommand + "^" + rpcParameter + "^" + context;
            this.Broker.AppContext = context;

            this.RpcResult = this.Broker.TransmitRPC(rpcCommand, rpcParameter);

            this.DebugLastResult = this.RpcResult;


            return this.RpcResult;
        }

        public String SafelyTransmitRPC(String rpcCommand, String rpcParameter, String context)
        {
            try
            {
                return this.TransmitRPC(rpcCommand, rpcParameter, context);
            }
            catch (Exception exception)
            {
                return "Exception: " + exception.Message;
            }
        }

        //TODO: Fix Broker - remove this stuff
        private void FixUpBrokerBMXIENissue(DataTable aDataTable)
        {
            if (aDataTable.Columns.IndexOf("NEW ROW") == -1)
                return;

            foreach (DataRow each in aDataTable.Rows)
            {
                each["NEW ROW"] = each["BMXIEN"];
            }
        }


        public bool SaveChanges(DataTable aDataTable)
        {
            BMXNetConnection bmxConnection;
            BMXNetCommand bmxSelectCommand;
            BMXNetCommand bmxUpdateCommand;
            BMXNetDataAdapter bmxDataAdaptor;

            DataTable changes = aDataTable.GetChanges();
            if (changes == null)
                return false;

            this.FixUpBrokerBMXIENissue(changes);

            this.Broker.AppContext = "BMXRPC";

            bmxConnection = new BMXNetConnection(this.Broker);

            bmxConnection.Open();
            if (bmxConnection.State != ConnectionState.Open)
                throw new BMXNetException("Unable to connect to RPMS.");

            try
            {
                bmxDataAdaptor = this.m_cachedDataAdapter;

                bmxSelectCommand = bmxConnection.CreateCommand() as BMXNetCommand;
                bmxSelectCommand.CommandText = aDataTable.ExtendedProperties["BMXNetSelectStatementForUpdate"] as String;
                bmxDataAdaptor.SelectCommand = bmxSelectCommand;

                DataTable schema = bmxDataAdaptor.FillSchema(aDataTable, SchemaType.Source);
                bmxUpdateCommand = bmxConnection.CreateCommand() as BMXNetCommand;
                bmxUpdateCommand.BMXBuildUpdateCommand(schema);
                bmxDataAdaptor.UpdateCommand = bmxUpdateCommand;

                bmxDataAdaptor.Update(changes);
                aDataTable.AcceptChanges();

                return true;
            }
            catch (Exception exception)
            {
                throw new BMXNetException("Unable to save data to RPMS.", exception);
            }
            finally
            {
                if (bmxConnection != null)
                    bmxConnection.ToString();
            }

        }


        public virtual DataTable TransmitTableGenerationRPC(String generationString)
        {
            return this.TransmitTableGenerationRPC(generationString, new DataSet());
        }


        public virtual DataTable TransmitTableGenerationRPC(String generationString, DataSet aDataSet)
        {
            BMXNetConnection bmxConnection;
            BMXNetCommand bmxCommand;
            BMXNetDataAdapter bmxDataAdaptor;

            this.Broker.AppContext = "BMXRPC";
            bmxConnection = new BMXNetConnection(this.Broker);

            bmxConnection.Open();
            if (bmxConnection.State != ConnectionState.Open)
                throw new BMXNetException("Unable to connect to RPMS.");

            try
            {
                bmxCommand = bmxConnection.CreateCommand() as BMXNetCommand;
                bmxCommand.CommandText = generationString;
                bmxDataAdaptor = new BMXNetDataAdapter();
                bmxDataAdaptor.SelectCommand = bmxCommand;

                DataSet dataSet = aDataSet;
                bmxDataAdaptor.Fill(dataSet);
                DataTable answer = dataSet.Tables[0];

                answer.ExtendedProperties["BMXNetSelectStatementForUpdate"] = bmxCommand.CommandText;
                answer.ExtendedProperties["RpmsQuery"] = generationString;
                answer.ExtendedProperties["RpmsSchema"] = this.RpmsSchema(generationString);

                return answer;
            }
            catch (Exception exception)
            {
                throw new Exception("Unable to retrive data from RPMS.", exception);
            }
            finally
            {
                if (bmxConnection != null)
                    bmxConnection.ToString();
            }

        }



        public virtual DataTable TransmitTableGenerationRPC(String generationString, DataSet aDataSet, String aTableName)
        {
            BMXNetConnection bmxConnection;
            BMXNetCommand bmxCommand;
            BMXNetDataAdapter bmxDataAdaptor;

            String initialAppContext = this.Broker.AppContext;

            if (generationString.StartsWith("BMX"))
            {
                this.Broker.AppContext = "BMXRPC";
            }

            bmxConnection = new BMXNetConnection(this.Broker);

            bmxConnection.Open();
            if (bmxConnection.State != ConnectionState.Open)
            {
                throw new BMXNetException("Unable to connect to RPMS.");
            }

            try
            {
                bmxCommand = bmxConnection.CreateCommand() as BMXNetCommand;
                bmxCommand.CommandText = generationString;
                bmxDataAdaptor = new BMXNetDataAdapter();
                bmxDataAdaptor = this.m_cachedDataAdapter;
                bmxDataAdaptor.SelectCommand = bmxCommand;

                DataSet dataSet = aDataSet;
                DataTable answer = aDataSet.Tables.Add(aTableName);
                bmxDataAdaptor.Fill(answer);

                answer.ExtendedProperties["BMXNetSelectStatementForUpdate"] = bmxCommand.CommandText;
                answer.ExtendedProperties["RpmsQuery"] = generationString;
                answer.ExtendedProperties["RpmsSchema"] = this.RpmsSchema(generationString);

                return answer;
            }
            catch (Exception exception)
            {
                throw new Exception("Unable to retrive data from RPMS.", exception);
            }
            finally
            {
                if (bmxConnection != null)
                    bmxConnection.ToString();

                this.Broker.AppContext = initialAppContext;
            }
        }




        private String RpmsSchema(String aString)
        {
            char[] carrotDelimiter = "^".ToCharArray();
            String[] parts = aString.Split(carrotDelimiter);
            return parts[1];
        }

        public DataTable TableFromRPC(String rpcCommand, String rpcParameter, String context)
        {
            return this.TableFromRPC(rpcCommand, rpcParameter, context, new DataSet());
        }



        public DataTable TableFromRPC(String rpcCommand, String rpcParameter, String context, DataSet aDataSet)
        {
            String tableGenerationRpc = this.TransmitRPC(rpcCommand, rpcParameter, context);

            if (IsTableGenerationRpc(tableGenerationRpc))
                return this.TransmitTableGenerationRPC(tableGenerationRpc, aDataSet);
            else
            {
                return null;
            }
        }


        /* BAD
                public DataTable TableFromRPC(String rpcCommand,String rpcParameter,String context,DataSet aDataSet, String aTableName)
                {
                    String tableGenerationRpc=this.TransmitRPC(rpcCommand,rpcParameter,context);

                    if (IsTableGenerationRpc(tableGenerationRpc))
                        return this.TransmitTableGenerationRPC(tableGenerationRpc, aDataSet, aTableName);
                    else
                    {
                         return null;
                    }
                }
        */
        public String RpcResult
        {
            get { return this._rpcResult; }
            set { this._rpcResult = value; }
        }

        public virtual DataTable TableFromSQL(String sql)
        {
            return this.TableFromSQL(sql, new DataSet());
        }


        public DataTable TableFromSQL(string sql, DataSet aDataSet)
        {
            return this.TableFromSQL(sql, aDataSet, "ResultTable");
        }

        public DataTable TableFromSQL(string sql, DataSet aDataSet, string aTableName)
        {
            BMXNetConnection bmxConnection;
            BMXNetCommand bmxCommand;
            BMXNetDataAdapter bmxDataAdaptor;

            this.Broker.AppContext = "BSDXRPC";
            bmxConnection = new BMXNetConnection(this.Broker);

            bmxConnection.Open();
            if (bmxConnection.State != ConnectionState.Open)
            {
                throw new BMXNetException("Unable to connect to RPMS.");
            }

            try
            {
                bmxCommand = bmxConnection.CreateCommand() as BMXNetCommand;
                bmxCommand.CommandText = sql;
                bmxDataAdaptor = new BMXNetDataAdapter();
                bmxDataAdaptor = this.m_cachedDataAdapter;
                bmxDataAdaptor.SelectCommand = bmxCommand;

                bmxDataAdaptor.Fill(aDataSet,aTableName);

                DataTable answer = aDataSet.Tables[aTableName];

                answer.ExtendedProperties["BMXNetSelectStatementForUpdate"] = bmxCommand.CommandText;
                answer.ExtendedProperties["SqlQuery"] = sql;

                return answer;
            }
            catch (Exception exception)
            {
                throw new Exception("Unable to retrive sql data from RPMS.", exception);
            }
            finally
            {
                if (bmxConnection != null)
                    bmxConnection.ToString();
                //TODO: bmxConnection.Close();
                //BMX framework lifecylce needs to be better defined.
            }

        }


        public bool IsTableGenerationRpc(String aString)
        {
            String validFetchTransactionPrefix = "BMX ADO SS";

            return aString.StartsWith(validFetchTransactionPrefix);
        }


        public virtual void Close()
        {
            this.Broker.CloseConnection();
            this.PollingTimer.Close();           
        }



        public string HostAddress
        {
            get { throw new NotImplementedException(); }
        }

        public string DUZ
        {
            get { return this.Broker.DUZ; }
        }


        public string UserName
        {
            get { return this.Broker.UserName; }
        }

        public string AppContext
        {
            get
            {
                return this.Broker.AppContext;
            }
            set
            {
                this.Broker.AppContext = value;
            }
        }

        public string TransmitRPC(string rpcCommand, string rpcParameter)
        {
            return this.TransmitRPC(rpcCommand, rpcParameter, this.AppContext);
        }

        public string SafelyTransmitRPC(string rpcCommand, string rpcParameter)
        {
            return this.SafelyTransmitRPC(rpcCommand, rpcParameter, this.AppContext);
        }

        public System.Data.DataTable TableFromRPC(string rpcCommand, string rpcParameter)
        {
            return this.TableFromRPC(rpcCommand, rpcParameter, this.AppContext);
        }

        public System.Data.DataTable TableFromRPC(string rpcCommand, string rpcParameter, System.Data.DataSet aDataSet)
        {
            return this.TableFromRPC(rpcCommand, rpcParameter, aDataSet);
        }



        #region BMXNetSession Members


        public DataTableFuture AsyncTableFromRPC(string rpcCommand, string rpcParameter)
        {
            throw new NotImplementedException();
        }

        public DataTableFuture AsyncTableFromRPC(string rpcCommand, string rpcParameter, DataSet aDataSet)
        {
            throw new NotImplementedException();
        }

        public DataTableFuture AsyncTableFromRPC(string rpcCommand, string rpcParameter, string context)
        {
            throw new NotImplementedException();
        }

        public DataTableFuture AsyncTableFromRPC(string rpcCommand, string rpcParameter, string context, DataSet aDataSet)
        {
            throw new NotImplementedException();
        }

        public event EventHandler<RemoteEventArgs> RemoteEvent;

        #endregion



        DataTableFuture RemoteSession.AsyncTableFromRPC(string rpcCommand, string rpcParameter)
        {
            throw new NotImplementedException();
        }

        DataTableFuture RemoteSession.AsyncTableFromRPC(string rpcCommand, string rpcParameter, DataSet aDataSet)
        {
            throw new NotImplementedException();
        }

        DataTableFuture RemoteSession.AsyncTableFromRPC(string rpcCommand, string rpcParameter, string context)
        {
            throw new NotImplementedException();
        }

        DataTableFuture RemoteSession.AsyncTableFromRPC(string rpcCommand, string rpcParameter, string context, DataSet aDataSet)
        {
            throw new NotImplementedException();
        }
   
    

        public bool HasSubscribers(string anEventName)
        {
            throw new NotImplementedException();
        }

        public int Subscribe(string anEventName)
        {
            return this.EventManagmentCall("BMX EVENT REGISTER^" + anEventName);
        }

        public int Unsubscribe(string anEventName)
        {
            return this.EventManagmentCall("BMX EVENT UNREGISTER^" + anEventName);
        }

        public int RaiseEvent(string EventName, string Param, bool RaiseBack)
        {
            return this.EventManagmentCall("BMX EVENT RAISE^" + EventName + "^" + Param + "^" + RaiseBack.ToString().ToUpper() + "^");
        }


        public int TriggerEvent(string anEventName, string someDetails)
        {
            throw new NotImplementedException();
        }

        protected int EventManagmentCall(String aCommand)
        {
            try
            {
                DataTable table = this.TransmitTableGenerationRPC(aCommand);
                return (int)table.Rows[0]["ERRORID"];
            }
            catch (Exception ex)
            {
             //   Debug.Write(ex.Message);
                return 99;
            }
        }



        //                    this.NativeNetLib.BMXRWL.ReleaseWriterLock();
        //				this.m_timerEvent.Enabled = true;
        //Disable timer before and after this

        protected void PollForEvent()
        {
            this.PollForTimerEvent();

            if (this.RpmsEvent != null)
            {
                DataTable events = this.TransmitTableGenerationRPC("BMX EVENT POLL");
                foreach (DataRow row in events.Rows)
                {
                    try
                    {
                        RemoteEventArgs args = new RemoteEventArgs();
                        args.EventType = row["EVENT"].ToString();
                        args.Details = row["PARAM"].ToString();
              
                        this.RpmsEvent(this, args);
                    }
                    catch
                    {
                    }
                }
            }
        }

        private void PollForTimerEvent()
        {
        }

        public RemoteEventService EventServices
        {
            get { return this; }
        }

        private System.Timers.Timer _pollingTimer = null;

        public System.Timers.Timer PollingTimer
        {
            get { return _pollingTimer; }
            set { _pollingTimer = value; }
        }

        public event EventHandler<RemoteEventArgs> RpmsEvent;
        public event EventHandler TimerEvent;

        private int _eventPollingInterval = 5000;

        public int EventPollingInterval
        {
            get { return _eventPollingInterval; }
            set
            {
                _eventPollingInterval = value;
                if (this.PollingTimer != null)
                {
                    this.PollingTimer.Interval = value;
                }
            }
        }
        private double _timerEventPollingMultiple = 1;

        public double TimerEventPollingMultiple
        {
            get { return _timerEventPollingMultiple; }
            set { _timerEventPollingMultiple = value; }
        }

        private bool _isEventPollingEnabled = false;

        public bool IsEventPollingEnabled
        {
            get { return _isEventPollingEnabled; }
            set
            {
                _isEventPollingEnabled = value;
                if (this.PollingTimer != null)
                {
                    this.PollingTimer.Enabled = value;
                }
            }
        }



        public DataTable VersionInfo
        {
            get { return this.TransmitTableGenerationRPC("BMX VERSION INFO^"); }  //this.Broker.NameSpace); }
        }




    }
}