using MainShell.Common;
using MainShell.Filewritable;
using MainShell.Log;
using MainShell.Models;
using MainShell.Models.Wafer;
using MainShell.Parameter;
using MainShell.ProcessResult;
using MainShell.ParaSetting.Model;
using MainShell.Recipe.Models;
using MainShell.Vision;
using MaxwellFramework.Core.Attributes;
using MXJM.FileWritable;
using MW.WorkFlow;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Windows;

using Jm1RecheckDiePoint = JM1.JM1Params.RecheckDiePoint;

namespace MainShell.Process
{
    public class DiePositionService
    {
        private sealed class PipelineFailureCoordinator
        {
            private readonly object _syncRoot = new object();
            private readonly CancellationTokenSource _pipelineCancellationTokenSource;
            private readonly ChannelWriter<WaferCaptureFrame> _frameWriter;
            private ExceptionDispatchInfo _failureDispatchInfo;

            public PipelineFailureCoordinator(CancellationTokenSource pipelineCancellationTokenSource, ChannelWriter<WaferCaptureFrame> frameWriter)
            {
                _pipelineCancellationTokenSource = pipelineCancellationTokenSource ?? throw new ArgumentNullException(nameof(pipelineCancellationTokenSource));
                _frameWriter = frameWriter ?? throw new ArgumentNullException(nameof(frameWriter));
            }

            public bool HasFailure
            {
                get
                {
                    lock (_syncRoot)
                    {
                        return _failureDispatchInfo != null;
                    }
                }
            }

            public void TrySet(Exception exception)
            {
                if (exception == null)
                {
                    throw new ArgumentNullException(nameof(exception));
                }

                bool shouldCancelPipeline = false;
                lock (_syncRoot)
                {
                    if (_failureDispatchInfo == null)
                    {
                        _failureDispatchInfo = ExceptionDispatchInfo.Capture(exception);
                        shouldCancelPipeline = true;
                    }
                }

                if (!shouldCancelPipeline)
                {
                    return;
                }

                _frameWriter.TryComplete(exception);
                if (!_pipelineCancellationTokenSource.IsCancellationRequested)
                {
                    _pipelineCancellationTokenSource.Cancel();
                }
            }

            public void ThrowIfFailed()
            {
                ExceptionDispatchInfo failureDispatchInfo = null;
                lock (_syncRoot)
                {
                    failureDispatchInfo = _failureDispatchInfo;
                }

                if (failureDispatchInfo != null)
                {
                    failureDispatchInfo.Throw();
                }
            }
        }

        private const int DefaultRecognitionConsumerCount = 2;
        private const int DefaultFrameChannelCapacity = 8;
        private const double DefaultMapAngleThreshold = 3.0d;
        private const double DefaultMapRotateAngle = 0.0d;
        private const double DefaultMapDistanceThreshold = 0.01d;

        private readonly IWaferScanPlanner _waferScanPlanner;
        private readonly IWaferMachineAdapter _waferMachineAdapter;
        private readonly IWaferCaptureExecutor _waferCaptureExecutor;
        private readonly IWaferDieRecognizer _waferDieRecognizer;
        private readonly ICommonVisionAlgorithmService _commonVisionAlgorithmService;
        private readonly IWaferRecognitionResultCollector _waferRecognitionResultCollector;
        private readonly IWaferDiePositionResultService _waferDiePositionResultService;
        private readonly GlobalParameterContext _globalParameterContext;
        private readonly FileWriteQueue _fileWriteQueue;

        public DiePositionService(
            IWaferScanPlanner waferScanPlanner,
            IWaferMachineAdapter waferMachineAdapter,
            IWaferCaptureExecutor waferCaptureExecutor,
            IWaferDieRecognizer waferDieRecognizer,
            ICommonVisionAlgorithmService commonVisionAlgorithmService,
            IWaferRecognitionResultCollector waferRecognitionResultCollector,
            IWaferDiePositionResultService waferDiePositionResultService,
            GlobalParameterContext globalParameterContext,
            FileWriteQueue fileWriteQueue)
        {
            _waferScanPlanner = waferScanPlanner ?? throw new ArgumentNullException(nameof(waferScanPlanner));
            _waferMachineAdapter = waferMachineAdapter ?? throw new ArgumentNullException(nameof(waferMachineAdapter));
            _waferCaptureExecutor = waferCaptureExecutor ?? throw new ArgumentNullException(nameof(waferCaptureExecutor));
            _waferDieRecognizer = waferDieRecognizer ?? throw new ArgumentNullException(nameof(waferDieRecognizer));
            _commonVisionAlgorithmService = commonVisionAlgorithmService ?? throw new ArgumentNullException(nameof(commonVisionAlgorithmService));
            _waferRecognitionResultCollector = waferRecognitionResultCollector ?? throw new ArgumentNullException(nameof(waferRecognitionResultCollector));
            _waferDiePositionResultService = waferDiePositionResultService ?? throw new ArgumentNullException(nameof(waferDiePositionResultService));
            _globalParameterContext = globalParameterContext ?? throw new ArgumentNullException(nameof(globalParameterContext));
            _fileWriteQueue = fileWriteQueue ?? throw new ArgumentNullException(nameof(fileWriteQueue));
        }

        public async Task ExecuteAsync(WorkflowContext context, ActivityControl activityControl)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (activityControl == null)
            {
                throw new ArgumentNullException(nameof(activityControl));
            }

            RecipeManager recipeManager = context.GetData<RecipeManager>(WorkflowContextKeys.RecipeManager);
            ProcessResultManager processResultManager = context.GetData<ProcessResultManager>(WorkflowContextKeys.ProcessResultManager);
            DiePositionProcessResult processResult = InitializeProcessResult(processResultManager);

            try
            {
                await CheckCancellationAndPauseAsync(activityControl).ConfigureAwait(false);
                _waferRecognitionResultCollector.Reset();

                WaferDiePositionContext runtimeContext = BuildRuntimeContext(recipeManager, context, activityControl);
                runtimeContext.MapAngleThreshold = ResolveMapAngleThreshold(_globalParameterContext);
                runtimeContext.MapRotateAngle = ResolveMapRotateAngle(processResultManager);
                runtimeContext.MapDistanceThreshold = ResolveDistanceThreshold();
                WaferScanPlan scanPlan = CreateScanPlan(runtimeContext);
                runtimeContext.ScanPlan = scanPlan;
                runtimeContext.ExecutionSummary = await ExecuteScanWithRecognitionPipelineAsync(runtimeContext).ConfigureAwait(false);
                WaferRecognitionSnapshot recognitionSnapshot = CollectSnapshot();
                _waferDiePositionResultService.ApplySnapshot(runtimeContext, recognitionSnapshot);
                await GenerateWaferMapAsync(runtimeContext, recognitionSnapshot).ConfigureAwait(false);

                PopulateProcessResult(processResult, runtimeContext, scanPlan);
                _waferDiePositionResultService.WriteProcessResult(runtimeContext, processResult);
                processResult.IsSuccess = true;
                processResult.ErrorMessage = null;
                processResult.ErrorMessageKey = MessageKey.None;
                processResult.ErrorMessageArguments = Array.Empty<string>();

                context.SetData(WorkflowContextKeys.DiePositionContext, runtimeContext);
                context.SetData(WorkflowContextKeys.DiePositionScanPlan, scanPlan);

                LogManager.LogProcessInfo(
                    string.Format(
                        "晶圆定位：扫描执行完成。配方={0}，行数={1}，列数={2}，扫描点数={3}，采图帧数={4}，识别成功数={5}，识别失败数={6}。",
                        processResult.RecipeName,
                        processResult.RowCount,
                        processResult.ColumnCount,
                        processResult.ScanPointCount,
                        runtimeContext.ExecutionSummary == null ? 0 : runtimeContext.ExecutionSummary.ProducedFrameCount,
                        runtimeContext.ExecutionSummary == null ? 0 : runtimeContext.ExecutionSummary.RecognitionSuccessCount,
                        runtimeContext.ExecutionSummary == null ? 0 : runtimeContext.ExecutionSummary.RecognitionFailureCount));
            }
            catch (OperationCanceledException)
            {
                processResult.IsSuccess = false;
                processResult.ErrorMessage = null;
                processResult.ErrorMessageKey = MessageKey.None;
                processResult.ErrorMessageArguments = Array.Empty<string>();
                LogManager.LogProcessInfo("晶圆定位：流程已取消。");
                throw;
            }
            catch (LocalizedProcessException ex)
            {
                processResult.IsSuccess = false;
                processResult.ErrorMessageKey = ex.FailureMessageKey;
                processResult.ErrorMessageArguments = ex.FailureMessageArguments;
                processResult.ErrorMessage = ex.Message;
                LogManager.LogProcessError(string.Format("晶圆定位：{0}", ex.Message));
            }
            catch (Exception ex)
            {
                processResult.IsSuccess = false;
                processResult.ErrorMessageKey = MessageKey.ProcessDiePositionFailedWithReason;
                processResult.ErrorMessageArguments = new string[] { ex.Message ?? string.Empty };
                processResult.ErrorMessage = LanguageResourceHelper.Format(
                    processResult.ErrorMessageKey,
                    LocalizedProcessException.ConvertArguments(processResult.ErrorMessageArguments));
                LogManager.LogProcessError(string.Format("晶圆定位：{0}", processResult.ErrorMessage));
            }
            finally
            {
                WriteResultToContext(context, processResultManager, processResult);
            }
        }

        private static WaferDiePositionContext BuildRuntimeContext(RecipeManager recipeManager, WorkflowContext context, ActivityControl activityControl)
        {
            WaferRecipe waferRecipe = GetCurrentWaferRecipe(recipeManager);
            WaferInfo waferInfo = GetWaferInfo(waferRecipe);

            WaferDiePositionContext runtimeContext = new WaferDiePositionContext();
            runtimeContext.WorkflowName = context.GetData<string>(WorkflowContextKeys.WorkflowName);
            runtimeContext.RecipeName = waferRecipe.RecipeName;
            runtimeContext.WaferRecipe = waferRecipe;
            runtimeContext.ScanSettings = waferRecipe.ScanSettings ?? new WaferScanSettings();
            runtimeContext.RowCount = waferInfo.WaferRowNum;
            runtimeContext.ColumnCount = waferInfo.WaferColNum;
            runtimeContext.PitchX = waferInfo.DiePitchX;
            runtimeContext.PitchY = waferInfo.DiePitchY;
            runtimeContext.CancellationToken = activityControl.CancellationToken;
            return runtimeContext;
        }

        private WaferScanPlan CreateScanPlan(WaferDiePositionContext runtimeContext)
        {
            return _waferScanPlanner.CreatePlan(runtimeContext);
        }

        private async Task<WaferScanExecutionSummary> ExecuteScanWithRecognitionPipelineAsync(WaferDiePositionContext runtimeContext)
        {
            if (runtimeContext == null)
            {
                throw new ArgumentNullException(nameof(runtimeContext));
            }

            CancellationToken originalCancellationToken = runtimeContext.CancellationToken;
            CancellationTokenSource pipelineCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(originalCancellationToken);
            try
            {
                runtimeContext.CancellationToken = pipelineCancellationTokenSource.Token;
                EnsureFrameResults(runtimeContext);

                int consumerCount = ResolveRecognitionConsumerCount(runtimeContext);
                Channel<WaferCaptureFrame> frameChannel = CreateFrameChannel(runtimeContext, consumerCount);
                PipelineFailureCoordinator failureCoordinator = new PipelineFailureCoordinator(pipelineCancellationTokenSource, frameChannel.Writer);
                List<WaferRecognitionFrameResult> frameResults = new List<WaferRecognitionFrameResult>();
                object frameResultsSyncRoot = new object();
                List<Task> consumerTasks = StartRecognitionConsumers(frameChannel.Reader, runtimeContext, frameResults, frameResultsSyncRoot, consumerCount, failureCoordinator);

                LogManager.LogProcessInfo($"晶圆定位：启动流式识别处理链。消费者数量={consumerCount}。");

                WaferScanExecutionSummary summary = null;
                try
                {
                    summary = await _waferCaptureExecutor.ExecuteAsync(runtimeContext, frameChannel.Writer, runtimeContext.CancellationToken).ConfigureAwait(false);
                }
                catch (OperationCanceledException) when (failureCoordinator.HasFailure)
                {
                }
                catch (Exception ex)
                {
                    failureCoordinator.TrySet(ex);
                }
                finally
                {
                    frameChannel.Writer.TryComplete();
                }

                try
                {
                    await Task.WhenAll(consumerTasks).ConfigureAwait(false);
                }
                catch (OperationCanceledException) when (failureCoordinator.HasFailure)
                {
                }
                catch (Exception ex)
                {
                    failureCoordinator.TrySet(ex);
                }

                failureCoordinator.ThrowIfFailed();
                runtimeContext.CancellationToken.ThrowIfCancellationRequested();
                ApplyFrameResults(runtimeContext, frameResults, summary);
                return summary;
            }
            finally
            {
                runtimeContext.CancellationToken = originalCancellationToken;
                pipelineCancellationTokenSource.Dispose();
            }
        }

        private static Channel<WaferCaptureFrame> CreateFrameChannel(WaferDiePositionContext runtimeContext, int consumerCount)
        {
            if (runtimeContext == null)
            {
                throw new ArgumentNullException(nameof(runtimeContext));
            }

            int capacity = ResolveFrameChannelCapacity(runtimeContext, consumerCount);
            BoundedChannelOptions options = new BoundedChannelOptions(capacity);
            options.SingleWriter = true;
            options.SingleReader = false;
            options.AllowSynchronousContinuations = false;
            options.FullMode = BoundedChannelFullMode.Wait;
            return Channel.CreateBounded<WaferCaptureFrame>(options);
        }

        private List<Task> StartRecognitionConsumers(
            ChannelReader<WaferCaptureFrame> frameReader,
            WaferDiePositionContext runtimeContext,
            List<WaferRecognitionFrameResult> frameResults,
            object frameResultsSyncRoot,
            int consumerCount,
            PipelineFailureCoordinator failureCoordinator)
        {
            if (frameReader == null)
            {
                throw new ArgumentNullException(nameof(frameReader));
            }

            if (runtimeContext == null)
            {
                throw new ArgumentNullException(nameof(runtimeContext));
            }

            List<Task> consumerTasks = new List<Task>();
            for (int consumerIndex = 0; consumerIndex < consumerCount; consumerIndex++)
            {
                consumerTasks.Add(ProcessQueuedFramesAsync(frameReader, runtimeContext, frameResults, frameResultsSyncRoot, consumerIndex + 1, failureCoordinator));
            }

            return consumerTasks;
        }

        private void ApplyFrameResults(
            WaferDiePositionContext runtimeContext,
            List<WaferRecognitionFrameResult> frameResults,
            WaferScanExecutionSummary summary)
        {
            if (runtimeContext == null)
            {
                throw new ArgumentNullException(nameof(runtimeContext));
            }

            if (frameResults == null)
            {
                throw new ArgumentNullException(nameof(frameResults));
            }

            List<WaferRecognitionFrameResult> orderedResults = frameResults
                .OrderBy(result => result == null ? long.MaxValue : result.SequenceId)
                .ToList();

            runtimeContext.FrameResults.Clear();
            foreach (WaferRecognitionFrameResult frameResult in orderedResults)
            {
                runtimeContext.FrameResults.Add(frameResult);
            }

            if (summary != null)
            {
                summary.ConsumedFrameCount = orderedResults.Count;
                summary.RecognitionSuccessCount = orderedResults.Count(result => result != null && result.IsSuccess);
                summary.RecognitionFailureCount = orderedResults.Count(result => result == null || !result.IsSuccess);
            }

            LogManager.LogProcessInfo($"晶圆定位：识别处理链完成。结果数={orderedResults.Count}，成功数={(summary == null ? 0 : summary.RecognitionSuccessCount)}，失败数={(summary == null ? 0 : summary.RecognitionFailureCount)}。");
        }

        private async Task ProcessQueuedFramesAsync(
            ChannelReader<WaferCaptureFrame> frameReader,
            WaferDiePositionContext runtimeContext,
            List<WaferRecognitionFrameResult> frameResults,
            object frameResultsSyncRoot,
            int consumerId,
            PipelineFailureCoordinator failureCoordinator)
        {
            if (frameReader == null)
            {
                throw new ArgumentNullException(nameof(frameReader));
            }

            if (runtimeContext == null)
            {
                throw new ArgumentNullException(nameof(runtimeContext));
            }

            if (frameResults == null)
            {
                throw new ArgumentNullException(nameof(frameResults));
            }

            if (frameResultsSyncRoot == null)
            {
                throw new ArgumentNullException(nameof(frameResultsSyncRoot));
            }

            if (failureCoordinator == null)
            {
                throw new ArgumentNullException(nameof(failureCoordinator));
            }

            LogManager.LogProcessInfo($"晶圆定位：识别消费者 {consumerId} 已启动。");

            try
            {
                while (await frameReader.WaitToReadAsync(runtimeContext.CancellationToken).ConfigureAwait(false))
                {
                    WaferCaptureFrame captureFrame = null;
                    while (frameReader.TryRead(out captureFrame))
                    {
                        runtimeContext.CancellationToken.ThrowIfCancellationRequested();

                        WaferRecognitionFrameResult frameResult = await RecognizeFrameAsync(captureFrame, runtimeContext).ConfigureAwait(false);
                        _waferRecognitionResultCollector.Add(frameResult);

                        lock (frameResultsSyncRoot)
                        {
                            frameResults.Add(frameResult);
                        }
                    }
                }
            }
            catch (OperationCanceledException) when (failureCoordinator.HasFailure)
            {
            }
            catch (Exception ex)
            {
                failureCoordinator.TrySet(ex);
                throw;
            }
            finally
            {
                LogManager.LogProcessInfo($"晶圆定位：识别消费者 {consumerId} 已停止。");
            }
        }

        private async Task<WaferRecognitionFrameResult> RecognizeFrameAsync(WaferCaptureFrame captureFrame, WaferDiePositionContext runtimeContext)
        {
            if (captureFrame == null)
            {
                throw new InvalidOperationException("DiePosition: capture frame is null.");
            }

            try
            {
                return await _waferDieRecognizer.RecognizeAsync(captureFrame, runtimeContext, runtimeContext.CancellationToken).ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                LogManager.LogProcessError($"晶圆定位：帧识别失败。序号={captureFrame.SequenceId}，详情={ex.Message}");
                throw;
            }
        }

        private WaferRecognitionSnapshot CollectSnapshot()
        {
            return _waferRecognitionResultCollector.CreateSnapshot();
        }

        private async Task GenerateWaferMapAsync(WaferDiePositionContext runtimeContext, WaferRecognitionSnapshot recognitionSnapshot)
        {
            if (runtimeContext == null)
            {
                throw new ArgumentNullException(nameof(runtimeContext));
            }

            if (recognitionSnapshot == null)
            {
                throw new ArgumentNullException(nameof(recognitionSnapshot));
            }

            InitializeMapResults(runtimeContext);
            QueueWaferMapFile(recognitionSnapshot);

            await ClearWaferMapAsync(runtimeContext.CancellationToken).ConfigureAwait(false);

            VisionProcessResult generateMapResult = await _commonVisionAlgorithmService.GenWaferMapXAsync(
                ResolveWaferMapDirectory(),
                recognitionSnapshot.OverlapRows,
                recognitionSnapshot.OverlapColumns,
                recognitionSnapshot.OverlapAngles,
                recognitionSnapshot.RowsReal,
                recognitionSnapshot.ColumnsReal,
                recognitionSnapshot.Angles,
                DefaultMapAngleThreshold,
                ResolveRowDistance(runtimeContext),
                ResolveColumnDistance(runtimeContext),
                runtimeContext.RowCount,
                runtimeContext.ColumnCount,
                0d,
                0d,
                runtimeContext.MapDistanceThreshold,
                runtimeContext.MapAngleThreshold,
                runtimeContext.MapRotateAngle,
                runtimeContext.CancellationToken).ConfigureAwait(false);
            EnsureVisionSucceeded(generateMapResult, "生成晶圆图");

            VisionProcessResult<(RecheckDieMap UpMap, List<Jm1RecheckDiePoint> PatchPos, List<Jm1RecheckDiePoint> ExceptPos)> getMapResult = await _commonVisionAlgorithmService.GetMapAsync(runtimeContext.CancellationToken).ConfigureAwait(false);
            EnsureVisionSucceeded(getMapResult, "读取晶圆图");

            runtimeContext.MapDies = ConvertMapToDies(getMapResult.Data.UpMap, runtimeContext.RowCount, runtimeContext.ColumnCount);
            runtimeContext.PatchDies = ConvertPatchDies(getMapResult.Data.PatchPos, runtimeContext.RowCount, runtimeContext.ColumnCount);
            runtimeContext.ExceptDies = ConvertExceptDies(getMapResult.Data.ExceptPos, runtimeContext.RowCount, runtimeContext.ColumnCount);

            LogManager.LogProcessInfo($"晶圆定位：晶圆图生成完成。补点数={runtimeContext.PatchDies.Count}，异常点数={runtimeContext.ExceptDies.Count}。");
        }

        private static void InitializeMapResults(WaferDiePositionContext runtimeContext)
        {
            runtimeContext.MapDies = new Die[Math.Max(0, runtimeContext.RowCount), Math.Max(0, runtimeContext.ColumnCount)];
            runtimeContext.PatchDies = new List<Die>();
            runtimeContext.ExceptDies = new List<Die>();
        }

        private void QueueWaferMapFile(WaferRecognitionSnapshot recognitionSnapshot)
        {
            if (recognitionSnapshot == null)
            {
                throw new ArgumentNullException(nameof(recognitionSnapshot));
            }

            WaferMapFile waferMapFile = CreateWaferMapFile(recognitionSnapshot);
            bool enqueueSucceeded = _fileWriteQueue.Enqueue(waferMapFile);
            if (!enqueueSucceeded)
            {
                throw new InvalidOperationException("晶圆定位：识别点文件入队保存失败。");
            }
        }

        private async Task ClearWaferMapAsync(CancellationToken cancellationToken)
        {
            int[] clearMapFlags = new int[] { 1, 2, 3 };
            for (int index = 0; index < clearMapFlags.Length; index++)
            {
                int clearMapFlag = clearMapFlags[index];
                VisionProcessResult clearMapResult = await _commonVisionAlgorithmService.ClearMapAsync(clearMapFlag, cancellationToken).ConfigureAwait(false);
                EnsureVisionSucceeded(clearMapResult, $"清空晶圆图，标志={clearMapFlag}");
            }
        }

        private static WaferMapFile CreateWaferMapFile(WaferRecognitionSnapshot recognitionSnapshot)
        {
            WaferMapFile waferMapFile = new WaferMapFile();
            waferMapFile.UserData = Tuple.Create(
                recognitionSnapshot.RowsReal ?? Array.Empty<double>(),
                recognitionSnapshot.ColumnsReal ?? Array.Empty<double>(),
                recognitionSnapshot.Angles ?? Array.Empty<double>(),
                recognitionSnapshot.OverlapRows ?? Array.Empty<double>(),
                recognitionSnapshot.OverlapColumns ?? Array.Empty<double>(),
                recognitionSnapshot.OverlapAngles ?? Array.Empty<double>());
            return waferMapFile;
        }

        private static string ResolveWaferMapDirectory()
        {
            return Path.GetDirectoryName(Paths.WaferFileNames["Row"]);
        }

        private static double ResolveRowDistance(WaferDiePositionContext runtimeContext)
        {
            if (runtimeContext.WaferRecipe != null &&
                runtimeContext.WaferRecipe.WaferInfo != null &&
                runtimeContext.WaferRecipe.WaferInfo.DiePitchY > 0d)
            {
                return runtimeContext.WaferRecipe.WaferInfo.DiePitchY;
            }

            return runtimeContext.PitchY;
        }

        private static double ResolveColumnDistance(WaferDiePositionContext runtimeContext)
        {
            if (runtimeContext.WaferRecipe != null &&
                runtimeContext.WaferRecipe.WaferInfo != null &&
                runtimeContext.WaferRecipe.WaferInfo.DiePitchX > 0d)
            {
                return runtimeContext.WaferRecipe.WaferInfo.DiePitchX;
            }

            return runtimeContext.PitchX;
        }

        private static double ResolveDistanceThreshold()
        {
            return DefaultMapDistanceThreshold;
        }

        private static double ResolveMapAngleThreshold(GlobalParameterContext globalParameterContext)
        {
            if (globalParameterContext != null &&
                globalParameterContext.RunSetting != null &&
                globalParameterContext.RunSetting.DieRecognizeProcessParameter != null)
            {
                return globalParameterContext.RunSetting.DieRecognizeProcessParameter.DieAngleThreshold;
            }

            return DefaultMapAngleThreshold;
        }

        private static double ResolveMapRotateAngle(ProcessResultManager processResultManager)
        {
            if (processResultManager == null || processResultManager.SubstratePositionResult == null)
            {
                return DefaultMapRotateAngle;
            }

            return processResultManager.SubstratePositionResult.SubstrateAngle * -1d;
        }

        private static void EnsureVisionSucceeded(VisionProcessResult result, string operationName)
        {
            if (result != null && result.Succeeded)
            {
                return;
            }

            string message = result == null || string.IsNullOrWhiteSpace(result.Message)
                ? $"晶圆定位：{operationName}失败。"
                : result.Message;
            throw new InvalidOperationException(message, result == null ? null : result.Exception);
        }

        private static Die[,] ConvertMapToDies(RecheckDieMap map, int rowCount, int columnCount)
        {
            Die[,] mapDies = new Die[Math.Max(0, rowCount), Math.Max(0, columnCount)];
            if (map == null)
            {
                return mapDies;
            }

            foreach (KeyValuePair<int, Dictionary<int, Jm1RecheckDiePoint>> rowPair in map)
            {
                if (rowPair.Value == null)
                {
                    continue;
                }

                foreach (KeyValuePair<int, Jm1RecheckDiePoint> columnPair in rowPair.Value)
                {
                    int resolvedRowIndex;
                    int resolvedColumnIndex;
                    if (!TryResolveArrayIndex(rowPair.Key, rowCount, out resolvedRowIndex) ||
                        !TryResolveArrayIndex(columnPair.Key, columnCount, out resolvedColumnIndex))
                    {
                        continue;
                    }

                    mapDies[resolvedRowIndex, resolvedColumnIndex] = CreateMapDie(columnPair.Value, resolvedRowIndex + 1, resolvedColumnIndex + 1);
                }
            }

            return mapDies;
        }

        private static List<Die> ConvertPatchDies(List<Jm1RecheckDiePoint> patchPoints, int rowCount, int columnCount)
        {
            return ConvertPointListToDies(patchPoints, rowCount, columnCount, DieStatus.Normal);
        }

        private static List<Die> ConvertExceptDies(List<Jm1RecheckDiePoint> exceptPoints, int rowCount, int columnCount)
        {
            return ConvertPointListToDies(exceptPoints, rowCount, columnCount, DieStatus.Ng);
        }

        private static List<Die> ConvertPointListToDies(List<Jm1RecheckDiePoint> points, int rowCount, int columnCount, DieStatus defaultStatus)
        {
            List<Die> dies = new List<Die>();
            if (points == null)
            {
                return dies;
            }

            foreach (Jm1RecheckDiePoint point in points)
            {
                if (point == null)
                {
                    continue;
                }

                int resolvedRowIndex;
                int resolvedColumnIndex;
                if (!TryResolveArrayIndex(point.rowIndex, rowCount, out resolvedRowIndex) ||
                    !TryResolveArrayIndex(point.colIndex, columnCount, out resolvedColumnIndex))
                {
                    continue;
                }

                dies.Add(CreateListDie(point, resolvedRowIndex + 1, resolvedColumnIndex + 1, defaultStatus));
            }

            return dies;
        }

        private static Die CreateMapDie(Jm1RecheckDiePoint point, int row, int column)
        {
            Die die = new Die();
            die.Row = row;
            die.Column = column;
            if (point != null)
            {
                die.X = point.X;
                die.Y = point.Y;
                die.Angle = point.Angle;
                die.Status = point.State ? DieStatus.Normal : DieStatus.Ng;
            }
            return die;
        }

        private static Die CreateListDie(Jm1RecheckDiePoint point, int row, int column, DieStatus defaultStatus)
        {
            Die die = new Die();
            die.Row = row;
            die.Column = column;
            die.X = point.X;
            die.Y = point.Y;
            die.Angle = point.Angle;
            die.Status = defaultStatus;
            return die;
        }

        private static bool TryResolveArrayIndex(int rawIndex, int count, out int resolvedIndex)
        {
            resolvedIndex = -1;
            if (count <= 0)
            {
                return false;
            }

            if (rawIndex >= 1 && rawIndex <= count)
            {
                resolvedIndex = rawIndex - 1;
                return true;
            }

            if (rawIndex >= 0 && rawIndex < count)
            {
                resolvedIndex = rawIndex;
                return true;
            }

            return false;
        }

        private static int ResolveRecognitionConsumerCount(WaferDiePositionContext runtimeContext)
        {
            if (runtimeContext == null)
            {
                throw new ArgumentNullException(nameof(runtimeContext));
            }

            int configuredConsumerCount = runtimeContext.ScanSettings == null
                ? DefaultRecognitionConsumerCount
                : runtimeContext.ScanSettings.ConsumerCount;
            int safeConsumerCount = Math.Max(1, configuredConsumerCount);
            int plannedFrameCount = runtimeContext.ScanPlan == null ? 0 : runtimeContext.ScanPlan.PathPointCount;
            if (plannedFrameCount <= 0)
            {
                return safeConsumerCount;
            }

            return Math.Min(plannedFrameCount, safeConsumerCount);
        }

        private static int ResolveFrameChannelCapacity(WaferDiePositionContext runtimeContext, int consumerCount)
        {
            if (runtimeContext == null)
            {
                throw new ArgumentNullException(nameof(runtimeContext));
            }

            int configuredCapacity = runtimeContext.ScanSettings == null
                ? DefaultFrameChannelCapacity
                : runtimeContext.ScanSettings.FrameChannelCapacity;
            int safeCapacity = configuredCapacity <= 0 ? DefaultFrameChannelCapacity : configuredCapacity;
            return Math.Max(1, Math.Max(consumerCount, safeCapacity));
        }

        private static void EnsureFrameResults(WaferDiePositionContext runtimeContext)
        {
            if (runtimeContext.FrameResults == null)
            {
                runtimeContext.FrameResults = new List<WaferRecognitionFrameResult>();
                return;
            }

            runtimeContext.FrameResults.Clear();
        }

        private static WaferRecipe GetCurrentWaferRecipe(RecipeManager recipeManager)
        {
            if (recipeManager == null)
            {
                throw new ArgumentNullException(nameof(recipeManager));
            }

            if (recipeManager.CurrentWaferRecipe == null)
            {
                throw new LocalizedProcessException(
                    MessageKey.ProcessDiePositionRecipeNotLoaded,
                    Array.Empty<string>());
            }

            return recipeManager.CurrentWaferRecipe;
        }

        private static WaferInfo GetWaferInfo(WaferRecipe waferRecipe)
        {
            if (waferRecipe == null || waferRecipe.WaferInfo == null)
            {
                throw new LocalizedProcessException(
                    MessageKey.ProcessDiePositionRecipeNotLoaded,
                    Array.Empty<string>());
            }

            WaferInfo waferInfo = waferRecipe.WaferInfo;
            if (waferInfo.WaferRowNum <= 0 ||
                waferInfo.WaferColNum <= 0 ||
                waferInfo.DiePitchX == 0d ||
                waferInfo.DiePitchY == 0d)
            {
                throw new LocalizedProcessException(
                    MessageKey.ProcessDiePositionWaferInfoMissing,
                    Array.Empty<string>());
            }

            return waferInfo;
        }

        private static DiePositionProcessResult InitializeProcessResult(ProcessResultManager processResultManager)
        {
            if (processResultManager == null)
            {
                throw new ArgumentNullException(nameof(processResultManager));
            }

            DiePositionProcessResult processResult = processResultManager.DiePositionResult;
            processResult.IsSuccess = false;
            processResult.RecipeName = null;
            processResult.ErrorMessage = null;
            processResult.ErrorMessageKey = MessageKey.None;
            processResult.ErrorMessageArguments = Array.Empty<string>();
            processResult.RowCount = 0;
            processResult.ColumnCount = 0;
            processResult.TheoryDieCount = 0;
            processResult.ScanPointCount = 0;
            processResult.RecognizedDieCount = 0;
            processResult.NgDieCount = 0;
            processResult.AverageSpacingX = 0;
            processResult.AverageSpacingY = 0;
            processResult.MapDies = new Die[0, 0];
            processResult.PatchDies.Clear();
            processResult.ExceptDies.Clear();
            processResult.ScanPoints.Clear();
            processResult.RecognizedDies.Clear();
            return processResult;
        }

        private static void PopulateProcessResult(
            DiePositionProcessResult processResult,
            WaferDiePositionContext runtimeContext,
            WaferScanPlan scanPlan)
        {
            List<Die> placeholderDies = CreatePlaceholderDies(runtimeContext);

            processResult.RecipeName = runtimeContext.RecipeName;
            processResult.RowCount = runtimeContext.RowCount;
            processResult.ColumnCount = runtimeContext.ColumnCount;
            processResult.TheoryDieCount = runtimeContext.RowCount * runtimeContext.ColumnCount;
            processResult.ScanPointCount = scanPlan == null ? 0 : scanPlan.PathPointCount;
            processResult.RecognizedDieCount = runtimeContext.RecognitionSnapshot == null
                ? placeholderDies.Count
                : runtimeContext.RecognitionSnapshot.RowsReal.Length;
            processResult.NgDieCount = runtimeContext.ExceptDies == null ? 0 : runtimeContext.ExceptDies.Count;
            processResult.AverageSpacingX = runtimeContext.PitchX;
            processResult.AverageSpacingY = runtimeContext.PitchY;
            processResult.MapDies = runtimeContext.MapDies ?? new Die[0, 0];
            processResult.PatchDies.Clear();
            if (runtimeContext.PatchDies != null)
            {
                foreach (Die patchDie in runtimeContext.PatchDies)
                {
                    processResult.PatchDies.Add(patchDie);
                }
            }

            processResult.ExceptDies.Clear();
            if (runtimeContext.ExceptDies != null)
            {
                foreach (Die exceptDie in runtimeContext.ExceptDies)
                {
                    processResult.ExceptDies.Add(exceptDie);
                }
            }

            if (scanPlan != null && scanPlan.PathPoints != null)
            {
                foreach (Point scanPoint in scanPlan.PathPoints)
                {
                    processResult.ScanPoints.Add(scanPoint);
                }
            }

            foreach (Die die in placeholderDies)
            {
                processResult.RecognizedDies.Add(die);
            }

            if (runtimeContext.MapDies != null && runtimeContext.MapDies.Length > 0)
            {
                processResult.RecognizedDies.Clear();
                foreach (Die mapDie in runtimeContext.MapDies)
                {
                    if (mapDie != null)
                    {
                        processResult.RecognizedDies.Add(mapDie);
                    }
                }
            }
        }

        private static List<Die> CreatePlaceholderDies(WaferDiePositionContext runtimeContext)
        {
            List<Die> dies = new List<Die>();
            if (runtimeContext == null || runtimeContext.RowCount <= 0 || runtimeContext.ColumnCount <= 0)
            {
                return dies;
            }

            for (int rowIndex = 0; rowIndex < runtimeContext.RowCount; rowIndex++)
            {
                for (int columnIndex = 0; columnIndex < runtimeContext.ColumnCount; columnIndex++)
                {
                    Die die = new Die();
                    die.Row = rowIndex + 1;
                    die.Column = columnIndex + 1;
                    die.X = columnIndex * runtimeContext.PitchX;
                    die.Y = rowIndex * runtimeContext.PitchY;
                    die.Status = DieStatus.Normal;
                    dies.Add(die);
                }
            }

            return dies;
        }

        private static async Task CheckCancellationAndPauseAsync(ActivityControl activityControl)
        {
            activityControl.ThrowIfCancellationRequested();
            await activityControl.CheckPauseAsync().ConfigureAwait(false);
        }

        private static void WriteResultToContext(
            WorkflowContext context,
            ProcessResultManager processResultManager,
            DiePositionProcessResult processResult)
        {
            context.SetData(WorkflowContextKeys.DiePositionResult, processResult);
            processResultManager.SaveDiePositionResult();
        }
    }
}
