using MainShell.Common;
using MainShell.Log;
using MainShell.Models;
using MainShell.Vision.Common;
using MaxwellFramework.Core.Attributes;
using SemiconductorVisionAlgorithm.SemiCalib;
using SemiconductorVisionAlgorithm.SemiParams;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MainShell.Vision
{
    /// <summary>
    /// 抓圆服务实现
    /// </summary>
    [Singleton]
    public class FindEdgeCircleService : IFindEdgeCircleService
    {
        private readonly IImageCaptureService _imageCaptureService;

        public FindEdgeCircleService(IImageCaptureService imageCaptureService)
        {
            _imageCaptureService = imageCaptureService ?? throw new ArgumentNullException(nameof(imageCaptureService));
        }

        /// <summary>
        /// 采集图像并执行抓圆
        /// </summary>
        public async Task<VisionProcessResult<FindEdgeCircleResult>> ProcessAsync(
            FindEdgeCircleRequest request,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            FindEdgeCircleImageRequest imageRequest = CreateImageRequest(request);
            return await ExecuteWithCaptureAsync(
                request.CameraSource,
                request.CaptureOptions,
                imageRequest,
                ProcessImageAsync,
                cancellationToken).ConfigureAwait(false);
        }

        /// <summary>
        /// 基于已有图像直接执行抓圆
        /// </summary>
        public Task<VisionProcessResult<FindEdgeCircleResult>> ProcessImageAsync(
            MxImage image,
            FindEdgeCircleImageRequest request,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (image == null)
            {
                throw new ArgumentNullException(nameof(image));
            }

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

            FindEdgeCircleImageRequest normalizedRequest = NormalizeRequest(request);
            VisionProcessResult validationResult = ValidateRequest(normalizedRequest);
            if (validationResult != null)
            {
                return Task.FromResult(CreateFailureResult(validationResult));
            }

            try
            {
                cancellationToken.ThrowIfCancellationRequested();
                VisionProcessResult<FindEdgeCircleResult> result = ExecuteEdgeCircle(image, normalizedRequest);
                if (!result.Succeeded)
                {
                    result.Message.LogSysError();
                }

                return Task.FromResult(result);
            }
            catch (OperationCanceledException ex)
            {
                string message = $"FindEdgeCircleService: process image was cancelled.Timeout ={ normalizedRequest.TimeoutMilliseconds}ms.";
                  message.LogInfo();
                return Task.FromResult(VisionProcessResult<FindEdgeCircleResult>.Failure(
                    VisionFailureCategory.Cancelled,
                    VisionErrorCode.OperationCancelled,
                    null,
                    MessageKey.VisionOperationCancelled,
                    message,
                    ex));
            }
            catch (Exception ex)
            {
                string message = $"FindEdgeCircleService: process image failed. {ex.Message}";
                message.LogSysError();
                return Task.FromResult(VisionProcessResult<FindEdgeCircleResult>.Failure(
                    VisionFailureCategory.Algorithm,
                    VisionErrorCode.CommonAlgorithmExecutionFailed,
                    VisionAlarmIds.CommonAlgorithmExecutionFailed,
                    MessageKey.VisionCommonAlgorithmExecutionFailed,
                    message,
                    ex));
            }
        }

        /// <summary>
        /// 执行边缘圆检测算法
        /// </summary>
        private static VisionProcessResult<FindEdgeCircleResult> ExecuteEdgeCircle(
            MxImage image,
            FindEdgeCircleImageRequest request)
        {
            Camera camera = image.Image;
            if (camera == null)
            {
                return VisionProcessResult<FindEdgeCircleResult>.Failure(
                    VisionFailureCategory.Algorithm,
                    VisionErrorCode.ImageNull,
                    VisionAlarmIds.ImageNull,
                    MessageKey.VisionImageIsNull,
                    "FindEdgeCircleService: input image does not contain a valid camera object.");
            }

            var parameters = request.Parameters;
            double[] hv_Parameter;
            bool flag;

            if (parameters.RoiRect != null)
            {
                // 使用 ROI 区域进行抓圆
                flag = CalibManager.Instance.calib_edge_circle_by_roi(
                    camera,
                    parameters.CameraId,
                    0,
                    parameters.RoiRect,
                    parameters.FileDirectory,
                    out hv_Parameter);
            }
            else
            {
                // 不使用 ROI 进行抓圆
                flag = CalibManager.Instance.calib_edge_circle(
                    camera,
                    parameters.CameraId,
                    0,
                    parameters.FileDirectory,
                    out hv_Parameter);
            }

            if (!flag)
            {
                return VisionProcessResult<FindEdgeCircleResult>.Failure(
                    VisionFailureCategory.Algorithm,
                    VisionErrorCode.CommonAlgorithmExecutionFailed,
                    VisionAlarmIds.CommonAlgorithmExecutionFailed,
                    MessageKey.VisionCommonAlgorithmExecutionFailed,
                    "FindEdgeCircleService: algorithm calib_edge_circle returned false.");
            }

            if (hv_Parameter == null || hv_Parameter.Length < 2)
            {
                return VisionProcessResult<FindEdgeCircleResult>.Failure(
                    VisionFailureCategory.Algorithm,
                    VisionErrorCode.CommonAlgorithmExecutionFailed,
                    VisionAlarmIds.CommonAlgorithmExecutionFailed,
                    MessageKey.VisionCommonAlgorithmExecutionFailed,
                    "FindEdgeCircleService: algorithm returned invalid parameters.");
            }

            FindEdgeCircleResult result = new FindEdgeCircleResult();
            result.CenterX = hv_Parameter[0];
            result.CenterY = hv_Parameter[1];
            return VisionProcessResult<FindEdgeCircleResult>.Success(result);
        }

        private static FindEdgeCircleImageRequest NormalizeRequest(FindEdgeCircleImageRequest request)
        {
            FindEdgeCircleImageRequest normalizedRequest = new FindEdgeCircleImageRequest();
            normalizedRequest.TimeoutMilliseconds = request.TimeoutMilliseconds > 0 ? request.TimeoutMilliseconds :3000;
            normalizedRequest.Parameters = CloneParameters(request.Parameters);
            return normalizedRequest;
        }

        private static VisionProcessResult ValidateRequest(FindEdgeCircleImageRequest request)
        {
            if (request.Parameters == null)
            {
                return VisionProcessResult.Failure(
                    VisionFailureCategory.Validation,
                    VisionErrorCode.RequestInvalid,
                    VisionAlarmIds.RequestInvalid,
                    MessageKey.VisionRequestInvalid,
                    "FindEdgeCircleService: parameters cannot be null.");
            }

            if (request.TimeoutMilliseconds <= 0 || request.TimeoutMilliseconds > 60000)
            {
                return VisionProcessResult.Failure(
                    VisionFailureCategory.Validation,
                    VisionErrorCode.TimeoutInvalid,
                    VisionAlarmIds.RequestInvalid,
                    MessageKey.VisionTimeoutInvalid,
                    string.Format("FindEdgeCircleService: TimeoutMilliseconds {0} is invalid. Expected range is [1,60000].", request.TimeoutMilliseconds));
              }

            if (string.IsNullOrWhiteSpace(request.Parameters.FileDirectory))
            {
                return VisionProcessResult.Failure(
                    VisionFailureCategory.Validation,
                    VisionErrorCode.RequestInvalid,
                    VisionAlarmIds.RequestInvalid,
                    MessageKey.VisionRequestInvalid,
                    "FindEdgeCircleService: FileDirectory cannot be empty.");
            }

            if (string.IsNullOrWhiteSpace(request.Parameters.CameraId))
            {
                return VisionProcessResult.Failure(
                    VisionFailureCategory.Validation,
                    VisionErrorCode.RequestInvalid,
                    VisionAlarmIds.RequestInvalid,
                    MessageKey.VisionRequestInvalid,
                    "FindEdgeCircleService: CameraId cannot be empty.");
            }

            return null;
        }

        private static FindEdgeCircleParameters CloneParameters(FindEdgeCircleParameters parameters)
        {
            if (parameters == null)
            {
                return new FindEdgeCircleParameters();
            }

            FindEdgeCircleParameters clone = new FindEdgeCircleParameters();
            clone.UseRoi = parameters.UseRoi;
            clone.RoiName = parameters.RoiName;
            clone.FileDirectory = parameters.FileDirectory;
            clone.CameraId = parameters.CameraId;
            clone.RoiRect = parameters.RoiRect;
            return clone;
        }

        private static FindEdgeCircleImageRequest CreateImageRequest(FindEdgeCircleRequest request)
        {
            FindEdgeCircleImageRequest imageRequest = new FindEdgeCircleImageRequest();
            imageRequest.TimeoutMilliseconds = request.TimeoutMilliseconds;
            imageRequest.Parameters = CloneParameters(request.Parameters);
            return imageRequest;
        }

        private async Task<VisionProcessResult<FindEdgeCircleResult>> ExecuteWithCaptureAsync(
            CameraType cameraSource,
            CameraCaptureOptions captureOptions,
            FindEdgeCircleImageRequest imageRequest,
            Func<MxImage, FindEdgeCircleImageRequest, CancellationToken,
Task<VisionProcessResult<FindEdgeCircleResult>>> imageProcessor,
            CancellationToken cancellationToken)
        {
            ImageCaptureResult captureResult = null;
            MxImage image = null;

            try
            {
                captureResult = await _imageCaptureService.CaptureAsync(
                    cameraSource,
                    captureOptions,
                    cancellationToken).ConfigureAwait(false);

                if (captureResult == null)
                {
                    return VisionProcessResult<FindEdgeCircleResult>.Failure(
                        VisionFailureCategory.Capture,
                        VisionErrorCode.NoFrame,
                        VisionAlarmIds.NoFrame,
                        MessageKey.VisionNoFrameReturned,
                        string.Format(
                            "FindEdgeCircleService: image capture returned null result for camera '{0}'.",
                            cameraSource));
                }

                if (!captureResult.Succeeded)
                {
                    return CreateFailureResult(VisionResultMapper.CreateCaptureFailure(captureResult));
                }

                image = captureResult.Image;
                if (image == null)
                {
                    string message = string.Format(
                        "FindEdgeCircleService: image capture succeeded but returned null image for camera '{0}'.",
                        cameraSource);
                    message.LogSysError();
                    return VisionProcessResult<FindEdgeCircleResult>.Failure(
                        VisionFailureCategory.Capture,
                        VisionErrorCode.ImageNull,
                        VisionAlarmIds.ImageNull,
                        MessageKey.VisionImageIsNull,
                        message);
                }

                return await imageProcessor(image, imageRequest, cancellationToken).ConfigureAwait(false);
            }
            catch (OperationCanceledException ex)
            {
                string message = $"FindEdgeCircleService: capture orchestration was cancelled for camera  '{cameraSource}'.";
                  message.LogInfo();
                return VisionProcessResult<FindEdgeCircleResult>.Failure(
                    VisionFailureCategory.Cancelled,
                    VisionErrorCode.OperationCancelled,
                    null,
                    MessageKey.VisionOperationCancelled,
                    message,
                    ex);
            }
            catch (Exception ex)
            {
                string message = $"FindEdgeCircleService: capture orchestration failed for camera '{cameraSource}'.  { ex.Message}                ";
                  message.LogSysError();
                return VisionProcessResult<FindEdgeCircleResult>.Failure(
                    VisionFailureCategory.Algorithm,
                    VisionErrorCode.CommonAlgorithmExecutionFailed,
                    VisionAlarmIds.CommonAlgorithmExecutionFailed,
                    MessageKey.VisionCommonAlgorithmExecutionFailed,
                    message,
                    ex);
            }
            finally
            {
                if (image != null)
                {
                    image.Dispose();
                }
            }
        }

        private static VisionProcessResult<FindEdgeCircleResult> CreateFailureResult(VisionProcessResult result)
        {
            return VisionProcessResult<FindEdgeCircleResult>.Failure(
                result.FailureCategory,
                result.ErrorCode,
                result.AlarmId,
                result.UserMessageKey,
                result.Message,
                result.Exception);
        }
    }
}