using DbCommon.BusinessCore.BaseCore;
using DbCommon.Enties.DbModels;
using HslCommunication.Core;
using ProjectManagementSystem.Common.Config;
using ProjectManagementSystem.Common.Core;
using ProjectManagementSystem.Common.Extenions;
using ProjectManagementSystem.Common.Function;
using ProjectManagementSystem.Common.Logger;
using ProjectManagementSystem.Device.CommandCallback;
using ProjectManagementSystem.Device.Extenions;
using ProjectManagementSystem.DispatchPack.Entity;
using ProjectManagementSystem.UI;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ProjectManagementSystem.DispatchPack
{
    public class PackEngine
    {
        private static PackEngine m_instance;

        private bool m_userThreadRunning;
        private bool m_dislayThreadRunning;
        //private byte m_plcHeartbeat;

        private ConcurrentDictionary<int, AgvInfo> m_agvInfoDict = new ConcurrentDictionary<int, AgvInfo>();
        private ConcurrentDictionary<int, StationInfo> m_stationInfoDict = new ConcurrentDictionary<int, StationInfo>();

        private StationInfoManager m_stationInfoManager;
        private AgvInfoManager m_agvInfoManager;

        public event Action<string> OnDisplayInfo;
        public event Action<EntityBase, string> OnDisplayEntityInfo;
        public event Action OnStart;

        public int AgvMaxNum { get { return 8; } }

        public ConcurrentDictionary<int, StationInfo> StationInfoDict { get { return m_stationInfoDict; } }
        public ConcurrentDictionary<string, IReadWriteNet> PlcDict { get; private set; } = new ConcurrentDictionary<string, IReadWriteNet>();
        public List<StationUI> StationUIList { get; private set; } = new List<StationUI>();
        public List<EntityBase> EntityBaseList { get; private set; } = new List<EntityBase>();


        public static PackEngine Instance
        {
            get
            {
                if (m_instance == null)
                {
                    m_instance = new PackEngine();
                }

                return m_instance;
            }
        }
        private PackEngine()
        {
        }

        private void Init()
        {
            EntityBaseList.Clear();
            StationUIList.Clear();
            m_stationInfoDict.Clear();
            m_agvInfoDict.Clear();

            m_stationInfoManager = new StationInfoManager();
            m_stationInfoManager.Init();

            m_agvInfoManager = new AgvInfoManager();
            m_agvInfoManager.Init();

            var lineInfoList = ExcelConfig.Instance.LineInfoList;

            for (int i = 0; i < lineInfoList.Count; i++)
            {
                var lineInfo = lineInfoList[i];

                var name = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
                string fullClassName = string.Format("ProjectManagementSystem.DispatchPack.Entity.Entity_{0}", lineInfo.Name);
                var entity = InstanceConstructor.GetInstance<EntityBase>(name, fullClassName);

                if(entity == null)//没有添加线体类时，使用基类处理逻辑
                {
                    entity = new EntityBase();
                }

                if (entity != null)
                {
                    entity.Index = i;
                    entity.LineInfo = lineInfo;
                    entity.PackEngine = this;

                    entity.Init();

                    EntityBaseList.Add(entity);
                }
            }

            InitStationInfo();
            InitAgvInfo();
        }

        private void UpdateAgvInfo()
        {
            var agvInfoList = DeviceControl.Instance.Communication.InfoAgvDictionary.Values.ToList();
            for (int i = 0; i < agvInfoList.Count; i++)
            {
                var agvinfo = agvInfoList[i];
                int agvNum = agvinfo.AgvId;

                if (m_agvInfoDict.ContainsKey(agvinfo.AgvId))
                {
                    m_agvInfoDict[agvNum].Online = agvinfo.DataValid;
                    m_agvInfoDict[agvNum].Mark = agvinfo.Mark;
                    m_agvInfoDict[agvNum].Route = agvinfo.Route;
                    m_agvInfoDict[agvNum].Status = agvinfo.Status;

                    m_agvInfoDict[agvNum].AlarmCode = agvinfo.Alarm;
                    m_agvInfoDict[agvNum].BatteryPower = agvinfo.BatteyPower;
                    m_agvInfoDict[agvNum].BatteryVoltage = agvinfo.BatteyVoltage;
                    m_agvInfoDict[agvNum].BatteryCurrent = agvinfo.BatteyCurrent;
                    m_agvInfoDict[agvNum].BatteryTemperature = agvinfo.BatteyTemperature;
                    m_agvInfoDict[agvNum].LastUpdateTime = DateTime.Now;
                }
            }
            m_agvInfoManager.Update(m_agvInfoDict.Values.ToList());

        }

        public void Start()
        {
            Init();
            OnStart?.Invoke();

            m_userThreadRunning = true;
            m_dislayThreadRunning = true;
            Task.Factory.StartNew(UserThread);
            Task.Factory.StartNew(DisplayThread);

            DeviceControl.Instance.Communication.OnInfoSkq += Communication_OnInfoSkq;
        }

        private void Communication_OnInfoSkq(InfoSkq infoSkq)
        {
            List<byte> listSend = new List<byte>();
            var bytes = infoSkq.RawBytes;
            switch (bytes[4])
            {
                case 0xE1://询问工位状态

                    listSend.Add(0XD1);
                    if (bytes[5] == 0)
                    {
                        foreach (var station in m_stationInfoDict.Values)
                        {
                            listSend.Add((byte)station.Mark);
                            listSend.Add(station.GetDBRunValues());
                            listSend.Add(station.GetDBAgvId());
                            listSend.Add(station.GetDBAgvFalut());
                        }
                    }
                    else
                    {
                        var station = m_stationInfoDict.Values.FirstOrDefault(o => o.Mark == bytes[5]);
                        if (station != null)
                        {
                            listSend.Add((byte)station.Mark);
                            listSend.Add(station.GetDBRunValues());
                            listSend.Add(station.GetDBAgvId());
                            listSend.Add(station.GetDBAgvFalut());
                        }
                    }
                    break;
                case 0xE2://询问AGV状态
                    listSend.Add(0XD2);

                    var agv = DeviceControl.Instance.Communication.InfoAgvDictionary.Values.FirstOrDefault(o => o.AgvId == bytes[5]);
                    var agv2 = m_stationInfoDict.Values.FirstOrDefault(o => o.AgvId == bytes[5]);
                    if (agv != null && agv2 != null)
                    {
                        listSend.Add((byte)agv.AgvId);
                        listSend.Add(agv2.GetDBAgvFalut());
                        if (agv.DataValid)
                        {
                            listSend.Add((byte)1);
                        }
                        else
                        {
                            listSend.Add((byte)0);
                        }
                        listSend.Add((byte)agv.Mark);
                        listSend.Add((byte)0);
                        listSend.Add((byte)agv2.Mark);
                        listSend.Add((byte)0);
                        listSend.Add((byte)0);
                        listSend.Add((byte)agv.SpeedLevel);
                        listSend.Add((byte)((int)agv.BatteyPower));
                    }
                    break;
                case 0xE3://控制工位状态

                    int dataLength = (int)bytes[3];
                    for (int i = 5; i < 4 + dataLength; i = i + 2)
                    {
                        var station = m_stationInfoDict.Values.FirstOrDefault(o => o.Mark == bytes[i]);
                        if (station != null)
                        {
                            DeviceControl.Instance.ReleaseAgvOnStop(bytes[i], 0, station.TargetMark, station.AgvId);
                        }
                    }
                    listSend.Add(0xF3); listSend.Add(0x01);
                    break;
                case 0xE4://控制AGV站点状态
                    var station3 = m_stationInfoDict.Values.Where(o => o.AgvId == bytes[5] && bytes[5] != 0).ToList();
                    if (station3 != null)
                    {
                        for (int i = 0; i < station3.Count; i++)
                        {
                            DeviceControl.Instance.ReleaseAgvOnStop(station3[i].Mark, 0, bytes[6], station3[i].AgvId);
                        }
                    }

                    listSend.Add(0XF4); listSend.Add(0x01);
                    break;
                case 0xE5://控制AGV状态
                    var station2 = m_stationInfoDict.Values.FirstOrDefault(o => o.AgvId == bytes[5]);
                    if (station2 != null)
                    {
                        int status = (int)bytes[6];
                        if (status == 2 || status == 4)
                        {
                            DeviceControl.Instance.RailAgv_StopButton(station2.AgvId);
                        }
                        else if (status == 1)
                        {
                            DeviceControl.Instance.RailAgv_StartButton(station2.AgvId);
                        }

                    }
                    listSend.Add(0XF5); listSend.Add(0x01);
                    break;
                case 0xE6://控制AGV速度
                    station2 = m_stationInfoDict.Values.FirstOrDefault(o => o.AgvId == bytes[5]);
                    if (station2 != null)
                    {
                        int speedd = (int)bytes[6];
                        if (speedd > 0 && speedd <= 30)
                        {
                            DeviceControl.Instance.RailAgv_Control1071(station2.AgvId, 0, 1);
                        }
                        else if (speedd > 30 && speedd <= 60)
                        {
                            DeviceControl.Instance.RailAgv_Control1071(station2.AgvId, 0, 2);
                        }
                        else if (speedd > 60)
                        {
                            DeviceControl.Instance.RailAgv_Control1071(station2.AgvId, 0, 3);
                        }
                    }
                    listSend.Add(0XF6); listSend.Add(0x01);
                    break;
                case 0xE7://发送产品信息指令
                    break;
            }

            if (listSend.Count > 1)
            {
                List<byte> list2 = new List<byte>();
                list2.Add(bytes[0]); 
                list2.Add(bytes[1]); 
                list2.Add(bytes[2]);
                list2.Add((byte)listSend.Count);
                list2.AddRange(listSend.ToArray());
                byte[] crc = list2.ToArray().CRC16();
                list2.AddRange(crc);

                DeviceControl.Instance.Communication.SendCommand(list2.ToArray(), infoSkq.RemoteEndPoint);
            }
        }

        public void Stop()
        {
            m_userThreadRunning = false;
            m_dislayThreadRunning = false;

            OnDisplayInfo = null;
            OnDisplayEntityInfo = null;
        }

        private void UserThread()
        {
            byte counter = 0;
            while (m_userThreadRunning)
            {
                try
                {
                    StationInfoProc();

                    if (counter % 5 == 0)
                    {
                        TrafficProc();
                        DateTime start = DateTime.Now;
                        m_stationInfoManager.UpdateStatus(m_stationInfoDict.Values.ToList());
                        Parallel.ForEach(EntityBaseList, delegate (EntityBase entity, ParallelLoopState loopState)
                        {
                            DateTime start2 = DateTime.Now;
                            try
                            {
                                entity.CustomUpdate();
                            }
                            catch
                            {
                            }
                            OnDisplayEntityInfo?.Invoke(entity, $"{Math.Round((DateTime.Now - start2).TotalMilliseconds, 0)}ms");
                        });

                        OnDisplayInfo.Invoke($"{Math.Round((DateTime.Now - start).TotalMilliseconds, 0)}ms");
                    }

                    Thread.Sleep(100);
                }
                catch (Exception ex)
                {
                    CLog.Instance.TaskLog.WriteException("CoreEngine", ex);
                    Thread.Sleep(5000);
                }
                counter++;
            }

        }

        private void DisplayThread()
        {
            byte counter = 0;
            while (m_dislayThreadRunning)
            {
                try
                {
                    for (int i = 0; i < StationUIList.Count; i++)
                    {
                        var btn = StationUIList[i];
                        btn.BeginInvoke(new MethodInvoker(delegate
                        {
                            btn.Display();
                        }));
                    }

                    if (counter % 5 == 0)
                    {
                        UpdateAgvInfo();
                    }


                    //for (int i = 0; i < m_plcInfoList.Count; i++)
                    //{
                    //    var plcInfo = m_plcInfoList[i];

                    //    var result = plcInfo.PlcReadWrite.Write(plcInfo.HeartbeatAddress, m_plcHeartbeat);
                    //    plcInfo.Connected = result.IsSuccess;
                    //}

                    //m_plcHeartbeat++;
                    Thread.Sleep(100);
                }
                catch (Exception ex)
                {
                    CLog.Instance.TaskLog.WriteException("CoreEngine", ex);
                    Thread.Sleep(5000);
                }
                counter++;
            }
        }

        private void StationInfoProc()
        {
            var agvInfoList = DeviceControl.Instance.Communication.InfoAgvDictionary.Values.ToList();
            for (int i = 0; i < agvInfoList.Count; i++)
            {
                var model = agvInfoList[i];

                //判断有效的Agv信息
                if (model == null
                    || !model.DataValid
                    || model.AgvId == 0)
                {
                    continue;
                }

                foreach (var info in m_stationInfoDict.Values)
                {
                    if (info.Mark == model.Mark)
                    {
                        info.AgvId = model.AgvId;
                        info.CurrentRoute = model.Route;
                        info.AgvStausValue = (byte)model.Status;
                        info.AgvAlarm = (byte)model.Alarm;
                        info.AgvAlarmText = model.AlarmText;

                        switch (model.StatusEnum)
                        {
                            //当车停到某个地标时
                            case EnumInfoAgvStatus.结束点:
                            case EnumInfoAgvStatus.停止点:
                            case EnumInfoAgvStatus.充电中:
                                info.HasCar = true;
                                info.Status = LocationStatus.Filled;
                                break;
                            //当车从某个地标出发时
                            case EnumInfoAgvStatus.运行:
                                info.HasCar = false;
                                info.Status = LocationStatus.Empty;
                                break;
                            case EnumInfoAgvStatus.暂停:
                                info.Status = LocationStatus.Exception;
                                break;
                            default:
                                break;
                        }

                        if (info.AgvAlarm > 0)
                        {
                            info.Status = LocationStatus.Exception;
                        }
                    }
                    else
                    {
                        if (info.AgvId == model.AgvId)
                        {
                            info.HasCar = false;
                        }
                    }
                }
            }

            //判断是否在线
            foreach (var info in m_stationInfoDict.Values)
            {
                int count = DeviceControl.Instance.Communication.InfoAgvDictionary.Values.Where(d => d.DataValid && d.Mark == info.Mark).Count();
                info.Online = count > 0;

                if (!info.Online)
                {
                    info.Status = LocationStatus.None;
                }

            }

        }

        private void TrafficProc()
        {
            var trafficList = ExcelConfig.Instance.TrafficList;
            for (int index = 0; index < trafficList.Count; index++)
            {
                var tafficInfo = trafficList[index];
                tafficInfo.IsTraffic = CheckTraffic(tafficInfo.Marks, tafficInfo.ExtraMarks);
                for (int i = 0; i < tafficInfo.Marks.Length; i++)
                {
                    int mark = tafficInfo.Marks[i];
                    if (m_stationInfoDict.ContainsKey(mark))
                    {
                        m_stationInfoDict[mark].Traffic = tafficInfo.IsTraffic;
                    }
                }

                if (!tafficInfo.ReleaseCold)
                {
                    continue;
                }

                if (tafficInfo.IsTraffic)
                {
                    //检查有AGV正在管制区中
                    continue;
                }

                for (int i = 0; i < tafficInfo.Marks.Length; i++)
                {
                    int mark = tafficInfo.Marks[i];
                    if (m_stationInfoDict.ContainsKey(mark))
                    {
                        //工位不处理放行
                        continue;
                    }

                    var model = DeviceControl.Instance.Communication.InfoAgvDictionary.Values.FirstOrDefault(d => d.Mark == mark);
                    if (model != null
                        && model.DataValid
                        && model.StatusEnum == EnumInfoAgvStatus.停止点)
                    {
                        DeviceControl.Instance.ReleaseAgvOnStop(model.Mark, model.Route, model.AgvId);
                        tafficInfo.LastReleaseTime = DateTime.Now.Ticks;


                        CLog.Instance.TaskLog.WriteInfo($"管制放行，地标：{model.Mark}，路线：{model.Route}，AGV：{model.AgvId}，0x{model.Status:X2}");
                        break;

                    }
                }
            }
        }

        private bool CheckTraffic(int[] marks, int[] extraMarks)
        {
            int count = DeviceControl.Instance.Communication.InfoAgvDictionary.Values
                .Where(d => d.DataValid && marks.Contains(d.Mark) && (d.StatusEnum != EnumInfoAgvStatus.停止点))
                .Count();
            if (count > 0)
            {
                return true;
            }

            count = DeviceControl.Instance.Communication.InfoAgvDictionary.Values
                .Where(d => extraMarks.Contains(d.Mark))
                .Count();
            return count > 0;
        }

        private void InitStationInfo()
        {
            var stationList = m_stationInfoDict.Values.OrderBy(d => d.SerialNumber).ToList();

            for (int i = 0; i < stationList.Count; i++)
            {
                var info = stationList[i];
                var sInfo = m_stationInfoManager.GetById(info.Id);

                if (sInfo == null)
                {
                    m_stationInfoManager.Insert(info);
                }
                else
                {
                    info.Status = sInfo.Status;
                    info.CurrentRoute = sInfo.CurrentRoute;
                    info.AgvId = sInfo.AgvId;
                    info.HasCar = sInfo.HasCar;
                }
            }
        }

        private void InitAgvInfo()
        {
            for (int i = 1; i <= AgvMaxNum; i++)
            {
                var data = new AgvInfo();
                data.Id = i;

                m_agvInfoDict.AddOrUpdate(i, data, (key, value) => data);

                var sInfo = m_agvInfoManager.GetById(data.Id);
                if (sInfo == null)
                {
                    m_agvInfoManager.Insert(data);
                }
            }
        }
    }
}
