using JM1Vision;
using MainShell.Common;
using MainShell.Log;
using MainShell.Vision.Common;
using MwFramework.Device;
using MwFramework.Device.Model;
using MwFramework.ManagerService;
using SemiconductorVisionAlgorithm.SemiCalib;
using SemiconductorVisionAlgorithm.SemiParams;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Jm1RecheckDiePoint = JM1.JM1Params.RecheckDiePoint;
using SemiPoint = SemiconductorVisionAlgorithm.SemiParams.Point;


namespace MainShell.Vision
{
    /// <summary>
    /// 通用视觉算法服务。
    /// </summary>
    public class CommonVisionAlgorithmService : ICommonVisionAlgorithmService
    {
        public Task<VisionProcessResult> GenWaferMapXAsync(
            string fileDirectory,
            double[] edgePtRow,
            double[] edgePtCol,
            double[] edgePtAngle,
            double[] inPtRow,
            double[] inPtCol,
            double[] inPtAngle,
            double upThresholdAngle,
            double upRowDistance,
            double upColDistance,
            int upRowNumber,
            int upColNumber,
            double firstPosRow,
            double firstPosCol,
            double distanceThreshold,
            double angleThreshold,
            double rotateAngle,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            return ExecuteBoolAlgorithmAsync(
                nameof(GenWaferMapXAsync),
                delegate ()
                {
                    return JM1Manager.Instance.gen_wafer_map_x(
                        fileDirectory,
                        edgePtRow,
                        edgePtCol,
                        edgePtAngle,
                        inPtRow,
                        inPtCol,
                        inPtAngle,
                        upThresholdAngle,
                        upRowDistance,
                        upColDistance,
                        upRowNumber,
                        upColNumber,
                        firstPosRow,
                        firstPosCol,
                        distanceThreshold,
                        angleThreshold,
                        rotateAngle);
                },
                cancellationToken);
        }

        public Task<VisionProcessResult> RecheckPosRgbAsync(
            List<Jm1RecheckDiePoint> edgeDiePoints,
            List<Jm1RecheckDiePoint> inDiePoints,
            double threshold,
            List<Jm1RecheckDiePoint> existingPoints,
            RecheckDieMap padPos,
            double diePitch,
            int rowNumber,
            int colNumber,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            return ExecuteBoolAlgorithmAsync(
                nameof(RecheckPosRgbAsync),
                delegate ()
                {
                    Dictionary<int, Dictionary<int, Jm1RecheckDiePoint>> rawPadPos = padPos;

                    return JM1Manager.Instance.recheck_posRGB(
                        edgeDiePoints,
                        inDiePoints,
                        threshold,
                        existingPoints,
                        rawPadPos,
                        diePitch,
                        rowNumber,
                        colNumber);
                },
                cancellationToken);
        }

        public Task<VisionProcessResult> ClearRecheckDiePosAsync(
            CancellationToken cancellationToken = default(CancellationToken))
        {
            return ExecuteBoolAlgorithmAsync(
                nameof(ClearRecheckDiePosAsync),
                delegate ()
                {
                    return JM1Manager.Instance.ClearRecheckdiepos();
                },
                cancellationToken);
        }

        public Task<VisionProcessResult> ClearMapAsync(
            int flag,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            return ExecuteBoolAlgorithmAsync(
                nameof(ClearMapAsync),
                delegate ()
                {
                    return JM1Manager.Instance.ClearMap(flag);
                },
                cancellationToken);
        }

        public Task<VisionProcessResult<(RecheckDieMap UpMap, List<Jm1RecheckDiePoint> PatchPos, List<Jm1RecheckDiePoint> ExceptPos)>> GetMapAsync(
            CancellationToken cancellationToken = default(CancellationToken))
        {
            return ExecuteDataAlgorithmAsync(
                nameof(GetMapAsync),
                delegate ()
                {
                    Dictionary<int, Dictionary<int, Jm1RecheckDiePoint>> upMap;
                    List<Jm1RecheckDiePoint> patchPos;
                    List<Jm1RecheckDiePoint> exceptPos;

                    bool executeResult = JM1Manager.Instance.GetMap(out upMap, out patchPos, out exceptPos);
                    EnsureExecutionSucceeded(nameof(GetMapAsync), executeResult);
                    return (new RecheckDieMap(upMap), patchPos, exceptPos);
                },
                cancellationToken);
        }

        public Task<VisionProcessResult<(RecheckDieMap Map, List<Jm1RecheckDiePoint> ExposedPoints)>> GetRecheckDiePosAsync(
            CancellationToken cancellationToken = default(CancellationToken))
        {
            return ExecuteDataAlgorithmAsync(
                nameof(GetRecheckDiePosAsync),
                delegate ()
                {
                    Dictionary<int, Dictionary<int, Jm1RecheckDiePoint>> map;
                    List<Jm1RecheckDiePoint> exposedPoints;

                    bool executeResult = JM1Manager.Instance.GetRecheckdiepos(out map, out exposedPoints);
                    EnsureExecutionSucceeded(nameof(GetRecheckDiePosAsync), executeResult);
                    return (new RecheckDieMap(map), exposedPoints);
                },
                cancellationToken);
        }

        public Task<VisionProcessResult> CalProductAffineAsync(
            List<SemiPoint> sourcePoints,
            List<SemiPoint> targetPoints,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            return ExecuteBoolAlgorithmAsync(
                nameof(CalProductAffineAsync),
                delegate ()
                {
                    return JM1Manager.Instance.cal_product_affine(sourcePoints, targetPoints);
                },
                cancellationToken);
        }

        public Task<VisionProcessResult<double>> CalculateLineAngleAsync(
            SemiPoint point1,
            SemiPoint point2,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            return ExecuteDataAlgorithmAsync(
                nameof(CalculateLineAngleAsync),
                delegate ()
                {
                    double deg;
                    bool executeResult = JM1Manager.Instance.calculate_line_angle(point1, point2, out deg);
                    EnsureExecutionSucceeded(nameof(CalculateLineAngleAsync), executeResult);
                    return deg;
                },
                cancellationToken);
        }

        public Task<VisionProcessResult> SetWorkAreaAsync(
            string fileDirectory,
            double heightY,
            double widthX,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            return ExecuteBoolAlgorithmAsync(
                nameof(SetWorkAreaAsync),
                delegate ()
                {
                    return JM1Manager.Instance.set_workarea(fileDirectory, heightY, widthX);
                },
                cancellationToken);
        }

        public Task<VisionProcessResult> SetDirectionAsync(
            string fileDirectory,
            int xDirection,
            int yDirection,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            return ExecuteBoolAlgorithmAsync(
                nameof(SetDirectionAsync),
                delegate ()
                {
                    return JM1Manager.Instance.set_direction(fileDirectory, xDirection, yDirection);
                },
                cancellationToken);
        }


        private static Task<VisionProcessResult> ExecuteBoolAlgorithmAsync(
            string algorithmName,
            Func<bool> execute,
            CancellationToken cancellationToken)
        {
            if (execute == null)
            {
                throw new ArgumentNullException(nameof(execute));
            }

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                bool executeResult = execute();
                EnsureExecutionSucceeded(algorithmName, executeResult);

                string message = $"CommonVisionAlgorithmService: algorithm {algorithmName} completed.";
                message.LogInfo();
                return Task.FromResult(VisionProcessResult.Success());
            }
            catch (OperationCanceledException ex)
            {
                string message = $"CommonVisionAlgorithmService: algorithm {algorithmName} was cancelled.";
                message.LogInfo();
                return Task.FromResult(VisionProcessResult.Failure(
                    VisionFailureCategory.Cancelled,
                    VisionErrorCode.OperationCancelled,
                    null,
                    MessageKey.VisionOperationCancelled,
                    message,
                    ex));
            }
            catch (Exception ex)
            {
                string message = $"CommonVisionAlgorithmService: algorithm {algorithmName} failed. {ex.Message}";
                message.LogSysError();
                return Task.FromResult(VisionProcessResult.Failure(
                    VisionFailureCategory.Algorithm,
                    VisionErrorCode.CommonAlgorithmExecutionFailed,
                    VisionAlarmIds.CommonAlgorithmExecutionFailed,
                    MessageKey.VisionCommonAlgorithmExecutionFailed,
                    message,
                    ex));
            }
        }

        private static Task<VisionProcessResult<TData>> ExecuteDataAlgorithmAsync<TData>(
            string algorithmName,
            Func<TData> execute,
            CancellationToken cancellationToken)
        {
            if (execute == null)
            {
                throw new ArgumentNullException(nameof(execute));
            }

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                TData result = execute();
                string message = $"CommonVisionAlgorithmService: algorithm {algorithmName} completed.";
                message.LogInfo();
                return Task.FromResult(VisionProcessResult<TData>.Success(result));
            }
            catch (OperationCanceledException ex)
            {
                string message = $"CommonVisionAlgorithmService: algorithm {algorithmName} was cancelled.";
                message.LogInfo();
                return Task.FromResult(VisionProcessResult<TData>.Failure(
                    VisionFailureCategory.Cancelled,
                    VisionErrorCode.OperationCancelled,
                    null,
                    MessageKey.VisionOperationCancelled,
                    message,
                    ex));
            }
            catch (Exception ex)
            {
                string message = $"CommonVisionAlgorithmService: algorithm {algorithmName} failed. {ex.Message}";
                message.LogSysError();
                return Task.FromResult(VisionProcessResult<TData>.Failure(
                    VisionFailureCategory.Algorithm,
                    VisionErrorCode.CommonAlgorithmExecutionFailed,
                    VisionAlarmIds.CommonAlgorithmExecutionFailed,
                    MessageKey.VisionCommonAlgorithmExecutionFailed,
                    message,
                    ex));
            }
        }

        private static void EnsureExecutionSucceeded(string algorithmName, bool executeResult)
        {
            if (!executeResult)
            {
                throw new InvalidOperationException($"CommonVisionAlgorithmService: algorithm {algorithmName} returned false.");
            }
        }
    }
}
