using ProjectManagementSystem.Common.Config;
using ProjectManagementSystem.Common.Logger;
using ProjectManagementSystem.Common.Core;
using ProjectManagementSystem.Common.Extenions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Pms.DataLibrary.Order;
using ProjectManagementSystem.Common.WebApi;
using Pms.Models;
using HslCommunication;

namespace ProjectManagementSystem.TaskBookEvent
{

    /// <summary>
    /// PLC交互行为码，对接地址动态扩展
    /// </summary>
    /// <typeparam name="T">泛型T</typeparam>
    public class TaskBookBehavior_PLC<T> : BaseTaskBookBehaviorV2
    {
        public PlcBehavior PlcBehavior { get; set; }

        public TaskBookBehavior_PLC(int behavior) : base(behavior, null)
        {
            PlcBehavior = ExcelConfig.Instance.PlcBehaviorList.FirstOrDefault(d => d.Behavior == behavior);
            Remark = PlcBehavior.Remark;
        }

        public bool IsLogicBitsOn(int agvId, int bitIndex)
        {
            var agvData = PmsApi.GetCarrier(agvId);
            if (agvData == null) return false;

            //判断AGV滚筒是否已经转动
            var logicBits = agvData.LogicBits;
            return (logicBits & (0x01 << bitIndex)) > 0;
        }


        public override bool TaskBookOperator(TaskData taskData, StepData stepInfo)
        {
            try
            {
                bool result = TaskBookOperator(taskData, stepInfo, out string log);
                if (result)
                {
                    //执行异步类
                    if (!string.IsNullOrEmpty(PlcBehavior.AsyncClassName)
                        && PlcBehavior.AsyncStepOffset > 0)
                    {
                        string assemblyName = this.GetType().Assembly.GetName().Name;
                        string fullClassName = $"{assemblyName}.TaskBookEvent.{PlcBehavior.AsyncClassName}";
                        var instance = Common.Function.InstanceConstructor.GetInstance<AsyncBehavior>(assemblyName, fullClassName);

                        if (instance != null)
                        {
                            instance.Behavior = stepInfo.PmsBehavior;
                            instance.Interval = 100;
                            instance.TaskID = taskData.TaskID;
                            instance.MaxStepId = taskData.CurrentStepID + PlcBehavior.AsyncStepOffset;

                            instance.Init();
                            instance.Start();
                        }
                    }
                }

                return result;
            }
            catch (Exception ex)
            {
                LogException(ex);
            }
            return false;
        }

        public bool TaskBookOperator(TaskData taskData, StepData stepInfo, out string log)
        {
            log = null;
            try
            {
                DateTime start = DateTime.Now;

                //模拟器模式下直接返回成功
                if (PlcBehavior.IsSimulate)
                {
                    log = $"【模拟器】{GetLog(taskData, stepInfo)}";
                    return true;
                }

                bool logicBit = false;
                if (PlcBehavior.LogicBitTrue > 0)
                {
                    logicBit = IsLogicBitsOn(taskData.Carrier, PlcBehavior.LogicBitTrue);
                }

                DateTime start1 = DateTime.Now;
                //写入
                if (!Interaction(0, PlcBehavior.DbAddressWrite, PlcBehavior.DbValueWriteStr, taskData, stepInfo, logicBit))
                {
                    return false;
                }
                var timeSpan1 = (DateTime.Now - start1).TotalMilliseconds;

                DateTime start2 = DateTime.Now;
                //读取
                if (!Interaction(1, PlcBehavior.DbAddressRead, PlcBehavior.DbValueReadStr, taskData, stepInfo, logicBit))
                {
                    return false;
                }
                var timeSpan2 = (DateTime.Now - start2).TotalMilliseconds;

                DateTime start3 = DateTime.Now;
                //最终写入
                if (!Interaction(0, PlcBehavior.DbAddressFinalWrite, PlcBehavior.DbValueFinalWriteStr, taskData, stepInfo, logicBit))
                {
                    return false;
                }
                var timeSpan3 = (DateTime.Now - start3).TotalMilliseconds;
                var totalTimeSpan = (DateTime.Now - start).TotalMilliseconds;

                log = $"{GetLog(taskData, stepInfo, logicBit)} 【写入{timeSpan1:F1}ms 读取{timeSpan2:F1}ms 最终写入{timeSpan3:F1}ms】总耗时{totalTimeSpan:F1}ms";
                Log(taskData, stepInfo, log);
                return true;
            }
            catch (Exception ex)
            {
                LogException(ex);
            }
            return false;
        }

        protected virtual bool Interaction(int flag, string[] addrArray, string valueArray, TaskData taskDetailInfo, StepData taskStepInfo, bool logicBit)
        {
            if (addrArray.Length <= 0) return true;

            var routeConfig = ExcelConfig.Instance.GetRouteConfig(taskStepInfo.WareHouseID);
            if (routeConfig == null)
            {
                Log(taskDetailInfo, taskStepInfo, "查找库位配置信息失败");
                return false;
            }
            if (string.IsNullOrEmpty(routeConfig.PlcIpAddr))
            {
                Log(taskDetailInfo, taskStepInfo, "PlcIp地址为空");
                return false;
            }

            var plc = routeConfig.PlcReadWrite;


            for (int i = 0; i < addrArray.Length; i++)
            {
                string addrDesc = addrArray[i];
                string plcAddr = taskStepInfo.WareHouseID.GetPlcTaskAddr(addrDesc);
                if (string.IsNullOrEmpty(plcAddr))
                {
                    Log(taskDetailInfo, taskStepInfo, $"查找地址失败：“{addrDesc}”");
                    return false;
                }

                var plcValue = GetArrayValue(valueArray, i);

                if (flag == 0)
                {
                    //根据逻辑位来确定写的值
                    if (PlcBehavior.LogicBitTrue > 0
                        && logicBit == false)
                    {
                        plcValue = default(T);
                    }

                    var result = plc.WriteValue(plcAddr, plcValue);
                    if (!result.IsSuccess)
                    {
                        Log(taskDetailInfo, taskStepInfo, $"写入失败({addrDesc}{plcAddr}写入{plcValue}) {result.Message}");
                        return false;
                    }

                    bool resultRead = plc.ReadValue(plcAddr, plcValue, out OperateResult<T> ret);
                    if (!ret.IsSuccess)
                    {
                        Log(taskDetailInfo, taskStepInfo, $"写入检查失败(读取{addrDesc}{plcAddr}失败) {ret.Message}");
                        return false;
                    }

                    if (!resultRead)
                    {
                        Log(taskDetailInfo, taskStepInfo, $"写入检查失败(读取{addrDesc}{plcAddr} != {plcValue}) {(ret.IsSuccess ? $"实际值:{ret.Content}" : ret.Message)}");
                        return false;
                    }
                }
                else
                {
                    bool resultRead = plc.ReadValue(plcAddr, plcValue, out OperateResult<T> ret);
                    if (!resultRead)
                    {
                        Log(taskDetailInfo, taskStepInfo, $"等待信号({addrDesc}{plcAddr}={plcValue}) {(ret.IsSuccess ? $"实际值:{ret.Content}" : ret.Message)}");
                        return false;
                    }
                }
            }

            return true;
        }

        private string GetLog(TaskData taskDetailInfo, StepData taskStepInfo, bool logicBit = true)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(PlcBehavior.Remark);

            sb.Append("【");
            for (int i = 0; i < PlcBehavior.DbAddressWrite.Length; i++)
            {
                string plcAddr = taskStepInfo.WareHouseID.GetPlcTaskAddr(PlcBehavior.DbAddressWrite[i]);
                var plcValue = GetArrayValue(PlcBehavior.DbValueWriteStr, i);

                if (PlcBehavior.LogicBitTrue > 0
                    && logicBit == false)
                {
                    plcValue = default(T);
                }

                sb.Append($" {plcAddr}已写{plcValue};");
            }

            for (int i = 0; i < PlcBehavior.DbAddressRead.Length; i++)
            {
                string plcAddr = taskStepInfo.WareHouseID.GetPlcTaskAddr(PlcBehavior.DbAddressRead[i]);
                var plcValue = GetArrayValue(PlcBehavior.DbValueReadStr, i);

                sb.Append($" 已等待{plcAddr}={plcValue};");
            }

            for (int i = 0; i < PlcBehavior.DbAddressFinalWrite.Length; i++)
            {
                string plcAddr = taskStepInfo.WareHouseID.GetPlcTaskAddr(PlcBehavior.DbAddressFinalWrite[i]);
                var plcValue = GetArrayValue(PlcBehavior.DbValueFinalWriteStr, i);

                if (PlcBehavior.LogicBitTrue > 0
                    && logicBit == false)
                {
                    plcValue = default(T);
                }

                sb.Append($" {plcAddr}已最终写{plcValue};");
            }
            sb.Append("】");

            return sb.ToString();
        }

        private T GetArrayValue(string str, int index)
        {
            if (string.IsNullOrEmpty(str)) return default(T);

            var array = str.ToValueArray<T>();
            if (array.Length == 0) return default(T);

            return array.Length > index ? array[index] : array[array.Length - 1];
        }
    }
}
