using BPA.Model.Enums;
using BPA.Model.Recipe;
using BPA.SingleDevice.Interface;
using BPA.SingleDevice.Json;
using BPA.SingleDevice.Services;

namespace BPA.SingleDevice.Business
{
    /// <summary>配料流程控制</summary>
    public class ProcessControl : IProcessControl
    {
        public ProcessControl(ILogService logService, GlobalData global)
        {
            this.logService = logService;
            this.global = global;
        }

        /// <summary>键就是配料设备对应的ID。这里分别为1，2，4，5。</summary>
        public ConcurrentDictionary<int, IBatchcer> Batchers { get; set; } = new();

        public ObservableCollection<RecipeData> CurrentRecipes { get; set; } = new();
        private readonly ILogService logService;
        private GlobalData global;

        public IConveyer Conveyer { get; set; } = new Conveyer();

        //RecipeData currentRecipe;

        //CancellationTokenSource cts;

        //Task runTask;
        public async Task InitalAsync()
        {
            Json<ConnectConfig>.Read();
            if (Json<ConnectConfig>.Data.BatcherConfigs.Count == 0)
            {
                InitBatcherConfig();
                Json<ConnectConfig>.Save();
            }
            if (Json<ConnectConfig>.Data.ConveyerConfigs.Count == 0)
            {
                InitConveyerConfig();
                Json<ConnectConfig>.Save();
            }
            CurrentRecipes.Clear();
            ActionRegister();

            await InitalConveyerAsync(Json<ConnectConfig>.Data.ConveyerConfigs);
            await InitalBatcherAsync(Json<ConnectConfig>.Data.BatcherConfigs);

            TaskManage.GetInstance.StartLong(() =>
            {
                InterActive();

                #region 移除配方

                for (int i = 0; i < CurrentRecipes.Count; i++)
                {
                    if (CurrentRecipes[i].IsMakeComplete.All(b => b == true))
                    {
                        var recipe = CurrentRecipes[i];
                        recipe.CompleteTime = DateTime.Now;
                        var recipeName = recipe.Name;
                        var time = recipe.CompleteTime.Subtract(recipe.StartTime);
                        var issueTime = recipe.IssueTime.ToString("HH:mm:ss");
                        App.Current.Dispatcher.Invoke(() =>
                        {
                            CurrentRecipes.RemoveAt(i);
                        });
                        global.CompletedCount++;
                        logService.LogRecipeCompleteInfo($"【{recipeName}】，下发时间【{issueTime}】,开始制作时间【{recipe.StartTime.ToString("HH:mm:ss")}】，耗时【{(int)time.TotalSeconds}】秒。");
                        logService.LogRunInfo($"配方【{recipeName}】，下发时间【{issueTime}】，开始制作时间【{recipe.StartTime.ToString("HH:mm:ss")}】-- 整体配料完成，已从配料队列移除。");
                        break;
                    }
                }

                #endregion 移除配方

                #region 配方加入队列

                if (global.RecipeQueue.Count > 0 && CanIssueRecipe())
                {
                    if (global.RecipeQueue.ElementAt(0) is not null && global.RecipeQueue.ElementAt(0) is RecipeData && CurrentRecipes.Count < 5)
                    {
                        if (global.RecipeQueue.TryDequeue(out RecipeData recipe))
                        {
                            if (recipe != null)
                            {
                                recipe.StartTime = DateTime.Now;
                                App.Current.Dispatcher.Invoke(() =>
                                {
                                    CurrentRecipes.Add(recipe);
                                });
                                logService.LogRunInfo($"配方【{recipe.Name}】-- 开始执行配料。");
                            }
                        }
                    }
                }

                #endregion 配方加入队列

                Batching();

                RefreshData();
            }, "MonitorRecipeIssue", true);
        }

        /// <summary>显示数据的赋值。</summary>
        private void RefreshData()
        {
            global.IsCompleted[0] = Batchers[1].BatchComplete;
            global.IsCompleted[1] = Batchers[2].BatchComplete;
            global.IsCompleted[2] = Batchers[4].BatchComplete;
            global.IsCompleted[3] = Batchers[5].BatchComplete;
            for (int i = 0; i < Conveyer.HaveVessel.Length; i++)
            {
                global.HaveVessel[i] = Conveyer.HaveVessel[i];
            }
            global.ConveyerMoveComplete = Conveyer.MoveComplete;
        }

        /// <summary>单工位配料</summary>
        /// <param name="recipe">配方</param>
        /// <param name="stationNum">工位数。</param>
        private void StationBatching(RecipeData recipe, int stationNum)
        {
            #region 数据验证

            //如果配料机里没连接该工位的配料机，则直接完成。
            if (!Batchers.ContainsKey(stationNum))
            {
                recipe.BatchStatus[stationNum] = BatchStep.BatchCompleted;
                recipe.IsMakeComplete[stationNum - 1] = true;
                logService.LogRunInfo($"参数【工位】值为[{stationNum}],目前该工位无配料机或连接配料机失败。");
                return;
            }

            #endregion 数据验证

            //数组起始索引是0，工位起始ID是1。
            if (Conveyer.HaveVessel[stationNum - 1])
            {
                ushort[] materialList = new ushort[14];
                //获取工位需要的配料数据。
                if (recipe.MaterialData.ContainsKey(stationNum))
                {
                    materialList = recipe.MaterialData[stationNum];
                }
                //如果该工位不需要配料，直接完成就行。
                else
                {
                    recipe.BatchStatus[stationNum] = BatchStep.BatchCompleted;
                    recipe.IsMakeComplete[stationNum - 1] = true;
                    logService.LogRunInfo($"参数<stationNum>值为[{stationNum}],该配方无该工位配料需求。");
                    return;
                }

                //配料机配料完成的上升沿检测。
                var completeTrig = RTrig.GetInstance($"Batchers[{stationNum}].BatchComplete").Start(Batchers[stationNum].BatchComplete);

                //配料流程。
                switch (recipe.BatchStatus[stationNum])
                {
                    case BatchStep.WaitBatch:
                        if (Batchers[stationNum].ResetCompleted())
                        {
                            recipe.BatchStatus[stationNum] = BatchStep.WriteBatchParam;
                            logService.LogRunInfo($"配方【{recipe.Name}】工位【{stationNum}】的配料完成信号已复位。");
                        }
                        break;

                    case BatchStep.WriteBatchParam:
                        if (materialList is not null && materialList.Length == 14)
                        {
                            if (Batchers[stationNum].WriteBatchData(materialList))
                            {
                                recipe.BatchStatus[stationNum] = BatchStep.StartBatch;
                                logService.LogRunInfo($"配方【{recipe.Name}】写入工位【{stationNum}】的下料重量【{String.Join('，', materialList)}】成功。");
                            }
                            else
                            {
                                logService.LogRunInfo($"配方【{recipe.Name}】写入工位【{stationNum}】的下料参数失败，稍后重试。");
                                Task.Delay(3000).Wait();
                            }
                        }
                        else
                        {
                            logService.LogRunInfo($"配方【{recipe.Name}】写入工位【{stationNum}】的下料参数失败，参数长度错误或为Null。");
                            return;
                        }
                        break;

                    case BatchStep.StartBatch:
                        Task.Delay(500).Wait();
                        if (Batchers[stationNum].StartBatching())
                        {
                            recipe.BatchStatus[stationNum] = BatchStep.WaitBatchComplete;
                            logService.LogRunInfo($"配方【{recipe.Name}】工位【{stationNum}】开始配料，等待配料完成信号上升沿。");
                        }
                        else
                        {
                            logService.LogRunInfo($"配方【{recipe.Name}】写入工位【{stationNum}】开始配料失败，3S后重试。");
                            Task.Delay(3000).Wait();
                        }
                        break;

                    case BatchStep.WaitBatchComplete:
                        Task.Delay(500).Wait();
                        if (completeTrig)
                        {
                            logService.LogRunInfo($"配方【{recipe.Name}】工位【{stationNum}】的配料完成。");
                            recipe.BatchStatus[stationNum] = BatchStep.BatchCompleted;
                        }
                        break;

                    case BatchStep.BatchCompleted:
                            recipe.IsMakeComplete[stationNum - 1] = true;
                        break;
                }
            }
            else
            {
                logService.LogRunInfo($"配方【{recipe.Name}】工位【{stationNum}】未检测到容器到位，请检查，3秒后重新检测。");
                Task.Delay(3000).Wait();
            }
        }

        /// <summary>移动传送带。</summary>
        /// <returns></returns>
        private void MoveConveyer()
        {
            //global.MoveConveyerStep = MoveConveyerStep.WaitMove;
            //while (global.MoveConveyerStep != MoveConveyerStep.MoveComplete)
            //{
            var moveCompleteTrig = RTrig.GetInstance("MoveCompleted").Start(Conveyer.MoveComplete);
            switch (global.MoveConveyerStep)
            {
                case MoveConveyerStep.WaitMove:
                    var delayTime = Json<ConnectConfig>.Data.MoveDelay;
                    Task.Delay(TimeSpan.FromSeconds(delayTime)).Wait();
                    Conveyer.InitalMoveParam();
                    Task.Delay(500).Wait();
                    if (Conveyer.MoveOnce())
                    {
                        logService.LogRunInfo($"控制传送带去下个工位，等待动作完成信号上升沿。");
                        Task.Delay(500).Wait();
                        global.MoveConveyerStep = MoveConveyerStep.Moveing;
                    }
                    else
                    {
                        logService.LogRunInfo($"控制传送带去下个工位失败，可能连接异常或不允许移动，3S后重试。");
                        Task.Delay(3000).Wait();
                    }
                    break;

                case MoveConveyerStep.Moveing:
                    if (moveCompleteTrig)
                    {
                        Conveyer.InitalMoveParam();
                        logService.LogRunInfo($"控制传送带移动结束，复位传送带移动指令和移动结束信号，开始更新配方工位。");
                        global.MoveConveyerStep = MoveConveyerStep.MoveComplete;
                    }
                    break;

                case MoveConveyerStep.MoveComplete:
                    UpdateRecipe();
                    global.MoveConveyerStep = MoveConveyerStep.WaitMove;
                    break;
            }
            //}
        }

        /// <summary>设置相应设备ID的配料机的通讯参数。</summary>
        /// <param name="id">该设备对应的工位ID。</param>
        /// <param name="ip">IP地址</param>
        /// <param name="port">端口号，默认为502。</param>
        private void SetBatcherComm(int id, string ip, int port = 502)
        {
            if (Batchers.ContainsKey(id))
            {
                Batchers[id].SetCommParam(id, ip, port);
            }
            else
            {
                logService.LogDebugInfo($"设置配料机设备【{id}】的通讯参数失败，未在集合中找到此ID对应的设备。");
            }
        }

        private void ActionRegister()
        {
            ActionManage.GetInstance.Register<bool>(new Func<bool>(() =>
            {
                return CanIssueRecipe();
            }), "CanIssueRecipe", true);
        }

        /// <summary>是否允许下发配方。</summary>
        /// <returns></returns>
        private bool CanIssueRecipe()
        {
            foreach (var item in CurrentRecipes)
            {
                //如果在配方队列里，有配方的当前工位是1，则不允许下发新的配方。
                if (item.CurrentStation == 1)
                {
                    return false;
                }
            }
            return global.MoveConveyerStep == MoveConveyerStep.WaitMove;
        }

        /// <summary>传送带和配料机之间的信号交互。</summary>
        private void InterActive()
        {
            #region 配料机

            foreach (var batcher in Batchers.Values)
            {
                batcher.AllowBatching = Conveyer.HaveVessel[batcher.ID - 1];
            }

            #endregion 配料机

            #region 传送带

            Conveyer.AllowMove = GetBatcherAllowMove();

            #endregion 传送带
        }

        /// <summary>获取传送带的运行许可,仅通过配方的完成状态来判断。</summary>
        /// <returns></returns>
        private bool GetBatcherAllowMove()
        {
            foreach (var recipe in CurrentRecipes)
            {
                foreach (var item in recipe.BatchStatus.Values)
                {
                    switch (item)
                    {
                        case BatchStep.WaitBatch:
                        case BatchStep.BatchCompleted:
                            break;

                        default:
                            return false;
                    }
                }
            }
            return true;
        }

        /// <summary>2023.10.14:新增，所有在队列的配方，必须等当前工位的配料完成后才可以移动,否则不允许移动。</summary>
        /// <returns></returns>
        private bool IsAllowConveyerMove()
        {
            foreach (var item in CurrentRecipes)
            {
                if (item.IsMakeComplete[item.CurrentStation - 1] == false)
                {
                    return false;
                }
            }
            return true;
        }

        private void SingleDetect()
        {
            //TODO:上升沿信号检测。
        }

        /// <summary>初始化传送带配置。</summary>
        private void InitConveyerConfig()
        {
            ConveyerConfig conveyerConfig = new() { IP = "192.168.6.104", Port = 502, ID = 1, IsConnect = true };
            Json<ConnectConfig>.Data.ConveyerConfigs.Add(conveyerConfig);
        }

        /// <summary>初始化配料机配置。</summary>
        private void InitBatcherConfig()
        {
            for (int i = 1; i < 6; i++)
            {
                BatcherConfig batcherConfig = new() { StationID = i, IP = $"192.168.6.10{i - 1}", Port = 502, IsConnect = (i == 3 ? false : true) };
                Json<ConnectConfig>.Data.BatcherConfigs.Add(batcherConfig);
            }
        }

        /// <summary>初始化配料机。</summary>
        /// <param name="configs"></param>
        private async Task InitalBatcherAsync(IList<BatcherConfig> configs)
        {
            if (configs is null || configs.Count == 0)
            {
                throw new ArgumentNullException("配置数据为Null");
            }
            List<Task> tasks = new();
            foreach (var item in configs)
            {
                if (item.IsConnect)
                {
                    Batchers.TryAdd(item.StationID, new Batcher());
                    SetBatcherComm(item.StationID, item.IP, item.Port);
                    tasks.Add(Batchers[item.StationID].InitialAsync());
                }
            }
        }
        private async Task InitalConveyerAsync(IList<ConveyerConfig> configs)
        {
            if (configs is null || configs.Count == 0)
            {
                throw new ArgumentNullException("配置数据为Null");
            }
            var item = configs.ElementAt(0);
            if (item is not null && item.IsConnect)
            {
                Conveyer.SetCommParam(item.ID, item.IP, item.Port);
                await Conveyer.InitialAsync();
            }

        }
        /// <summary>刷新配方的当前工站。</summary>
        private void UpdateRecipe()
        {
            foreach (var item in CurrentRecipes)
            {
                if (item.CurrentStation < 5)
                {
                    while (!Conveyer.HaveVessel[item.CurrentStation])
                    {
                        logService.LogRunInfo($"更新工位时，未检测到配方【{item.Name}】在工位【{item.CurrentStation + 1}】的容器到位信号，请检查。");
                        Task.Delay(3000).Wait();
                    }
                    item.CurrentStation++;
                }
            }
        }
        /// <summary>刷新配方的当前工站。</summary>
        private bool UpdateRecipeCurrentSataion()
        {
            bool result = true;
            foreach (var item in CurrentRecipes)
            {
                if (item.CurrentStation < 5 && item.IsMakeComplete[item.CurrentStation - 1]==true)
                {
                    if (Conveyer.HaveVessel[item.CurrentStation])
                    {
                        item.CurrentStation++;
                    }
                    else
                    {
                        logService.LogRunInfo($"更新配方当前工位时，未检测到配方【{item.Name}】在工位【{item.CurrentStation + 1}】的容器到位信号，请检查。");
                        result = false;
                    }
                }
            }
            return result;
        }

        /// <summary>适用于多配方的配料。</summary>
        private void Batching()
        {
            if (CurrentRecipes.Count > 0 && IsAllowConveyerMove())
            {
                MoveConveyer();
            }
            else if (global.MoveConveyerStep == MoveConveyerStep.WaitMove)
            {
                foreach (var item in CurrentRecipes)
                {
                    if (!item.IsMakeComplete[item.CurrentStation - 1])
                    {
                        StationBatching(item, item.CurrentStation);
                    }
                }
            }
        }
    }
}