using HslCommunication;
using ProjectManagementSystem.Common.Config;
using ProjectManagementSystem.Common.Extenions;
using ProjectManagementSystem.Common.Logger;
using ProjectManagementSystem.Common.Models;
using ProjectManagementSystem.Common.WebApi;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ProjectManagementSystem.Common.Core
{
    public class PackControl
    {
        private static PackControl m_instance;
        private bool checkOnline = true;
        private bool firstStart = true;
        public ConcurrentDictionary<string, PackStation> StationDictionary { get; set; } = new ConcurrentDictionary<string, PackStation>();

        public static PackControl Instance
        {
            get
            {
                if (m_instance == null)
                {
                    m_instance = new PackControl();
                    if (!m_instance.Initialize())
                    {
                        m_instance = null;
                    }
                }
                return m_instance;
            }
        }


        private PackControl()
        {
        }


        private bool Initialize()
        {
            try
            {
                var configList = ExcelConfig.Instance.RouteConfigList;

                for (int i = 0; i < configList.Count; i++)
                {
                    var config = configList[i];
                    PackStation data = StationDictionary.ContainsKey(config.LocationCode) ? StationDictionary[config.LocationCode] : new PackStation();

                    data.Id = config.LocationCode;
                    data.routeConfig = config;


                    data.请求进入导航点 = config.LocationCode.GetLocationMember("AGV请求进入导航点").ToValue<int>();

                    data.EnteringEdges = config.LocationCode.GetLocationMember("AGV正在进入路段").ToValueArray<int>();
                    data.EnteringVertexs = config.LocationCode.GetLocationMember("AGV正在进入导航点").ToValueArray<int>();

                    data.工位导航点 = config.GraphVertex;

                    data.LeavingEdges = config.LocationCode.GetLocationMember("AGV正在离开路段").ToValueArray<int>();
                    data.LeavingVertexs = config.LocationCode.GetLocationMember("AGV正在离开导航点").ToValueArray<int>();

                    StationDictionary.AddOrUpdate(data.Id, data, (key, value) => data);
                }

                Task.Factory.StartNew(UserThread);
                Task.Factory.StartNew(UserThread2);
                return true;
            }
            catch (Exception ex)
            {
                CLog.Instance.SystemLog.WriteException("PackControl Initialize", ex);
            }

            return false;
        }

        private void TaskCustomProc()
        {
            //return;
            //AGV状态
            //0：空闲，
            //1：配送中，
            //3：完成
            //4：充电中
            //5:发生异常
            //默认值：65535
            //return;

            var agvDataList1 = PmsApi.GetAllCarrier();
            if (agvDataList1 == null) return;
            var agvDataList = checkOnline ? agvDataList1.Where(d => d.Online) : agvDataList1.Where(d => true);

            var configList = ExcelConfig.Instance.RouteConfigList;

            for (int i = 0; i < configList.Count; i++)
            {
                var config = configList[i];
                if (!config.PackTaskAddCooled)
                {
                    //CLog.Instance.TaskLog.WriteInfo($"{config.LocationCode} 限制添加任务（未冷却）");
                    continue;
                }


                //已到位的AGV可以出发后任务模板
                var agvData = agvDataList?.FirstOrDefault(d => /*d.Status == 0 && */d.GraphVertex == config.GraphVertex);
                if (agvData != null)
                {
                    var taskList = PmsApi.GetTaskList();
                    if (taskList != null
                        && taskList.FirstOrDefault(d => d.Carrier == agvData.AgvID || d.BindingAGVNumber == agvData.AgvID) == null)
                    {
                        //添加任务
                        string nextLocation = GetPlcNextLocation(config, agvData.AgvID, out string templateName);

                        if (!string.IsNullOrEmpty(nextLocation)
                            && !string.IsNullOrEmpty(templateName))
                        {
                            AddPackTask(templateName, agvData.AgvID, config.LocationCode, nextLocation);
                        }
                    }
                }


                //工位前的任务导航点：可根据前任务导航点，匹配不一样的任务模板
                var vertexArray = config.LocationCode.GetLocationMember("前任务导航点").ToValueArray<int>();
                for (int index = 0; index < vertexArray.Length; index++)
                {
                    int vertex = vertexArray[index];

                    agvData = agvDataList?.FirstOrDefault(d => d.Status == 0 && vertex == d.GraphVertex);
                    if (agvData != null)
                    {
                        //判断是否直通
                        var passToNextLocation = GetPlcAllowToPassNextLocation(config, agvData.AgvID);
                        if (!string.IsNullOrEmpty(passToNextLocation))
                        {
                            string preTemplateName = config.LocationCode.GetLocationMember("直通任务模板").GetArrayValue<string>(index);
                            AddPackTask(preTemplateName, agvData.AgvID, config.LocationCode, passToNextLocation);
                        }
                        else
                        {
                            string preTemplateName = config.LocationCode.GetLocationMember("前任务模板").GetArrayValue<string>(index);
                            AddPackTask(preTemplateName, agvData.AgvID, config.LocationCode);
                        }

                    }
                }


            }

        }

        private string GetPlcAllowToPassNextLocation(RouteConfig config, int agvId)
        {
            string plcAllowToPass = config.LocationCode.GetLocationMember("PLC允许通过");
            if (string.IsNullOrEmpty(plcAllowToPass)) return null;

            var plc = config.PlcReadWrite;
            var read = plc.ReadBool(plcAllowToPass);
            if (!read.IsSuccess && !read.Content)
            {
                return null;
            }

            string nextLocation = config.LocationCode.GetLocationMember("下个工位");
            if (string.IsNullOrEmpty(nextLocation))
            {
                CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 没配置下个工位");
                return null;
            }

            return nextLocation.ToValueArray<string>()[0];
        }

        private string GetPlcNextLocation(RouteConfig config, int agvId, out string templateName)
        {
            if (!checkOnline)
            {
                //模拟测试
                templateName = config.LocationCode.GetLocationMember("后任务模板");
                return config.LocationCode.GetLocationMember("下个工位").ToValueArray<string>()[0];
            }

            templateName = null;
            if (string.IsNullOrEmpty(config.PlcIpAddr))
            {
                return null;
            }


            //var plc放行 = config.LocationCode.GetLocationMember("PLC放行");

            //产生任务的地址改成允许上升地址
            var plc放行 = config.LocationCode.GetLocationMember("PLC允许上升");
            if (string.IsNullOrEmpty(plc放行))
            {
                plc放行 = config.LocationCode.GetLocationMember("PLC放行");
                if (string.IsNullOrEmpty(plc放行))
                {
                    return null;
                }
            }

            var plc = config.PlcReadWrite;
            var read = plc.ReadBool(plc放行);
            if (!read.IsSuccess || !read.Content)
            {
                return null;
            }
            //已有放行信号

            string nextLocation = config.LocationCode.GetLocationMember("下个工位");
            if (string.IsNullOrEmpty(nextLocation))
            {
                CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 没配置下个工位");
                return null;
            }

            templateName = config.LocationCode.GetLocationMember("后任务模板");
            nextLocation = nextLocation.ToValueArray<string>()[0];
            var plc返修 = config.LocationCode.GetLocationMember("PLC返修");
            if (string.IsNullOrEmpty(plc返修))
            {
                //没有配置返修地址，直接去下个工位
                CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 没配置返修地址，去默认下个工位 {nextLocation} （读取{plc放行} = {read.Content}）");
                return nextLocation;
            }

            read = plc.ReadBool(plc返修);
            if (!read.IsSuccess)
            {
                return null;
            }

            if (read.Content == false)
            {
                //不需要返修去下个工位
                CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 不需要返修，去默认下个工位 {nextLocation} （读取{plc返修} = {read.Content}）");
                return nextLocation;
            }

            //已有需要返修的信号
            templateName = config.LocationCode.GetLocationMember("返修任务模板");
            if (string.IsNullOrEmpty(templateName))
            {
                CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 需要返修，但没配置返修任务模板");
                return null;
            }

            //需要返修去固定返修位置
            string ngLocation = config.LocationCode.GetLocationMember("返修工位");
            if (string.IsNullOrEmpty(ngLocation))
            {
                CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 需要返修，但没配置返修工位");
                return null;
            }

            CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 需要返修，去返修工位 {ngLocation} （读取{plc返修} = {read.Content}）");
            return ngLocation;
        }

        private bool AddPackTask(string templateName, int agvId, params string[] locations)
        {
            //添加任务
            if (string.IsNullOrEmpty(templateName)) return false;

            var taskList = PmsApi.GetTaskList();
            if (taskList == null) return false;

            var taskData = taskList.FirstOrDefault(d => d.Carrier == agvId || d.BindingAGVNumber == agvId);
            if (taskData != null)
            {
                //指定AGV已经有任务，就不添任务
                return false;
            }

            MidTaskInfoEx data = new MidTaskInfoEx();
            data.TemplateName = templateName;

            for (int i = 0; i < locations.Length; i++)
            {
                data.ParametersDic.Add($"U{i + 1}", locations[i]);
            }

            data.AgvType = "0";
            data.AgvId = agvId;
            data.Priority = 0;
            data.CreateTime = DateTime.Now;

            bool result = PmsApi.TaskAdd(data);
            if (result)
            {
                for (int i = 0; i < locations.Length; i++)
                {
                    var location = locations[i];
                    var config = ExcelConfig.Instance.GetRouteConfig(location);
                    if (config != null)
                    {
                        config.LastPackTaskAddTime = DateTime.Now;
                    }
                }
            }
            return result;
        }

        private void UpdateStationDict(bool[] forceWrite)
        {
            var agvDataList1 = PmsApi.GetAllCarrier();
            if (agvDataList1 == null) return;

            var agvDataList = checkOnline ? agvDataList1.Where(d => d.Online) : agvDataList1.Where(d => true);

            DateTime start = DateTime.Now;
            Parallel.ForEach(StationDictionary.Values, delegate (PackStation data, ParallelLoopState loopState)
            {
                try
                {
                    var agvData = agvDataList.FirstOrDefault(d => data.请求进入导航点 > 0 && d.GraphVertex == data.请求进入导航点);
                    bool result = agvData != null;
                    data.请求进入AGV信息 = result ? $"({agvData.AgvID}# 路段:{agvData.GraphEdge} 导航点:{agvData.GraphVertex})" : null;
                    if (data.请求进入 != result || forceWrite[0])
                    {
                        data.请求进入 = result;
                        HandleAgvToPlc(data.routeConfig, "AGV请求进入", data.请求进入, data.请求进入AGV信息);
                    }


                    var agvDataAtStation = agvDataList.FirstOrDefault(d => data.工位导航点 > 0 && d.GraphVertex == data.工位导航点);
                    result = agvDataAtStation != null;
                    data.到位AGV信息 = result ? $"({agvDataAtStation.AgvID}# 路段:{agvDataAtStation.GraphEdge} 导航点:{agvDataAtStation.GraphVertex})" : null;

                    if (data.到位 != result || forceWrite[1])
                    {
                        data.到位 = result;
                        HandleAgvToPlc(data.routeConfig, "AGV到位", data.到位, data.到位AGV信息);
                    }


                    var agvDataEntering = agvDataList.FirstOrDefault(d =>
                    (d.GraphEdge > 0 && data.EnteringEdges.Length > 0 && data.EnteringEdges.Contains(d.GraphEdge)) ||
                    (d.GraphVertex > 0 && data.EnteringVertexs.Length > 0 && data.EnteringVertexs.Contains(d.GraphVertex)));

                    result = agvDataEntering != null;
                    data.正在进入AGV信息 = result ? $"({agvDataEntering.AgvID}# 路段:{agvDataEntering.GraphEdge} 导航点:{agvDataEntering.GraphVertex})" : null;

                    if (data.正在进入 != result || firstStart)
                    {
                        data.正在进入 = result;
                        HandleAgvToPlc(data.routeConfig, "AGV正在进入", data.正在进入, data.正在进入AGV信息);

                    }

                    if (forceWrite[2])
                    {
                        data.正在进入 = result;
                        if (data.正在进入)
                        {
                            HandleAgvToPlc(data.routeConfig, "AGV正在进入", data.正在进入, data.正在进入AGV信息);
                        }
                        else
                        {
                            //清零信号需要已经离开前面的导航点
                            if (data.请求进入 == false)
                            {
                                HandleAgvToPlc(data.routeConfig, "AGV正在进入", data.正在进入, data.正在进入AGV信息);
                            }
                        }
                    }


                    var agvDataLeaving = agvDataList.FirstOrDefault(d =>
                    (d.GraphEdge > 0 && data.LeavingEdges.Length > 0 && data.LeavingEdges.Contains(d.GraphEdge)) ||
                    (d.GraphVertex > 0 && data.LeavingVertexs.Length > 0 && data.LeavingVertexs.Contains(d.GraphVertex)));

                    result = agvDataLeaving != null;
                    data.正在离开AGV信息 = result ? $"({agvDataLeaving.AgvID}# 路段:{agvDataLeaving.GraphEdge} 导航点:{agvDataLeaving.GraphVertex})" : null;

                    if (data.正在离开 != result || firstStart)
                    {
                        data.正在离开 = result;
                        HandleAgvToPlc(data.routeConfig, "AGV正在离开", data.正在离开, data.正在离开AGV信息);
                    }

                    if (forceWrite[3])
                    {
                        data.正在离开 = result;
                        if (data.正在离开)
                        {
                            HandleAgvToPlc(data.routeConfig, "AGV正在离开", data.正在离开, data.正在离开AGV信息);
                        }
                        else
                        {
                            //清零信号需要已经离开前面的导航点
                            if (data.到位 == false)
                            {
                                HandleAgvToPlc(data.routeConfig, "AGV正在离开", data.正在离开, data.正在离开AGV信息);
                            }
                        }
                    }


                    //LogicBit 9  上升到位状态 512
                    //LogicBit 10 下降到位状态 1024
                    result = agvDataAtStation != null ? agvDataAtStation.LogicBits.GetBitVaule(9) : false;
                    data.上升到位AGV信息 = result ? $"({agvDataAtStation.AgvID}# 路段:{agvDataAtStation.GraphEdge} 导航点:{agvDataAtStation.GraphVertex}) LogicBits:{agvDataAtStation.LogicBits}" : null;

                    if (data.AGV上升到位 != result || forceWrite[4])
                    {
                        data.AGV上升到位 = result;
                        HandleAgvToPlc(data.routeConfig, nameof(data.AGV上升到位), data.AGV上升到位, data.上升到位AGV信息);
                    }

                    result = agvDataAtStation != null ? agvDataAtStation.LogicBits.GetBitVaule(10) : false;
                    data.下降到位AGV信息 = result ? $"({agvDataAtStation.AgvID}# 路段:{agvDataAtStation.GraphEdge} 导航点:{agvDataAtStation.GraphVertex}) LogicBits:{agvDataAtStation.LogicBits}" : null;

                    if (data.AGV下降到位 != result || forceWrite[5])
                    {
                        data.AGV下降到位 = result;
                        HandleAgvToPlc(data.routeConfig, nameof(data.AGV下降到位), data.AGV下降到位, data.下降到位AGV信息);
                    }


                    var agvId = agvDataAtStation == null ? 0 : agvDataAtStation.AgvID;
                    if (data.AGV编号 != agvId || forceWrite[1])
                    {
                        data.AGV编号 = agvId;
                        HandleAgvToPlc(data.routeConfig, nameof(data.AGV编号), (ushort)data.AGV编号, data.到位AGV信息);
                    }

                    var alarmId = agvDataAtStation == null || agvDataAtStation.AlarmList == null || agvDataAtStation.AlarmList.Count == 0 ?
                    0 : agvDataAtStation.AlarmList.OrderByDescending(d => d.alarmLevel).FirstOrDefault().alarmId;
                    if (data.AGV报警代码 != alarmId || forceWrite[1])
                    {
                        data.AGV报警代码 = alarmId;
                        HandleAgvToPlc(data.routeConfig, nameof(data.AGV报警代码), (ushort)data.AGV报警代码, data.到位AGV信息);
                    }

                    data.异常消息 = null;
                }
                catch (Exception ex)
                {
                    data.异常消息 = ex.Message;
                }

                data.耗时 = Math.Round((DateTime.Now - start).TotalMilliseconds);
            });
        }

        //private void HandleAgvToPlc(RouteConfig routeConfig, string plcAddrDesc, bool agvValue, string agvInfo)
        //{
        //    string plcAddr = routeConfig.LocationCode.GetLocationMember(plcAddrDesc);
        //    if (!string.IsNullOrEmpty(plcAddr)
        //        && routeConfig.PlcReadWrite != null
        //        && routeConfig.PlcConnected)
        //    {
        //        var readResult = routeConfig.PlcReadWrite.ReadBool(plcAddr);
        //        if (readResult.IsSuccess
        //            && readResult.Content != agvValue)
        //        {
        //            var result = routeConfig.PlcReadWrite.Write(plcAddr, agvValue);
        //            string strLog = $"{routeConfig.LocationCode} {routeConfig.PlcIpAddr} {plcAddrDesc}：{plcAddr} = {agvValue} 写入{result.IsSuccess.ToChineseString()} {agvInfo}";
        //            CLog.Instance.TaskLog.WriteInfo(strLog);
        //        }
        //    }
        //}

        private void HandleAgvToPlc<T>(RouteConfig routeConfig, string plcAddrDesc, T agvValue, string agvInfo)
        {
            string plcAddr = routeConfig.LocationCode.GetLocationMember(plcAddrDesc);
            if (!string.IsNullOrEmpty(plcAddr)
                && routeConfig.PlcReadWrite != null
                && routeConfig.PlcConnected)
            {
                var readResult = routeConfig.PlcReadWrite.ReadValue(plcAddr, agvValue, out OperateResult<T> operateResult);
                if (!readResult)
                {
                    var result = routeConfig.PlcReadWrite.WriteValue(plcAddr, agvValue);
                    string strLog = $"{routeConfig.LocationCode} {routeConfig.PlcIpAddr} {plcAddrDesc}：{plcAddr} = {agvValue} 写入{result.IsSuccess.ToChineseString()} {agvInfo}";
                    CLog.Instance.TaskLog.WriteInfo(strLog);
                }
            }
        }

        private void UserThread()
        {
            CLog.Instance.SystemLog.WriteDebug($"PackControl已启动");

            uint couter = 0;
            bool[] forces = new bool[6];
            while (true)
            {
                try
                {
                    //错开刷新
                    var mod = couter % 10;
                    if (mod >= 0 && mod <= forces.Length)
                    {
                        for (int i = 0; i < forces.Length; i++)
                        {
                            forces[i] = (mod == i);
                        }
                    }


                    DateTime start = DateTime.Now;
                    UpdateStationDict(forces);

                    System.Diagnostics.Trace.WriteLine($"UpdateStationDict {string.Join(" ", forces.Select(d => { return d ? "1" : "0"; }))} 耗时：{(DateTime.Now - start).TotalMilliseconds} ms");
                }
                catch (Exception ex)
                {
                    CLog.Instance.SystemLog.WriteException("PackControl", ex);
                    Thread.Sleep(5000);
                }

                firstStart = false;
                couter++;
                Thread.Sleep(1000);
            }
        }

        private void UserThread2()
        {
            CLog.Instance.SystemLog.WriteDebug($"PackControl2已启动");
            while (true)
            {
                try
                {
                    TaskCustomProc();
                }
                catch (Exception ex)
                {
                    CLog.Instance.SystemLog.WriteException("PackControl2", ex);
                    Thread.Sleep(5000);
                }
                Thread.Sleep(500);
            }
        }
    }

    public class PackStation
    {
        public string Id { get; set; }

        internal RouteConfig routeConfig { get; set; }

        public int 工位导航点 { get; set; }
        public int 请求进入导航点 { get; set; }
        public int[] EnteringVertexs { get; set; }
        public int[] EnteringEdges { get; set; }

        public int[] LeavingVertexs { get; set; }
        public int[] LeavingEdges { get; set; }


        public bool 请求进入 { get; set; }
        public bool 正在进入 { get; set; }
        public bool 到位 { get; set; }
        public bool 正在离开 { get; set; }
        public bool 区域有Agv { get { return 正在进入 || 到位 || 正在离开; } }


        public bool AGV上升到位 { get; set; }
        public bool AGV下降到位 { get; set; }
        public int AGV编号 { get; set; }
        public int AGV报警代码 { get; set; }


        public string 请求进入AGV信息 { get; set; }
        public string 正在进入AGV信息 { get; set; }
        public string 到位AGV信息 { get; set; }
        public string 正在离开AGV信息 { get; set; }
        public string 上升到位AGV信息 { get; set; }
        public string 下降到位AGV信息 { get; set; }

        public double 耗时 { get; set; }
        public string 异常消息 { get; set; }

    }
}
