using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sharp7;
using HTCoreServiceApp.DriverInterface;
using HTCoreServiceApp.Models;
using System.Threading;
using HTCoreServiceApp.Common;
using System.Text.RegularExpressions;
using HTCoreServiceApp.DataHandle;
using System.Timers;
using Newtonsoft.Json.Linq;
using Pipelines.Sockets.Unofficial.Arenas;
using libplctag;
using libplctag.DataTypes;
using HTCoreServiceApp.Communicate.AllenBradleyEIP;
using SqlSugar.DistributedSystem.Snowflake;

namespace HTCoreServiceApp.Communicate.AllenBradleyEIPTag
{
    public class ABTagClientExample : IDriverEntity
    {

        public string DeviceId { get; set; }

        private string DriverName { get; set; }
        private List<MetaSystem> Systems { get; set; }
        public bool IsConnected { get; set; }

        public int Timeout { get; set; }

        public bool isScan { get; set; }

        public System.Timers.Timer ScanTimer { get ; set; }

		System.Timers.Timer timerCheck = new System.Timers.Timer();

        //public List<System.Threading.Timer> Timers { get; set; }
        public List<ScanTimer> ScanTimers { get; set; }

        public string Address = "127.0.0.1";
        private int Rack = 0;
        //private int Slot = 0;
        private string Slot = "0";

        private string PLCType = "";

		private int ARRAY_LENGTH = 1;

		private string path = "1,0";

		public AllenBradleyClient Client;
        private byte[] Buffer = new byte[65536];
        private byte[] DB_A = new byte[1024];
        private byte[] DB_B = new byte[1024];
        private byte[] DB_C = new byte[1024];
        private readonly object _lock = new();


        private List<MetaGroup> MetaGroups;
        private List<MetaTag> MetaTags;
        //List<Argument> argument, List<MetaGroup> metaGroups,List<MetaTag> metaTags
        public ABTagClientExample(TagGroup tagGroup) 
        {

            ScanTimers = new List<ScanTimer>();
            Address =tagGroup.Arguments.Where(x => x.PropertyName == "ServerName").First().PropertyValue;
            //Rack = int.Parse(tagGroup.Arguments.Where(x => x.PropertyName == "Rack").First().PropertyValue);
            //Slot = int.Parse(tagGroup.Arguments.Where(x => x.PropertyName == "Slot").First().PropertyValue);
            Slot = tagGroup.Arguments.Where(x => x.PropertyName == "Slot").First().PropertyValue;
            PLCType = tagGroup.Arguments.Where(x => x.PropertyName == "PLCType").First().PropertyValue;
            Timeout = int.Parse(tagGroup.Arguments.Where(x => x.PropertyName == "TimeOut").First().PropertyValue);
            MetaGroups =tagGroup.Groups;
            MetaTags = tagGroup.Tags;//只有一组
            DriverName = tagGroup.DriverName;
            Systems = tagGroup.Systems;

            Client = new AllenBradleyClient(Address, 44818, byte.Parse(Slot), Timeout);


            //Client.SetConnectionType(0x3);
            //Client.ConnTimeout= Timeout;
            //Client.RecvTimeout = 30;



			//Connect();
			timerCheck.Interval = 2000;
			StartConnectCheck();

            StartScan();
        }

        public void StartScan()
        {
            var groups = MetaGroups.GroupBy(x => new { x.GroupId });

            var scanTagsParas = new List<ScanTagsPara>();

            foreach (var metaGroup in groups)
            {
                ScanTagsPara scanTagsPara = new ScanTagsPara();
                scanTagsPara.GroupName = MetaGroups.Where(x=>x.GroupId == metaGroup.Key.GroupId).ToList().First().GroupName;
                scanTagsPara.UpdateRate = MetaGroups.Where(x => x.GroupId == metaGroup.Key.GroupId).ToList().First().UpdateRate;
                scanTagsPara.IsConsecutive = (bool)MetaGroups.Where(x => x.GroupId == metaGroup.Key.GroupId).ToList().First().IsActive;
                scanTagsPara.IsActive = MetaGroups.Where(x => x.GroupId == metaGroup.Key.GroupId).ToList().First().IsActive;
                short driverID = (short)MetaGroups.Where(x => x.GroupId == metaGroup.Key.GroupId).ToList().First().DriverId;
                scanTagsPara.DriverName = DriverName;
                var groupSysId = MetaGroups.Where(x => x.GroupId == metaGroup.Key.GroupId).ToList().First().SystemId;
                scanTagsPara.SystemName = Systems.Where(x => x.SystemId == groupSysId).ToList().First().SystemName;

                scanTagsPara.ScanTags = MetaTags.Where(x => x.GroupId == metaGroup.Key.GroupId).ToList();
                if (scanTagsPara.ScanTags.Count < 1)
                {
                    continue;
                }
                //var ScanTags = MetaTags.Where(x => x.GroupId == metaGroup.Key.GroupId).ToList();
                //scanTagsPara.ScanTags = new List<MetaTag>();
                //foreach (var tagitem in ScanTags)
                //{
                //    var srcAddress = tagitem.Address;
                //    var ArrayName = Regex.Replace(srcAddress, "[0-9,.]", "", RegexOptions.IgnoreCase);
                //    var TagRegIndex = Regex.Replace(srcAddress, "[a-z]", "", RegexOptions.IgnoreCase);
                //    var metatagAB = PCAutoCopy(tagitem);
                //    scanTagsPara.ScanTags.Add(metatagAB);
                //}

                scanTagsParas.Add(scanTagsPara);
            }
            //if 添加连续或单变量读取标记
            //if (scanTagsPara.IsConsecutive == true)
            //{
            //    if (scanTagsPara.IsActive == true && scanTagsPara.ScanTags.Count > 0)
            //        Task.Run(() =>
            //        {
            //            ScanConsecutive(scanTagsPara);
            //        }) ;
            //}
            //else
            //{
            //    if (scanTagsPara.IsActive == true && scanTagsPara.ScanTags.Count > 0)
            //        Task.Run(() =>
            //        {
            //            Scan(scanTagsPara);
            //        });

            //}
            ScanConsecutive(scanTagsParas);
        }

        /// <summary>
        /// 启动连续读取
        /// </summary>
        /// <param name="scanTagsParas"></param>
        public void ScanConsecutive(List<ScanTagsPara> scanTagsParas)
        {
            foreach (var scanTagsPara in scanTagsParas)
            {
                ScanTimer timerScan = new ScanTimer();
                var Interval = scanTagsPara.UpdateRate;
                if (Interval <= 0)
                {
                    Interval = 1000;
                }
                timerScan.ScanTagsPara = scanTagsPara;
                //timerScan.AddressArg = PLCHelper.GetAddressArg(scanTagsParas.ScanTags);
                if (scanTagsPara.IsConsecutive == true)
                    timerScan.Timer = new System.Threading.Timer(TimerScanConsecutive_Elapsed, timerScan.ScanTagsPara, 0, (int)Interval);
                //else
                //timerScan.Timer = new System.Threading.Timer(TimerScanDisperse_Elapsed, timerScan.ScanTagsPara, 0, (int)Interval);
                //timerScan.Enabled = true;
                ScanTimers.Add(timerScan);
            }
        }
        /// <summary>
        /// 连续读取timer事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TimerScanConsecutive_Elapsed(System.Object stateInfo)
        {
            long hdatetime = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
            ScanTagsPara scanTagsPara = (ScanTagsPara)stateInfo;
            //timerScan.Enabled = false;
            //List<ScanTagsPara> scanTagsParas = timerScan.ScanTagsParas;
            //AddressArg addressArg = timerScan.AddressArg;
            if (scanTagsPara.IsActive == false)
                    return;
            if (Client.Connected)
            {

                //var addressArg = scanTagsPara.AddressArg;

                //object _lock = new();

                Task task = Task.Factory.StartNew(() =>
                {
                    lock (_lock)
                    {
                        try
                        {
                            var tcollstart = DateTime.Now;

							var dintTag = new Tag<DintPlcMapper, int[]>()
							{
								Name = scanTagsPara.GroupName,
								Gateway = Address,
								Path = path,
								PlcType = PlcType.ControlLogix,
								Protocol = Protocol.ab_eip,
								ArrayDimensions = new int[] { ARRAY_LENGTH },
								Timeout = TimeSpan.FromMilliseconds(Timeout),
							};

                            var result = dintTag.Read();

                                

							var tcollend = DateTime.Now;
                            var timecolldiff =  tcollend - tcollstart;
                            Console.WriteLine($"AB变量组{scanTagsPara.GroupName}读取耗时：{timecolldiff},设备:{DriverName}变量组:{scanTagsPara.GroupName}");
                                

                            Console.WriteLine(string.Join(",", result) + "AB：" + " 字节数：" + result.Length*4);
                            var t1 = DateTime.Now;
                            //Console.WriteLine(string.Join(",", buffer) + "读取完成：DB" + DBNumber.ToString() + ",字节数：" + amount.ToString());


                            DataExtractTAB.DataExtractCAsync(scanTagsPara.ScanTags, scanTagsPara.SystemName,scanTagsPara.GroupName, scanTagsPara.DriverName, result);


                            var t2 = DateTime.Now;
                            var timediff = t2- t1;
                            Console.WriteLine($"AB缓存和储存耗时：{timediff}");
                        }
                        catch (Exception ex)
                        {
                            LogHelper.LogWrite($"{DriverName}读取失败，AB错误信息{ex}");
                        }
                    }

                });
            }
            else
            {
                Client.Open();
				LogHelper.LogWrite($"{DriverName}读取失败，AB EIP未连接");
				//Connect();
			}
            Thread.Sleep(50);
            
        }
        /*
        /// <summary>
        /// 启动单个变量读取
        /// </summary>
        /// <param name="scanTagsPara"></param>
        public void Scan(ScanTagsPara scanTagsPara)
        {
            ScanTimer timerScan = new ScanTimer();
            if (scanTagsPara.UpdateRate > 0)
            {
                timerScan.Interval = (double)scanTagsPara.UpdateRate;
            }
            else
            {
                timerScan.Interval = 1000;
            }
            timerScan.ScanTagsPara = scanTagsPara;
            timerScan.Elapsed += new System.Timers.ElapsedEventHandler(TimerScan_Elapsed);
            timerScan.Enabled = true;
        }

        /// <summary>
        /// 单个变量读取timer事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TimerScan_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
            ScanTimer timerScan = (ScanTimer)sender;
            ScanTagsPara scanTagsPara = timerScan.ScanTagsPara;
            AddressArg addressArg = timerScan.AddressArg;
            if (IsConnected = true)
            {
                object _lock = new();

                Task task = Task.Run(() =>
                {
                    lock (_lock)
                    {
                        try
                        {
                            //CodingTools.StartTime = DateTime.Now.Millisecond;//de
                            //scanTagsPara.ScanTags.Min(x => x.Address);
                            //scanTagsPara.ScanTags.Min(x => x.Address);提高速度应放在timer事件前，测试后更改
                            int startAdress = addressArg.Start;
                            int Area = 132;
                            int DBNumber = 0;
                            if (scanTagsPara.GroupName.Contains("DB"))
                            {
                                DBNumber = int.Parse(Regex.Replace(scanTagsPara.GroupName, "[a-z]", "", RegexOptions.IgnoreCase));
                            }
                            else
                            {
                                DBNumber = int.Parse(scanTagsPara.GroupName);
                            }
                            int amount = addressArg.Length;
                            int WordLen = 2;
                            byte[] buffer = new byte[amount];
                            int Result = ReadArea(Area, DBNumber, startAdress, amount, WordLen, ref buffer);
                            //CodingTools.EndTime = DateTime.Now.Millisecond;//de
                            //var timess = (CodingTools.EndTime - CodingTools.StartTime).ToString();
                            //Console.WriteLine($"读取耗时：{timess}");
                            if (Result == 0)
                            {
                                //Console.WriteLine(string.Join(",", buffer) + "读取完成：DB" + DBNumber.ToString() + ",字节数：" + amount.ToString());
                                int saveResult = DataExtract.DataExtractConsecutive(timerScan.ScanTagsPara.ScanTags, scanTagsPara.GroupName, timerScan.ScanTagsPara.DriverName, buffer, addressArg);
                                {
                                    if (saveResult == 0)
                                    {
                                        //logger
                                    }
                                    else
                                    {
                                        //logger
                                    }

                                }
                            }
                            else if (Result != 0)
                            {
                                Console.WriteLine("读取失败：DB" + DBNumber.ToString() + ",ERRORCODE：" + Result.ToString());
                                Close();
                                Thread.Sleep(200);
                                Connect();
                            }

                        }
                        catch (Exception ex)
                        {

                        }
                    }

                });
            }
            else
            {
                Connect();
            }
        }
        */
        public Result ReadArea(string arrayName)
        {
            object _lock = new();
            lock (_lock)
            {
                //buffer = new byte[65536];
                Result result = Client.ReadFloat(arrayName);
                return result;
            }
        }

		public void StartConnectCheck()
		{
			
			timerCheck.Elapsed += new System.Timers.ElapsedEventHandler(TimerCheckConnect_Elapsed);
			timerCheck.Enabled = true;
		}

        private void TimerCheckConnect_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
			timerCheck.Enabled = false;
			if (Client.Connected)
            {
				timerCheck.Enabled = true;
				return;
            }
            else
            {
                Connect();
			}
        }


		public bool Connect()
        {
            //int Result;
            var Result = Client.Open();
            if (Result.IsSucceed == true && Client.Connected)
            {
                IsConnected = true;
				//log
				LogHelper.LogWrite($"EIP连接成功，返回值:{Result.ErrCode}");
				timerCheck.Enabled = true;
				return true;
            }
            else
            {
                IsConnected = false;
                //log
                LogHelper.LogWrite($"EIP连接失败，返回值:{Result.ErrCode}");
				timerCheck.Enabled = true;
				return false;
            }
        }

        public bool Close()
        {
            var Result = Client.Close();
            if (Result.IsSucceed == true&&Client.Connected==false)
            {
                IsConnected = false;
				//log
				LogHelper.LogWrite($"EIP关闭连接成功，返回值:{Result.ErrCode}");
				return true;
            }
            else
            {
                IsConnected = false;
				LogHelper.LogWrite($"EIP关闭连接失败，返回值:{Result.ErrCode}");
				//log
				return false;
            }
        }

        public void Dispose()
        {
            try
            {
                var Result = Client.Close();
                //log
            }
            catch (Exception ex)
            {
                //log
            }
        }
        /// <summary>
        /// 父类的value赋值给子类
        /// </summary>
        /// <param name="parent"></param>
        /// <returns></returns>
        private static MetaTag PCAutoCopy(MetaTag parent)
        {
            MetaTag child = new MetaTag();


            var ParentType = typeof(MetaTag);


            var Properties = ParentType.GetProperties();


            foreach (var Propertie in Properties)
            {
                if (Propertie.CanRead && Propertie.CanWrite)
                {
                    Propertie.SetValue(child, Propertie.GetValue(parent, null), null);
                }
            }


            return child;
        }
        /// <summary>
        /// 写入方法
        /// </summary>
        /// <param name="writePara"></param>
        /// <returns></returns>
		public int Write(WritePara writePara)
		{
            Result reCode = new Result() { ErrCode = 0} ;
            //JObject valueJson = JObject.Parse(writePara);
            var srcValue = writePara.Value; //valueJson[propertyName: "Value"].ToString();
            var writeAddress = writePara.WriteAddress;//valueJson[propertyName: "WriteAddress"].ToString();
            var dataType = writePara.DataType;//short.Parse(valueJson[propertyName: "DataType"].ToString());
            var groupName = writePara.GroupName; //valueJson[propertyName: "GroupName"].ToString();
            var tagName = writePara.TagName; //valueJson[propertyName: "TagName"].ToString();

			decimal value = decimal.Parse(srcValue);
            lock (_lock)
            {
			    if (dataType == (short)DataType.BOOL)
			    {
                    bool wrvalue = srcValue == "1" ? true : false;
				    reCode = Client.Write(tagName, wrvalue);
			    }
			    else if (dataType == (short)DataType.FLOAT)
			    {
				    reCode = Client.Write(tagName, (float)value);
			    }
                else if (dataType == (short)DataType.INT)
                {
                    reCode = Client.Write(tagName,int.Parse(value.ToString()));
                }
                //else if (dataType == (short)DataType.WORD)
                //{
                // revbuffer_w = new byte[2];
                // Sharp7.S7.SetWordAt(revbuffer_w, 0, (ushort)value);
                // reCode = Client.WriteArea(132, DBNum, address_int, 2, 2, revbuffer_w);
                //}
                //else if (dataType == (short)DataType.BYTE)
                //{
                // revbuffer_w = new byte[1];
                // Sharp7.S7.SetByteAt(revbuffer_w, 0, (byte)value);
                // reCode = Client.WriteArea(132, DBNum, address_int, 1, 2, revbuffer_w);
                //}
                else
                {

                }
			}
			Console.WriteLine(writePara);
            return reCode.ErrCode;
		}

        public int WriteBlock(WriteBlockPara writePara)
        {
            throw new NotImplementedException();
        }
    }
}
