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 System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Edge.Core.Database.Models;
using LanTian_Sinopec_PumpIcCardReader.MessageEntity.Outgoing;
using Edge.Core.UniversalApi;

namespace LanTian_Sinopec_PumpIcCardReader
{
    /// <summary>
    /// PC主动
    /// </summary>
    public class Handler : IFdcPumpController, IDeviceHandler<byte[], KaJiLianDongV11MessageTemplateBase>
    {
        private IServiceProvider services;
        static ILogger logger = NullLogger.Instance;

        private LogicalDeviceState lastLogicalDeviceState = LogicalDeviceState.FDC_OFFLINE;
        private DateTime lastLogicalDeviceStateReceivedTime;
        // by seconds, change this value need change the correlated deviceOfflineCountdownTimer's interval as well
        public const int lastLogicalDeviceStateExpiredTime = 6;
        private List<LogicalNozzle> nozzles = new List<LogicalNozzle>();

        private System.Timers.Timer deviceOfflineCountdownTimer;

        private bool isOnFdcServerInitCalled = false;

        private int pumpId;
        protected IContext<byte[], KaJiLianDongV11MessageTemplateBase> context;

        private byte rotateMsgSeqNo_Seed = 0;

        /// <summary>
        /// 0<= value <=63
        /// </summary>
        private byte NextMsgSeqNo
        {
            get
            {
                var cur = this.rotateMsgSeqNo_Seed;
                if (cur > 63) cur = this.rotateMsgSeqNo_Seed = 0;
                this.rotateMsgSeqNo_Seed++;
                return cur;
            }
        }

        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;
        public IEnumerable<LogicalNozzle> Nozzles => this.nozzles;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pumpId">the ic card also be treated as a logical pump</param>
        /// <param name="terminalPhysicalAddress">加液机的通讯终端的逻辑编号POS-P, 通讯终端出厂时POS-P 设为0xFF</param>
        /// <param name="fcPhysicalAddress">PC 机的地址范围:0xE0~0xF9</param>
        /// <param name="services"></param>
        public Handler(int pumpId,
            int terminalPhysicalAddress, int fcPhysicalAddress, IServiceProvider services)
        {
            this.services = services;
            var loggerFactory = services.GetRequiredService<ILoggerFactory>();
            logger = loggerFactory.CreateLogger("PumpHandler");
            this.pumpId = pumpId;
            this.terminalPhysicalAddress = (byte)terminalPhysicalAddress;
            this.fcPhysicalAddress = (byte)fcPhysicalAddress;
            //对于这个品牌,一个IC卡键盘仅可控制一把枪
            this.nozzles.Add(new LogicalNozzle(pumpId, (byte)terminalPhysicalAddress, 1, null));
        }

        public void Init(IContext<byte[], KaJiLianDongV11MessageTemplateBase> context)
        {
            this.context = context;
            var timeWindowWithActivePollingOutgoing =
                this.context.Outgoing as TimeWindowWithActivePollingOutgoing<byte[], KaJiLianDongV11MessageTemplateBase>;
            timeWindowWithActivePollingOutgoing.PollingMsgProducer = () =>
            {
                return new PcGenericInquiryRequest()
                {
                    SourceAddress = this.fcPhysicalAddress,
                    TargetAddress = this.terminalPhysicalAddress,
                    MessageSequenceNumber = this.NextMsgSeqNo,

                    PC_TIME = DateTime.Now,
                };
            };
        }

        public virtual async Task Process(IContext<byte[], KaJiLianDongV11MessageTemplateBase> context)
        {
            this.context = context;
            this.lastLogicalDeviceStateReceivedTime = DateTime.Now;
            if (this.lastLogicalDeviceState == LogicalDeviceState.FDC_OFFLINE)
            {
                logger.LogInformation("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;
                this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_READY));
            }

            if (context.Incoming.Message is PumpRealTimeStateEvent pumpRealTimeStateResponse)
            {
                if (pumpRealTimeStateResponse.NozzleOperatingStates == null)
                {
                    if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_READY)
                    {
                        this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
                        this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(
                            LogicalDeviceState.FDC_READY,
                            this.nozzles.FirstOrDefault()));
                    }
                }

                if (pumpRealTimeStateResponse.NozzleOperatingStates != null
                    && pumpRealTimeStateResponse.NozzleOperatingStates.Any(n =>
                        n.St状态字 == NozzleOperatingState.PumpStateChangeCode.抬枪或加油中))
                {
                    this.nozzles.First().RealPriceOnPhysicalPump =
                        pumpRealTimeStateResponse.NozzleOperatingStates.First().PRC价格;
                    if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_FUELLING)
                    {
                        this.lastLogicalDeviceState = LogicalDeviceState.FDC_FUELLING;
                        this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
                        {
                            Nozzle = this.nozzles.First(),
                            Amount = pumpRealTimeStateResponse.NozzleOperatingStates.First().AMN数额,
                            Volumn = pumpRealTimeStateResponse.NozzleOperatingStates.First().VOL升数,
                            Price = pumpRealTimeStateResponse.NozzleOperatingStates.First().PRC价格,
                            Finished = false,
                        }));
                    }
                }
            }
            else if (context.Incoming.Message is PumpNotifyTransactionDoneRequest pumpNotifyTransactionDoneRequest)
            {
                this.nozzles.First().RealPriceOnPhysicalPump =
                        pumpNotifyTransactionDoneRequest.PRC_成交价格;
                this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
                {
                    Nozzle = this.nozzles.First(),
                    Amount = pumpNotifyTransactionDoneRequest.AMN数额,
                    Volumn = pumpNotifyTransactionDoneRequest.VOL_升数,
                    Price = pumpNotifyTransactionDoneRequest.PRC_成交价格,
                    SequenceNumberGeneratedOnPhysicalPump = pumpNotifyTransactionDoneRequest.POS_TTC,
                    //AmountTotalizer = ,
                    VolumeTotalizer = pumpNotifyTransactionDoneRequest.V_TOT_升累计,
                    Finished = true,
                }));
#warning should send a request to external module to validate the T-MAC
                this.context.Outgoing.Write(new PumpNotifyTransactionDoneResponse()
                {
                    SourceAddress = this.fcPhysicalAddress,
                    TargetAddress = this.terminalPhysicalAddress,
                    MessageSequenceNumber = this.NextMsgSeqNo,

                    Result = PumpNotifyTransactionDoneResponse.ResultEnum.正确
                });
            }
            else if (context.Incoming.Message is PumpAskDataDownloadRequest pumpAskDataDownloadRequest)
            {
#warning should query cloud side to get the BL_LEN_长度
                this.context.Outgoing.Write(new PumpAskDataDownloadResponse()
                {
                    SourceAddress = this.fcPhysicalAddress,
                    TargetAddress = this.terminalPhysicalAddress,
                    MessageSequenceNumber = this.NextMsgSeqNo,

                    BL_LEN_长度 = 0,
                    Content_内容 = pumpAskDataDownloadRequest.Content_内容,
                });
            }
            else if (context.Incoming.Message is PumpStartDataDownloadRequest pumpStartDataDownloadRequest)
            {
#warning should query cloud side to get the real data for downloading to pump
                this.context.Outgoing.Write(new PumpStartDataDownloadResponse()
                {
                    SourceAddress = this.fcPhysicalAddress,
                    TargetAddress = this.terminalPhysicalAddress,
                    MessageSequenceNumber = this.NextMsgSeqNo,

                    Content_内容 = pumpStartDataDownloadRequest.Content_内容,
                    S_OFFSET_段偏移 = pumpStartDataDownloadRequest.S_OFFSET_段偏移,
                    SEG_段数_segs = 0,
                    DATA_Content_数据内容 = new List<byte>() { 0x00 }
                });
            }
            else if (context.Incoming.Message is PumpInquiryGrayCardRecordRequest pumpInquiryGrayCardRecordRequest)
            {
#warning should query cloud side to get the real data
                this.context.Outgoing.Write(new PumpInquiryGrayCardRecordResponse()
                {
                    SourceAddress = this.fcPhysicalAddress,
                    TargetAddress = this.terminalPhysicalAddress,
                    MessageSequenceNumber = this.NextMsgSeqNo,

                    M_FLAG_匹配标志 = PumpInquiryGrayCardRecordResponse.PumpInquiryGrayCardRecordResult.匹配,
                    ASN卡应用号 = pumpInquiryGrayCardRecordRequest.ASN卡应用号,
                    BAL_余额 = 999911,
                    AMN_交易额 = 183,
                    AMN_优惠价 = 123,
                    AMN_折扣 = 45,
                    AMN_优惠后金额 = 97,
                    CTC_交易计数器 = 765,
                    DS_扣款来源 = 0,
                    TIME_日期及时间 = DateTime.Now,
                    GMAC电子签名 = 23192394,
                    PSAM_TID_PSAM编号 = new List<byte>() { 0x01, 0x02 },
                    PSAM_TTC = 654
                });
            }
            else if (context.Incoming.Message is PumpInquiryCreditCardRecordRequest pumpInquiryCreditCardRecordRequest)
            {
#warning should query cloud side to get the real data
                this.context.Outgoing.Write(new PumpInquiryCreditCardRecordResponse()
                {
                    SourceAddress = this.fcPhysicalAddress,
                    TargetAddress = this.terminalPhysicalAddress,
                    MessageSequenceNumber = this.NextMsgSeqNo,

                    S_DATA_记帐卡状态 = PumpInquiryCreditCardRecordResponse.CardStateEnum.可以加油_无里程限制,
                });
            }
            else if (context.Incoming.Message is PumpInquiryBlackAndWhiteListRequest pumpInquiryBlackAndWhiteListRequest)
            {
#warning should query cloud side to get the real data
                this.context.Outgoing.Write(new PumpInquiryBlackAndWhiteListResponse()
                {
                    SourceAddress = this.fcPhysicalAddress,
                    TargetAddress = this.terminalPhysicalAddress,
                    MessageSequenceNumber = this.NextMsgSeqNo,

                    M_FLAG_匹配标志 = PumpInquiryBlackAndWhiteListResponse.ResultEnum.不匹配,
                    ASN卡应用号 = pumpInquiryBlackAndWhiteListRequest.ASN卡应用号,
                });
            }
            else if (context.Incoming.Message is PumpInternalErrorRequest pumpInternalErrorRequest)
            {
#warning may need notify someone that error happened??
                this.context.Outgoing.Write(new PumpInternalErrorResponse()
                {
                    SourceAddress = this.fcPhysicalAddress,
                    TargetAddress = this.terminalPhysicalAddress,
                    MessageSequenceNumber = this.NextMsgSeqNo,

                    COMMAND_通讯的命令字 = 0,
                    FLAG_成功标志 = 0,
                });
            }
            else
            {
                if (logger.IsEnabled(LogLevel.Debug))
                    logger.LogDebug("Incoming an unknown message, will do nothing and ignore.");
            }
        }

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

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

        /// <summary>
        /// PC 机的地址范围:0xE0~0xF9
        /// </summary>
        private byte fcPhysicalAddress;

        /// <summary>
        /// 加液机的通讯终端的逻辑编号POS-P, 通讯终端出厂时POS-P 设为0xFF
        /// </summary>
        private byte terminalPhysicalAddress;
        public int PumpPhysicalId => this.terminalPhysicalAddress;

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

        public int VolumeTotalizerDecimalDigits => 2;

        [UniversalApi]
        public virtual async Task<LogicalDeviceState> QueryStatusAsync()
        {
            return this.lastLogicalDeviceState;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns>MoneyTotalizer:VolumnTotalizer</returns>
        [UniversalApi]
        public async Task<Tuple<int, int>> QueryTotalizerAsync(byte logicalNozzleId)
        {
            var response = await this.context.Outgoing.WriteAsync(new PcReadPumpAccumulatorRequest()
            {
                SourceAddress = this.fcPhysicalAddress,
                TargetAddress = this.terminalPhysicalAddress,
                MessageSequenceNumber = this.NextMsgSeqNo,
            }, (_, testResponse) => testResponse is PcReadPumpAccumulatorResponse, 4000) as PcReadPumpAccumulatorResponse;
            if (response == null) return new Tuple<int, int>(-1, -1);
            var volTotalizerValue = response?.MZN_单个油枪.FirstOrDefault()?.V_TOT_升累计;
            return new Tuple<int, int>(-1, volTotalizerValue ?? -1);
        }

        [UniversalApi]
        public async Task<PcReadPumpInfoResponse> PC机读取加液机信息Async()
        {
            var response = await this.context.Outgoing.WriteAsync(new PcReadPumpInfoRequest()
            {
                SourceAddress = this.fcPhysicalAddress,
                TargetAddress = this.terminalPhysicalAddress,
                MessageSequenceNumber = this.NextMsgSeqNo,
            }, (_, testResponse) => testResponse is PcReadPumpInfoResponse, 4000) as PcReadPumpInfoResponse;
            return response;
        }

        [UniversalApi]
        public async Task<KaJiLianDongV11MessageTemplateBase> PC机主动读取加液记录Async(int pos_TTC_终端交易号)
        {
            var response = await this.context.Outgoing.WriteAsync(new PcReadTransactionRequest(pos_TTC_终端交易号)
            {
                SourceAddress = this.fcPhysicalAddress,
                TargetAddress = this.terminalPhysicalAddress,
                MessageSequenceNumber = this.NextMsgSeqNo,
            }, (_, testResponse) => testResponse is PumpNotifyTransactionDoneRequest || testResponse is PcReadTransactionButDoesNotFindResponse, 4000);
            return response;
        }


        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;
        }
    }
}