using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Threading;
//using System.Diagnostics;

using Wayne.Lib;
using Wayne.ForecourtControl;
using Wayne.ForecourtControl.Fusion;
using Wayne.FDCPOSLibrary;

//using DWItaly.Sinp.Utility;

namespace Wayne.ForecourtControl.OptBridge.Fusion
{
    public enum OPTManagedBy
    {
        SINP = 1,
        Fusion = 2
    }

    internal class FUSIONOptBridge : IOptBridge, IConnectable, IIdentifiableEntity, IDisposable
    {
        // Fields
        private int clientId;
        private string clientName;
        private DeviceConnectionState connectionState;
        private int id;
        public FUSIONManager manager;
        private List<IOpt> optList = new List<IOpt>();
        private IIdentifiableEntity parentEntity;

        // Events
        public event EventHandler OnConfigurationChanged;

        public event EventHandler<ConnectionChangedEventArgs> OnConnectionStateChange;

        // Methods
        public FUSIONOptBridge(int id, IIdentifiableEntity parentEntity)
        {
            this.id = id;
            this.parentEntity = parentEntity;
            try
            {
                Trace.Add(this);
                if (!FUSIONFactory.fusionForecourtControlList.ContainsKey(id))
                {
                    Trace.WriteLine(string.Format("fusionForecourtControl id={0} NOT exist - creating", id));
                    FUSIONFactory.CreateForecourtControl(id, NoPumpAuthorizationIdGenerator.Instance);
                }

                if (FUSIONFactory.fusionForecourtControlList.ContainsKey(id))
                {
                    Trace.WriteLine(string.Format("fusionForecourtControl id={0} adding event OnConnectionStateChange", id));
                    manager = ((FUSIONForecourtControl)(FUSIONFactory.fusionForecourtControlList[id])).manager;
                    this.manager.ifsfManager.clientSocket.OnConnectionStateChange += new EventHandler<ConnectionChangedEventArgs>(clientSocket_OnConnectionStateChange); //new EventHandler<ConnectionChangedEventArgs>(this, (IntPtr)this.clientSocket_OnConnectionStateChange);
                }
                else
                    Trace.WriteLine(string.Format("fusionForecourtControl id={0} NOT exist - error!!!", id));
            }
            catch (Exception ex)
            {
                Trace.WriteLine(string.Format("Exception creating FUSIONOptBridge ! '{0}'", ex.Message + " - " + ex.StackTrace));
            }
        }

        public void AddOptAsync(int optId, EventHandler<AsyncCompletedEventArgs> requestCompleted, object userToken)
        {
            //this.SetTerminalModeAsync(optId, NfsTerminalMode.Configured, requestCompleted, userToken);
            string sValue = "";
            IOpt opt = null;
            try
            {
                opt = this.GetOptFromId(optId);
                if (opt == null)
                {
                    opt = new FUSIONOpt(optId, this);
                    this.WritableOptList.Add(opt);
                }
                //else if (((FUSIONOpt)opt).managedBy == OPTManagedBy.SINP)
                //    return;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(string.Format("Exception adding opt ! '{0}'", ex.Message + " - " + ex.StackTrace));
            }

            try
            {
                sValue = IniFile.IniReadValue(ConfigurationParams.inifile, "OPT" + optId.ToString(), "Port");
                if (sValue.Length > 0)
                {
                    //sValue = sValue.ToUpper().Replace("COM", "");
                    ((FUSIONOpt)opt).COMPort = sValue;
                    Trace.WriteLine(string.Format("port={0}", ((FUSIONOpt)opt).COMPort));
                    sValue = IniFile.IniReadValue(ConfigurationParams.inifile, "OPT" + optId.ToString(), "BaudRate");
                    if (sValue.Length > 0)
                        ((FUSIONOpt)opt).baudRate = Convert.ToInt16(sValue);
                    Trace.WriteLine(string.Format("baudRate={0}", ((FUSIONOpt)opt).baudRate));
                    sValue = IniFile.IniReadValue(ConfigurationParams.inifile, "OPT" + optId.ToString(), "DataBit");
                    if (sValue.Length > 0)
                        ((FUSIONOpt)opt).dataBit = Convert.ToInt16(sValue);
                    Trace.WriteLine(string.Format("dataBit={0}", ((FUSIONOpt)opt).dataBit));
                    sValue = IniFile.IniReadValue(ConfigurationParams.inifile, "OPT" + optId.ToString(), "StopBit");
                    if (sValue.Length > 0)
                        ((FUSIONOpt)opt).stopBit = Convert.ToInt16(sValue);
                    Trace.WriteLine(string.Format("stopBit={0}", ((FUSIONOpt)opt).stopBit));
                    sValue = IniFile.IniReadValue(ConfigurationParams.inifile, "OPT" + optId.ToString(), "Parity");
                    if (sValue.Length > 0)
                        ((FUSIONOpt)opt).parity = Convert.ToInt16(sValue);
                    Trace.WriteLine(string.Format("parity={0}", ((FUSIONOpt)opt).parity));
                    ((FUSIONOpt)opt).managedBy = OPTManagedBy.Fusion;

                    if (this.manager == null || this.manager.ifsfManager == null)
                        Trace.WriteLine((this.manager == null) ? "this.manager == null" : "this.manager.ifsfManager == null");
                    else
                    {
                        Trace.WriteLine(string.Format("OptAddSerialPort calling"));
                        this.manager.ifsfManager.OptAddSerialPort(optId, ((FUSIONOpt)opt).COMPort, ((FUSIONOpt)opt).baudRate, ((FUSIONOpt)opt).dataBit, ((FUSIONOpt)opt).stopBit, ((FUSIONOpt)opt).parity, requestCompleted, userToken, this);
                    }

                    Trace.WriteLine(string.Format("OptAddSerialPort called"));
                }
                else
                {
                    sValue = IniFile.IniReadValue(ConfigurationParams.inifile, "OPT" + optId.ToString(), "IPAddress");
                    Trace.WriteLine(string.Format("IPAddress={0}", sValue));
                    if (sValue.Length > 0)
                    {
                        ((FUSIONOpt)opt).address = sValue;
                        sValue = IniFile.IniReadValue(ConfigurationParams.inifile, "OPT" + optId.ToString(), "IPPort");
                        if (sValue.Length > 0)
                            ((FUSIONOpt)opt).IPPort = Convert.ToInt16(sValue);
                        Trace.WriteLine(string.Format("port={0}", ((FUSIONOpt)opt).IPPort));
                        sValue = IniFile.IniReadValue(ConfigurationParams.inifile, "OPT" + optId.ToString(), "ManagedBy");
                        if (sValue.Length == 0 || sValue.ToUpper() == "FUSION")
                            ((FUSIONOpt)opt).managedBy = OPTManagedBy.Fusion;
                        else
                            ((FUSIONOpt)opt).managedBy = OPTManagedBy.SINP;
                        Trace.WriteLine(string.Format("managedBy={0}", sValue));
                        if (((FUSIONOpt)opt).managedBy == OPTManagedBy.Fusion && manager != null && manager.ifsfManager != null)
                            this.manager.ifsfManager.OptAddTCP(optId, ((FUSIONOpt)opt).address, ((FUSIONOpt)opt).IPPort, requestCompleted, userToken, this);
                        //OPTTCP
                        else
                        {
                            // TODO sistemare 
                            if (((FUSIONOpt)opt).optTCP == null)
                            {
                                ((FUSIONOpt)opt).optTCP = new OptTCP(optId, ((FUSIONOpt)opt).address, ((FUSIONOpt)opt).IPPort, ((FUSIONOpt)opt));
                            }
                            // send request completed ...
                            requestCompleted.Invoke(this, new AsyncCompletedEventArgs(true, userToken));
                            ((FUSIONOpt)opt).optTCP.ConnectionState = DeviceConnectionState.Disconnected;
                            ((FUSIONOpt)opt).optTCP.Connect();
                        }
                    }
                }
                if (((FUSIONOpt)opt).managedBy == OPTManagedBy.Fusion && manager != null && manager.ifsfManager != null)
                    manager.ifsfManager.GetDeviceState(Wayne.FDCPOSLibrary.DeviceType.DT_OutdoorPaymentTerminal, optId, null, null, null);
                Trace.WriteLine(string.Format("GetDeviceState called"));
            }
            catch (Exception ex)
            {
                Trace.WriteLine(string.Format("Exception ! '{0}'", ex.Message + " - " + ex.StackTrace));
            }
        }

        public void AddOptAsync(int optId, string port, int baudRate, int dataBit, int stopBit, int parity, EventHandler<AsyncCompletedEventArgs> requestCompleted, object userToken)
        {
            //this.SetTerminalModeAsync(optId, NfsTerminalMode.Configured, requestCompleted, userToken);
            IOpt opt = this.GetOptFromId(optId);
            if (opt == null)
            {
                opt = new FUSIONOpt(optId, this);
                this.WritableOptList.Add(opt);
            }
            this.manager.ifsfManager.OptAddSerialPort(optId, port, baudRate, dataBit, stopBit, parity, requestCompleted, userToken, this);
            manager.ifsfManager.GetDeviceState(Wayne.FDCPOSLibrary.DeviceType.DT_OutdoorPaymentTerminal, optId, null, null, null);
        }

        public void AddOptAsync(int optId, string address, int port, EventHandler<AsyncCompletedEventArgs> requestCompleted, object userToken)
        {
            //this.SetTerminalModeAsync(optId, NfsTerminalMode.Configured, requestCompleted, userToken);
            IOpt opt = this.GetOptFromId(optId);
            if (opt == null)
            {
                opt = new FUSIONOpt(optId, this);
                this.WritableOptList.Add(opt);
            }
            if (((FUSIONOpt)opt).managedBy == OPTManagedBy.Fusion && manager != null && manager.ifsfManager != null)
            {
                this.manager.ifsfManager.OptAddTCP(optId, address, port, requestCompleted, userToken, this);
                manager.ifsfManager.GetDeviceState(Wayne.FDCPOSLibrary.DeviceType.DT_OutdoorPaymentTerminal, optId, null, null, null);
            }
            //OPTTCP
            else
            {
                // TODO sistemare 
                ((FUSIONOpt)opt).optTCP = new OptTCP(optId, address, port, ((FUSIONOpt)opt));
                // send request completed ...
                requestCompleted.Invoke(this, new AsyncCompletedEventArgs(true, userToken));
                ((FUSIONOpt)opt).optTCP.ConnectionState = DeviceConnectionState.Disconnected;
                ((FUSIONOpt)opt).optTCP.Connect();
            }
        }

        public void Connect(string connectionString)
        {
            //if (this.manager.ifsfManager.clientSocket.mainConnectionState == DeviceConnectionState.Connected)
            WritableConnectionState = DeviceConnectionState.Connected;
            //else
            //    this.manager.Connect(connectionString);
        }

        public void Disconnect()
        {
            this.manager.Disconnect();
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (disposing && (this.manager != null))
            {
                this.manager.Dispose();
            }
            this.manager = null;
        }

        ~FUSIONOptBridge()
        {
            this.Dispose(false);
        }

        internal void FireOnConfigurationChanged()
        {
            if (this.OnConfigurationChanged != null)
            {
                this.OnConfigurationChanged(this, EventArgs.Empty);
            }
        }

        internal void FireOnDataRead(int optId, byte[] data)
        {
            FUSIONOpt opt = this.GetOptFromId(optId);
            if (opt != null)
            {
                opt.FireOnDataRead(data);
            }
        }

        public void RemoveOptAsync(int optId, EventHandler<AsyncCompletedEventArgs> requestCompleted, object userToken)
        {
            //this.SetTerminalModeAsync(optId, NfsTerminalMode.NotConfigured, requestCompleted, userToken);
            this.manager.ifsfManager.OptRemove(optId, requestCompleted, userToken, this);
        }

        //private void ReserveCompleted(object sender, AsyncCompletedEventArgs e)
        //{
        //    SetTerminalModeParams userToken = e.UserToken as SetTerminalModeParams;
        //    if (userToken != null)
        //    {
        //        if (e.Success)
        //        {
        //            AsyncOperation<ushort> operation = this.manager.AsyncManager.RegisterOperation<AsyncCompletedEventArgs>(this, new EventHandler<AsyncCompletedEventArgs>(this.SetTerminalModeCompleted), userToken);
        //            Function function = new Function(0x7d3, new Parameter[] { new PrimitiveParameter(0x66, PrimitiveType.UInt16, operation.Id), new PrimitiveParameter(5, PrimitiveType.Byte, userToken.OptId), new PrimitiveParameter(0x25b, PrimitiveType.Byte, NfsTerminalMode.Configured) });
        //            this.manager.Send(function);
        //        }
        //        else if (userToken.RequestCompleted != null)
        //        {
        //            userToken.RequestCompleted(this, new AsyncCompletedEventArgs(false, userToken.UserToken));
        //        }
        //    }
        //}

        //private void SetTerminalModeAsync(int optId, NfsTerminalMode terminalMode, EventHandler<AsyncCompletedEventArgs> requestCompleted, object userToken)
        //{
        //    SetTerminalModeParams params = new SetTerminalModeParams(optId, terminalMode, requestCompleted, userToken);
        //    AsyncOperation<ushort> operation = this.manager.AsyncManager.RegisterOperation<AsyncCompletedEventArgs>(this, new EventHandler<AsyncCompletedEventArgs>(this.ReserveCompleted), params);
        //    Function function = new Function(0x7d1, new Parameter[] { new PrimitiveParameter(0x66, PrimitiveType.UInt16, operation.Id), new PrimitiveParameter(5, PrimitiveType.Byte, optId) });
        //    this.manager.Send(function);
        //}

        //private void SetTerminalModeCompleted(object sender, AsyncCompletedEventArgs e)
        //{
        //    SetTerminalModeParams userToken = e.UserToken as SetTerminalModeParams;
        //    if (userToken != null)
        //    {
        //        AsyncOperation<ushort> operation = this.manager.AsyncManager.RegisterOperation<AsyncCompletedEventArgs>(this, new EventHandler<AsyncCompletedEventArgs>(this.UnreserveCompleted), userToken);
        //        userToken.Success = e.Success;
        //        Function function = new Function(0x7d2, new Parameter[] { new PrimitiveParameter(0x66, PrimitiveType.UInt16, operation.Id), new PrimitiveParameter(5, PrimitiveType.Byte, userToken.OptId) });
        //        this.manager.Send(function);
        //    }
        //}

        //private void UnreserveCompleted(object sender, AsyncCompletedEventArgs e)
        //{
        //    SetTerminalModeParams userToken = e.UserToken as SetTerminalModeParams;
        //    if ((userToken != null) && (userToken.RequestCompleted != null))
        //    {
        //        userToken.RequestCompleted(this, new AsyncCompletedEventArgs(userToken.Success, userToken.UserToken));
        //    }
        //}

        // Properties
        public int ClientId
        {
            get
            {
                return this.clientId;
            }
        }

        public string ClientName
        {
            get
            {
                return this.clientName;
            }
        }

        public DeviceConnectionState ConnectionState
        {
            get
            {
                return this.connectionState;
            }
        }

        public string EntitySubType
        {
            get
            {
                return "Nfs";
            }
        }

        public string EntityType
        {
            get
            {
                return "OptBridge";
            }
        }

        /// <summary>
        /// This is used by the logger and should never be set by inheriting classes
        /// </summary>
        public string FullEntityName { get; set; }

        public int Id
        {
            get
            {
                return this.id;
            }
        }

        public ReadOnlyCollection<IOpt> Opts
        {
            get
            {
                return this.optList.AsReadOnly();
            }
        }

        public IIdentifiableEntity ParentEntity
        {
            get
            {
                return this.parentEntity;
            }
        }

        internal int WritableClientId
        {
            get
            {
                return this.clientId;
            }
            set
            {
                this.clientId = value;
            }
        }

        internal string WritableClientName
        {
            get
            {
                return this.clientName;
            }
            set
            {
                this.clientName = value;
            }
        }

        internal DeviceConnectionState WritableConnectionState
        {
            get
            {
                return this.connectionState;
            }
            set
            {
                if (this.connectionState != value)
                {
                    this.connectionState = value;
                    if (this.OnConnectionStateChange != null)
                    {
                        this.OnConnectionStateChange(this, new ConnectionChangedEventArgs(this.connectionState));
                    }
                }
            }
        }

        private void clientSocket_OnConnectionStateChange(object sender, ConnectionChangedEventArgs e)
        {
            Trace.WriteLine("bridge clientSocket_OnConnectionStateChange ConnectionState=" + e.ConnectionState);
            this.WritableConnectionState = e.ConnectionState;
            if (e.ConnectionState == DeviceConnectionState.Disconnected)
            {
                foreach (IOpt opt in Opts)
                {
                    if (((FUSIONOpt)opt).managedBy == OPTManagedBy.Fusion)
                        ((FUSIONOpt)opt).WritableConnectionState = DeviceConnectionState.Disconnected;
                }
            }
        }

        internal List<IOpt> WritableOptList
        {
            get
            {
                return this.optList;
            }
        }

        public FUSIONOpt GetOptFromId(int deviceId)
        {
            foreach (FUSIONOpt opt in this.Opts)
            {
                if (opt.Id == deviceId)
                {
                    return opt;
                }
            }
            return null;
        }

        // Nested Types
        //private class SetTerminalModeParams
        //{
        //    // Fields
        //    private int optId;
        //    private EventHandler<AsyncCompletedEventArgs> requestCompleted;
        //    private bool success;
        //    private NfsTerminalMode terminalMode;
        //    private object userToken;

        //    // Methods
        //    public SetTerminalModeParams(int optId, NfsTerminalMode terminalMode, EventHandler<AsyncCompletedEventArgs> requestCompleted, object userToken)
        //    {
        //        this.optId = optId;
        //        this.requestCompleted = requestCompleted;
        //        this.userToken = userToken;
        //        this.terminalMode = terminalMode;
        //    }

        //    // Properties
        //    public int OptId
        //    {
        //        get
        //        {
        //            return this.optId;
        //        }
        //    }

        //    public EventHandler<AsyncCompletedEventArgs> RequestCompleted
        //    {
        //        get
        //        {
        //            return this.requestCompleted;
        //        }
        //    }

        //    public bool Success
        //    {
        //        get
        //        {
        //            return this.success;
        //        }
        //        set
        //        {
        //            this.success = value;
        //        }
        //    }

        //    public NfsTerminalMode TerminalMode
        //    {
        //        get
        //        {
        //            return this.terminalMode;
        //        }
        //    }

        //    public object UserToken
        //    {
        //        get
        //        {
        //            return this.userToken;
        //        }
        //    }
        //}
    }

}