using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using Timer = System.Timers.Timer;
using System.Collections;
using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
using Wayne.FDCPOSLibrary;
using System.Xml;
using Edge.Core.Database.Models;
using System.Threading.Tasks;

namespace FuRen_Sinopec_IcCardReader
{
    /// <summary>
    /// This pump handler only listen the ic card reader, will never send message.
    /// </summary>
    public class SilentPumpHandler : IFdcPumpController, IDeviceHandler<byte[], KaJiLianDongV11MessageTemplateBase>
    {
        static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("PumpHandler");

        protected IContext<byte[], KaJiLianDongV11MessageTemplateBase> context;
        public event EventHandler<FdcPumpControllerOnStateChangeEventArg> OnStateChange;
        /// <summary>
        /// fired on fueling process is on going, the fuel amount should keep changing.
        /// </summary>
        public event EventHandler<FdcTransactionDoneEventArg> OnCurrentFuellingStatusChange;

        protected LogicalDeviceState lastLogicalDeviceState = LogicalDeviceState.FDC_CLOSED;
        private DateTime lastLogicalDeviceStateReceivedTime;
        // by seconds
        private const int lastLogicalDeviceStateExpiredTime = 6;

        private Guid uniqueId = Guid.NewGuid();
        private int pumpId = -1;
        protected List<LogicalNozzle> nozzles = new List<LogicalNozzle>();

        public IEnumerable<LogicalNozzle> Nozzles => this.nozzles;

        /// <summary>
        /// </summary>
        /// <param name="pumpId"></param>
        /// <param name="nozzlesXmlConfiguration"></param>
        public SilentPumpHandler(int pumpId)
        {
            this.pumpId = pumpId;
            //this.nozzles.Add(new LogicalNozzle(pumpId, 1, 1, null));
        }

        public void Init(IContext<byte[], KaJiLianDongV11MessageTemplateBase> context)
        {
            this.context = context;
        }

        public virtual async Task Process(IContext<byte[], KaJiLianDongV11MessageTemplateBase> context)
        {
            this.context = context;
            this.lastLogicalDeviceStateReceivedTime = DateTime.Now;
            //PumpStateChangeCommand  -- it's also the PC polling message response from pump.
            //PumpNotifyTransactionDoneCommand

            if (this.lastLogicalDeviceState == LogicalDeviceState.FDC_OFFLINE)
            {
                logger.Info("Pump: " + this.pumpId + ", " + "Recevied an IcCard Msg in FDC_OFFLINE state, " +
                    "indicates the underlying connection is established, switch to FDC_READY");
                this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
                var safe = this.OnStateChange;
                safe?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_READY));
            }

            if (context.Incoming.Message is PumpStateChangeCommand PumpStateChangeCommand)
            {
                if (PumpStateChangeCommand.StateNozzleOperatingSubMessages == null
                    && PumpStateChangeCommand.StateNozzleOperatingSubMessages == null)
                {
                    if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_READY)
                    {
                        this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
                        var safe2 = this.OnStateChange;
                        safe2?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(
                            LogicalDeviceState.FDC_READY,
                            this.nozzles.FirstOrDefault()));
                    }
                }

                if (PumpStateChangeCommand.StateNozzleOperatingSubMessages != null
                    && PumpStateChangeCommand.StateNozzleOperatingSubMessages.Any(n =>
                        n.St状态字 == PumpStateChangeNozzleOperatingSubState.PumpStateChangeCode.抬枪或加油中))
                {
                    if (!this.nozzles.Any())
                        this.nozzles.Add(new LogicalNozzle(this.pumpId, 0,
                            PumpStateChangeCommand.StateNozzleOperatingSubMessages.First().MZN枪号,
                            PumpStateChangeCommand.StateNozzleOperatingSubMessages.First().PRC价格));

                    if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_FUELLING)
                    {
                        this.lastLogicalDeviceState = LogicalDeviceState.FDC_FUELLING;
                        var safe = this.OnCurrentFuellingStatusChange;
                        safe?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
                        {
                            Nozzle = this.nozzles.First(),
                            Amount = PumpStateChangeCommand.StateNozzleOperatingSubMessages.First().AMN数额,
                            Volumn = PumpStateChangeCommand.StateNozzleOperatingSubMessages.First().VOL升数,
                            Price = PumpStateChangeCommand.StateNozzleOperatingSubMessages.First().PRC价格,
                            Finished = false,
                        }));
                    }
                }
            }
            else if (context.Incoming.Message is PumpNotifyTransactionDoneCommand PumpNotifyTransactionDoneCommand)
            {
                if (!this.nozzles.Any())
                    this.nozzles.Add(new LogicalNozzle(this.pumpId, 0,
                        PumpNotifyTransactionDoneCommand.NZN_枪号,
                        PumpNotifyTransactionDoneCommand.PRC_成交价格));
                var safe1 = this.OnCurrentFuellingStatusChange;
                safe1?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
                {
                    // 恒山油机 一个加油点只有一把枪
                    Nozzle = this.nozzles.First(),
                    Amount = PumpNotifyTransactionDoneCommand.AMN数额,
                    Volumn = PumpNotifyTransactionDoneCommand.VOL_升数,
                    Price = PumpNotifyTransactionDoneCommand.PRC_成交价格,
                    SequenceNumberGeneratedOnPhysicalPump = PumpNotifyTransactionDoneCommand.POS_TTC,
                    //AmountTotalizer = ,
                    VolumeTotalizer = PumpNotifyTransactionDoneCommand.V_TOT_升累计,
                    Finished = true,
                }));
            }
            else
            {
                if (logger.IsDebugEnabled)
                    logger.Debug("Incoming an unknown message, will do nothing and ignore.");
            }
        }

        public string Name => this.GetType().FullName;

        public Guid Id => this.uniqueId;
        /// <summary>
        /// Gets the Identification of the pump for the system. Is the logical number of the pump
        /// </summary>
        public int PumpId => this.pumpId;

        /// <summary>
        /// this pump have no way to share same comport since this HengShan protocol content does not contains
        /// any id info, so always static 0 here.
        /// 地址面地址
        /// </summary>
        public int PumpPhysicalId => 0;

        public int AmountDecimalDigits => 2;
        public int VolumeDecimalDigits => 2;
        public int PriceDecimalDigits => 2;

        public int VolumeTotalizerDecimalDigits => 2;

        public virtual async Task<LogicalDeviceState> QueryStatusAsync()
        {
            // if last state is expired, we return a OFFLINE here to FdcClient.
            if (DateTime.Now.Subtract(this.lastLogicalDeviceStateReceivedTime).TotalSeconds > lastLogicalDeviceStateExpiredTime)
            {
                if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_OFFLINE)
                {
                    this.lastLogicalDeviceState = LogicalDeviceState.FDC_OFFLINE;
                    logger.Info("Pump: " + this.pumpId + ", " + "      State switched to FDC_OFFLINE due to cached state expired");
                    var safe0 = this.OnStateChange;
                    safe0?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_OFFLINE, null));
                }

                return LogicalDeviceState.FDC_OFFLINE;
            }

            return this.lastLogicalDeviceState;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns>MoneyTotalizer:VolumnTotalizer</returns>
        public async Task<Tuple<int, int>> QueryTotalizerAsync(byte logicalNozzleId)
        {
            throw new NotImplementedException();
        }


        public virtual async Task<bool> ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param>
        /// <returns></returns>
        public virtual async Task<bool> AuthorizeAsync(byte logicalNozzleId)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="moneyAmount"></param>
        /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param>
        /// <returns></returns>
        public virtual async Task<bool> AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="volumn"></param>
        /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param>
        /// <returns></returns>
        public virtual async Task<bool> AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId)
        {
            throw new NotImplementedException();
        }

        public virtual async Task<bool> FuelingRoundUpByAmountAsync(int amount)
        {
            throw new NotImplementedException();
        }

        #region not implemented

        public async Task<bool> UnAuthorizeAsync(byte logicalNozzleId)
        {
            throw new NotImplementedException();
        }

        public async Task<bool> SuspendFuellingAsync()
        {
            throw new NotImplementedException();
        }

        public async Task<bool> ResumeFuellingAsync()
        {
            throw new NotImplementedException();
        }

        public async Task<bool> FuelingRoundUpByVolumeAsync(int volume)
        { throw new NotImplementedException(); }

        #endregion

        /// <summary>
        /// </summary>
        protected Dictionary<byte, FuelSaleTransaction> logicalNozzleIdToLastFuelSaleTrxMapping = new Dictionary<byte, FuelSaleTransaction>();
        public void OnFdcServerInit(Dictionary<string, object> parameters)
        {
            if (parameters.ContainsKey("LastPriceChange"))
            {
            }

            /* Load Last sale(from db) for void the case of FC accidently disconnect from Pump in fueling,
             and may cause a fueling trx gone from FC control */
            if (parameters.ContainsKey("LastFuelSaleTrx"))
            {
                // nozzle logical id:lastSale
                //var lastFuelSaleTrxes = parameters["LastFuelSaleTrx"] as Dictionary<byte, FuelSaleTransaction>;
                //foreach (var lastFuelSaleTrx in lastFuelSaleTrxes)
                //{
                //    logger.Info("Pump: " + this.pumpId + ", OnFdcServerInit, load last fuel sale " +
                //        "on logical nozzle: " + lastFuelSaleTrx.Key + " with value: " + lastFuelSaleTrx.Value);
                //    this.logicalNozzleIdToLastFuelSaleTrxMapping.Remove(lastFuelSaleTrx.Key);
                //    this.logicalNozzleIdToLastFuelSaleTrxMapping.Add(lastFuelSaleTrx.Key, lastFuelSaleTrx.Value);
                //}
            }
        }

        public async Task<bool> LockNozzleAsync(byte logicalNozzleId)
        {
            return false;
        }

        public async Task<bool> UnlockNozzleAsync(byte logicalNozzleId)
        {
            return false;
        }
    }
}