using DbCommon.BusinessCore.BaseCore;
using DbCommon.Enties.DbModels;
using ProjectManagementSystem.Common.Config;
using ProjectManagementSystem.Common.Extenions;
using ProjectManagementSystem.Common.Logger;
using ProjectManagementSystem.Device.Command;
using ProjectManagementSystem.Device.CommandCallback;
using ProjectManagementSystem.Device.Core;
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 DeviceControl
    {
        private static DeviceControl m_instance;

        private CommandRoller commandRollerQuery = new CommandRoller();
        public CasunCommunication Communication { get; private set; }

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

        private bool Initialize()
        {
            string ipAddress = Function.AppSetting.TryGetValue<string>("ListenIp");
            int port = Function.AppSetting.TryGetValue<int>("ListenPort");
            string comPortName = Function.AppSetting.TryGetValue<string>("ComPortName");

            Communication = new CasunCommunication();
            Communication.OnInfoRoller += Communication_OnInfoRoller;
            Communication.OnInfoIO += Communication_OnInfoIO;
            Communication.OnInfoBox += Communication_OnInfoBox;
            Communication.OnInfoBoxRelease += Communication_OnInfoBoxRelease;
            Communication.OnInfoAgv += Communication_OnInfoAgv;

            if (!string.IsNullOrEmpty(ipAddress))
            {
                var tcp = new CommunicationTcp(ipAddress, port);
                tcp.OnLogMsg += LogMsg;
                tcp.DataReceived += (addr, data) => Communication.Handling(addr, data);
                tcp.Start();

                if (tcp.IsStart) { Communication.Communications.Add(tcp); }
                else { return false; }

                var udp = new CommunicationUdp(ipAddress, port);
                udp.OnLogMsg += LogMsg;
                udp.DataReceived += (addr, data) => Communication.Handling(addr, data);
                udp.Start();

                if (udp.IsStart) { Communication.Communications.Add(udp); }
                else { return false; }
            }

            if (!string.IsNullOrEmpty(comPortName))
            {
                var com = new CommunicationCom(comPortName, 9600);
                com.OnLogMsg += LogMsg;
                com.DataReceived += (addr, data) => Communication.Handling(addr, data);
                com.Start();

                if (com.IsStart) { Communication.Communications.Add(com); }
                else { return false; }
            }

            var vcu = CommunicationVcu.Instance;
            vcu.OnLogMsg += LogMsg;
            vcu.DataReceived += (addr, data) => Communication.Handling(addr, data);
            Communication.Communications.Add(vcu);

            CLog.Instance.SystemLog.WriteDebug($"DeviceControl已启动");
            return true;
        }

        private void Communication_OnInfoAgv(InfoAgv info)
        {
            CLog.Instance.SystemLog.WriteDebug($"AGV{info.AgvId}：地标{info.Mark} 路线{info.Route} 状态{info.Status}({info.StatusText})，报警状态{info.Alarm}({info.AlarmText})");
        }

        private void Communication_OnInfoBoxRelease(InfoBoxRelease info)
        {
            if (info.BoxReleaseLightOn)
            {
                CLog.Instance.SystemLog.WriteInfo($"放行盒已亮灯，地标{info.Mark} 路线{info.Route} AGV{info.AgvId}");
            }

            if (info.BoxReleaseAgv)
            {
                CLog.Instance.SystemLog.WriteInfo($"放行盒已放行，地标{info.Mark} 路线{info.Route} AGV{info.AgvId}");
            }
        }

        private void Communication_OnInfoBox(InfoBox info)
        {
            if (info.BoxCalled || info.BoxCancelCall)
            {
                RouteCalled.Instance.Call(info.Route, info.BoxCalled, info.BoxCancelCall);
                return;
            }
        }

        private void Communication_OnInfoIO(InfoIO info)
        {
            //if (info.DataValid
            //    && info.DeviceId > 0)
            //{
            //    RouteConfig routeConfig = ExcelConfig.Instance.RouteConfigList.FirstOrDefault(d => d.IOTransID == info.DeviceId);
            //    if (routeConfig != null)
            //    {
            //        RouteCalled.Instance.Call(routeConfig.Route, info.X1);
            //    }
            //}
        }

        private void Communication_OnInfoRoller(InfoRoller info)
        {
            //if (info.DataValid
            //    && info.DeviceId > 0)
            //{
            //    RouteConfig routeConfig = ExcelConfig.Instance.RouteConfigList.FirstOrDefault(d => d.RollerID == info.DeviceId);
            //    if (routeConfig != null)
            //    {
            //        if (info.FullTray)
            //        {
            //            RouteCalled.Instance.Call(routeConfig.Route, true);
            //        }
            //        else
            //        {
            //            if (routeConfig.CancelLineStauts == 0)
            //            {
            //                RouteCalled.Instance.Call(routeConfig.Route, false);
            //            }

            //            if (routeConfig.CancelLineStauts == 2)
            //            {
            //                if (info.FullEmpty)
            //                {
            //                    RouteCalled.Instance.Call(routeConfig.Route, false);
            //                }
            //                else
            //                {
            //                    RouteCalled.Instance.Call(routeConfig.Route, true);
            //                }
            //            }
            //        }

            //    }
            //}
        }

        private void LogMsg(string obj, Exception ex)
        {
            if (ex == null)
            {
                CLog.Instance.DeviceLog.WriteDebug(obj);
            }
            else
            {
                CLog.Instance.DeviceLog.WriteException("DeviceControl", obj, ex);
            }
        }

        #region IO透传功能

        private int GetIOAddressIndex(string address)
        {
            //解析地址，格式Y1-Y8（输出信号）
            var invalidValue = -1;
            if (string.IsNullOrEmpty(address)) return invalidValue;
            if (!int.TryParse(address.Substring(1), out int index))
            {
                return invalidValue;
            }
            index--;
            if (index < 0)
            {
                return invalidValue;
            }
            return index;
        }

        public bool CheckInputIO(int id, string[] addressArray, bool[] bitArray, out string msg)
        {
            msg = null;
            for (int i = 0; i < addressArray.Length; i++)
            {
                string plcAddr = addressArray[i];
                bool plcValue = bitArray[i];

                var result2 = GetInputIO(id, addressArray[i]);
                if (result2.Item1 == false
                    || result2.Item2 != plcValue)
                {
                    msg = $"等待信号({plcAddr}={plcValue})";
                    return false;
                }
            }

            return true;
        }

        public bool CheckOutputIO(int id, string[] addressArray, bool[] bitArray, out string msg)
        {
            msg = null;
            for (int i = 0; i < addressArray.Length; i++)
            {
                string plcAddr = addressArray[i];
                bool plcValue = bitArray[i];

                var result = GetOutputIO(id, addressArray[i]);
                if (result.Item1 == false
                    || result.Item2 != plcValue)
                {
                    msg = $"写入失败({plcAddr}写入{plcValue})";
                    return false;
                }
            }

            return true;
        }

        public Tuple<bool, bool> GetInputIO(int id, string address)
        {
            int index = GetIOAddressIndex(address);
            if (index < 0) return new Tuple<bool, bool>(false, false);

            if (!Communication.InfoIODictionary.ContainsKey(id))
            {
                return new Tuple<bool, bool>(false, false);
            }

            var info = Communication.InfoIODictionary[id];
            if (!info.DataValid) return new Tuple<bool, bool>(false, false);

            bool bitTrue = (info.Input & (0x01 << index)) > 0;
            return new Tuple<bool, bool>(true, bitTrue);
        }

        public Tuple<bool, bool> GetOutputIO(int id, string address)
        {
            int index = GetIOAddressIndex(address);
            if (index < 0) return new Tuple<bool, bool>(false, false);

            if (!Communication.InfoIODictionary.ContainsKey(id))
            {
                return new Tuple<bool, bool>(false, false);
            }

            var info = Communication.InfoIODictionary[id];
            if (!info.DataValid) return new Tuple<bool, bool>(false, false);

            bool bitTrue = (info.Output & (0x01 << index)) > 0;
            return new Tuple<bool, bool>(true, bitTrue);
        }

        public void SetOutputIO(int id, string address, bool bit)
        {
            SetOutputIO(id, new string[] { address }, new bool[] { bit });
        }

        public void SetOutputIO(int id, string[] addressArray, bool[] bitArray)
        {
            if (addressArray == null || addressArray.Length == 0) return;
            if (bitArray == null || bitArray.Length == 0) return;

            int[] indexArray = new int[addressArray.Length];

            for (int i = 0; i < addressArray.Length; i++)
            {
                string address = addressArray[i];
                indexArray[i] = GetIOAddressIndex(address);
            }

            SetOutputIO(id, indexArray, bitArray);
        }

        public bool SetOutputIO_ThenCheck(int id, string[] addressArray, bool[] bitArray, out string msg)
        {
            msg = null;
            SetOutputIO(id, addressArray, bitArray);

            for (int i = 0; i < 5; i++)
            {
                if (CheckOutputIO(id, addressArray, bitArray, out msg))
                {
                    return true;
                }
                Thread.Sleep(20);
            }

            return false;
        }

        public void SetOutputIO(int id, int[] indexArray, bool[] bitArray)
        {
            if (!Communication.InfoIODictionary.ContainsKey(id))
            {
                return;
            }

            var info = Communication.InfoIODictionary[id];
            if (!info.DataValid) return;

            ushort newOutput = info.Output;

            for (int i = 0; i < indexArray.Length; i++)
            {
                int index = indexArray[i];
                if (index < 0)
                {
                    return;
                }

                bool bit = bitArray[i];

                if (bit)
                {
                    newOutput = (ushort)(newOutput | (0x01 << index));
                }
                else
                {
                    newOutput = (ushort)(newOutput & (ushort.MaxValue - (1 << index)));
                }

            }

            SetOutputIO(id, newOutput);
        }

        public void SetOutputIO(int id, ushort output)
        {
            var data = new CommandIO();
            data.TargetID = (byte)id;
            data.Output = output;

            SendCommand(data, id, Communication.InfoIODictionary);
        }

        public void SetOutputIOClear(int id)
        {
            if (Communication.InfoIODictionary.ContainsKey(id)
                && Communication.InfoIODictionary[id].Output != 0)
            {
                SetOutputIO(id, 0);
                CLog.Instance.SystemLog.WriteInfo($"对接盒ID{id}，清零输出");
                Thread.Sleep(100);
            }
        }
        #endregion

        #region 滚筒台对接
        public void RollerOperate(int id, ushort code)
        {
            var data = new CommandRoller();
            data.TargetID = (byte)id;
            data.ActionCode = code;

            SendCommand(data, id, Communication.InfoRollerDictionary);
        }

        public void RollerOperateClear(int id)
        {
            var data = new CommandRoller();
            data.TargetID = (byte)id;
            data.Operate = 0;
            data.ActionCode = 0;

            SendCommand(data, id, Communication.InfoRollerDictionary);
        }

        public void RollerActionClear(int id)
        {
            var data = new CommandRoller();
            data.TargetID = (byte)id;
            data.ActionCode = 0;

            SendCommand(data, id, Communication.InfoRollerDictionary);
        }

        public void RollerQuery(int id)
        {
            commandRollerQuery.Function = 0x21;
            commandRollerQuery.TargetID = (byte)id;

            SendCommand(commandRollerQuery, id, Communication.InfoRollerDictionary);
        }
        public void RollerQuery()
        {
            var configList = ExcelConfig.Instance.RouteConfigList;
            for (int i = 0; i < configList.Count; i++)
            {
                var config = configList[i];
                if (config.RollerID > 0)
                {
                    RollerQuery(config.RollerID);
                }
            }

        }
        #endregion

        #region 报警灯功能
        public void SetAlarmLight(int id, int lightType)
        {
            var data = new CommandAlarmLight();
            data.DeviceId = (byte)id;
            data.LightType = (byte)lightType;

            SendCommand(data, id, Communication.InfoAlarmLightDictionary);
        }
        #endregion

        #region 叫料盒功能
        public void CallBoxQuery(int route)
        {
            var data = new CommandBox();
            data.Route = (byte)route;
            data.Function = 0x40;

            SendCommand(data, route, Communication.InfoBoxDictionary);
        }

        public void CallBoxLightOff(int route)
        {
            var data = new CommandBox();
            data.Route = (byte)route;
            data.Function = 0x45;

            SendCommand(data, route, Communication.InfoBoxDictionary);
        }
        #endregion

        #region 放行盒功能
        public void ReleaseBoxLightOn(int mark, int route = 0, int agvId = 0)
        {
            var data = new CommandBox();
            data.Mark = (byte)mark;
            data.Route = (byte)route;
            data.AgvId = (byte)agvId;
            data.Function = 0x35;

            SendCommand(data, agvId, Communication.InfoBoxDictionary);
        }

        public void ReleaseBoxLightOff(int mark, int route = 0, int agvId = 0)
        {
            var data = new CommandBox();
            data.Mark = (byte)mark;
            data.Route = (byte)route;
            data.AgvId = (byte)agvId;
            data.Function = 0x38;

            SendCommand(data, agvId, Communication.InfoBoxDictionary);
        }
        #endregion

        #region 有轨AGV
        public void ReleaseAgvOnStop(int mark, int route, int agvId)
        {
            var data = new CommandBox();
            data.Mark = (byte)mark;
            data.Route = (byte)route;
            data.AgvId = (byte)agvId;
            data.Function = 0x37;

            SendCommand(data, agvId, Communication.InfoAgvDictionary);
        }

        public void ReleaseAgvOnStop(int mark, int route, int targetMark, int agvId)
        {
            var data = new CommandBoxRelease();
            data.Mark = (byte)mark;
            data.Spare = (byte)targetMark;
            data.Route = (byte)route;
            data.AgvId = (byte)agvId;
            data.Function = 0x37;

            SendCommand(data, agvId, Communication.InfoAgvDictionary);
        }

        public void ReleaseAgvOnStandby(int mark, int route, int agvId)
        {
            var data = new CommandBox();
            data.Mark = (byte)mark;
            data.Route = (byte)route;
            data.AgvId = (byte)agvId;
            data.Function = 0x31;

            SendCommand(data, agvId, Communication.InfoAgvDictionary);
        }

        public void RailAgv_Query1061(int agvId = 0)
        {
            var data = new Command1061();

            SendCommand(data, agvId, Communication.InfoAgvDictionary);
        }

        public void RailAgv_Query1063(int fps, int agvId = 0)
        {
            var data = new Command1063();
            data.FPS = fps;

            SendCommand(data, agvId, Communication.InfoAgvDictionary);
        }

        public void RailAgv_Query()
        {
            for (int i = 1; i <= 3; i++)
            {
                RailAgv_Query1063(i);
                Thread.Sleep(100);
            }
        }

        public void RailAgv_StartButton(int agvId)
        {
            RailAgv_Control1071(agvId, 0, 0);
        }

        public void RailAgv_StopButton(int agvId)
        {
            RailAgv_Control1071(agvId, 1, 0);
        }

        public void RailAgv_Control1071(int agvId, int iOperation = 0, int iSpeed = 0)
        {
            var data = new Command1071();
            data.Function = 0x71;
            data.Operation = (byte)iOperation;
            data.Speed = (byte)iSpeed;
            data.AgvId = (byte)agvId;

            SendCommand(data, agvId, Communication.InfoAgvDictionary);
        }

        /// <summary>
        /// 改变AGV速度
        /// </summary>
        /// <param name="agvId">AGV编号</param>
        /// <param name="speed">AGV速度，单位：m/min</param>
        public void RailAgv_SpeedChange(int agvId, decimal speed)
        {
            var data = new CommandBox();
            data.Spare = (int)Math.Round(speed * 100);
            data.AgvId = (byte)agvId;
            data.Function = 0x47;

            SendCommand(data, agvId, Communication.InfoAgvDictionary);
        }

        public void RailAgv_ReleaseAlarm(int agvId, int alarmSec)
        {
            var data = new CommandBox();
            data.Spare = (byte)alarmSec;
            data.AgvId = (byte)agvId;
            data.Function = 0x49;

            SendCommand(data, agvId, Communication.InfoAgvDictionary);
        }
        #endregion

        /// <summary>
        /// 根据设备ID查找设备终结点，并向指定终结点发送指令
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="cmd"></param>
        /// <param name="deviceId"></param>
        /// <param name="infoDict"></param>
        public void SendCommand<TKey, TValue>(IDeviceCommand cmd, TKey deviceId, IDictionary<TKey, TValue> infoDict)
            where TKey : struct
            where TValue : BaseInfo
        {
            string ipAddress = null;
            if (infoDict.ContainsKey(deviceId))
            {
                ipAddress = infoDict[deviceId].RemoteEndPoint;
            }

            var data = cmd.Serialization();

            //CLog.Instance.SystemLog.WriteInfo($"Send {ipAddress} {data.ToHexString()}");
            Communication.SendCommand(data, ipAddress);
        }

    }
}
