using MainShell.Log;
using MainShell.Models;
using MaxwellFramework.Core.Attributes;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MainShell.Vision
{
    /// <summary>
    /// 中心识别服务实现。
    /// </summary>
    [Singleton]
    public class FindCenterService : IFindCenterService
    {
        private readonly IImageCaptureService _imageCaptureService;

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

        public async Task<VisionProcessResult<FindCenterResult>> ProcessAsync(
            FindCenterRequest request,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            ImageCaptureResult captureResult = null;
            MxImage image = null;

            try
            {
                captureResult = await _imageCaptureService.CaptureAsync(
                    request.CameraSource,
                    request.CaptureOptions,
                    cancellationToken).ConfigureAwait(false);

                if (captureResult == null)
                {
                    string message = string.Format(
                        "FindCenterService: image capture returned null result for camera '{0}'.",
                        request.CameraSource);
                    message.LogSysError();
                    return VisionProcessResult<FindCenterResult>.Failure(message);
                }

                if (!captureResult.Succeeded)
                {
                    return VisionProcessResult<FindCenterResult>.Failure(captureResult.Message, captureResult.Exception);
                }

                image = captureResult.Image;
                if (image == null)
                {
                    string message = string.Format(
                        "FindCenterService: image capture succeeded but returned null image for camera '{0}'.",
                        request.CameraSource);
                    message.LogSysError();
                    return VisionProcessResult<FindCenterResult>.Failure(message);
                }

                FindCenterImageRequest imageRequest = new FindCenterImageRequest();
                imageRequest.TimeoutMilliseconds = request.TimeoutMilliseconds;
                imageRequest.Parameters = request.Parameters;

                return await ProcessImageAsync(image, imageRequest, cancellationToken).ConfigureAwait(false);
            }
            catch (OperationCanceledException ex)
            {
                string message = "FindCenterService: process was cancelled.";
                message.LogInfo();
                return VisionProcessResult<FindCenterResult>.Failure(message, ex);
            }
            catch (Exception ex)
            {
                string message = string.Format("FindCenterService: process failed. {0}", ex.Message);
                message.LogSysError();
                return VisionProcessResult<FindCenterResult>.Failure(message, ex);
            }
            finally
            {
                if (image != null)
                {
                    image.Dispose();
                }
            }
        }

        public Task<VisionProcessResult<FindCenterResult>> ProcessImageAsync(
            MxImage image,
            FindCenterImageRequest request,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (image == null)
            {
                throw new ArgumentNullException(nameof(image));
            }

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

            FindCenterImageRequest normalizedRequest = NormalizeRequest(request);
            VisionProcessResult validationResult = ValidateRequest(normalizedRequest);
            if (validationResult != null)
            {
                return Task.FromResult(VisionProcessResult<FindCenterResult>.Failure(validationResult.Message, validationResult.Exception));
            }

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                string message = string.Format(
                    "FindCenterService: algorithm is not implemented. RoiName={0}, UseRoi={1}, MinScore={2}.",
                    normalizedRequest.Parameters != null ? normalizedRequest.Parameters.RoiName : null,
                    normalizedRequest.Parameters != null && normalizedRequest.Parameters.UseRoi,
                    normalizedRequest.Parameters != null ? normalizedRequest.Parameters.MinScore : 0.0);

                message.LogSysError();
                return Task.FromResult(VisionProcessResult<FindCenterResult>.Failure(message));
            }
            catch (OperationCanceledException ex)
            {
                string message = string.Format(
                    "FindCenterService: process image was cancelled. Timeout={0} ms.",
                    normalizedRequest.TimeoutMilliseconds);
                message.LogInfo();
                return Task.FromResult(VisionProcessResult<FindCenterResult>.Failure(message, ex));
            }
            catch (Exception ex)
            {
                string message = string.Format("FindCenterService: process image failed. {0}", ex.Message);
                message.LogSysError();
                return Task.FromResult(VisionProcessResult<FindCenterResult>.Failure(message, ex));
            }
        }

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

        private static VisionProcessResult ValidateRequest(FindCenterImageRequest request)
        {
            if (request.Parameters == null)
            {
                return VisionProcessResult.Failure("FindCenterService: parameters cannot be null.");
            }

            if (request.TimeoutMilliseconds <= 0 || request.TimeoutMilliseconds > 60000)
            {
                return VisionProcessResult.Failure(
                    string.Format("FindCenterService: TimeoutMilliseconds {0} is invalid. Expected range is [1, 60000].", request.TimeoutMilliseconds));
            }

            if (request.Parameters.MinScore <= 0d || request.Parameters.MinScore > 1d)
            {
                return VisionProcessResult.Failure(
                    string.Format("FindCenterService: MinScore {0} is invalid. Expected range is (0, 1].", request.Parameters.MinScore));
            }

            if (request.Parameters.UseRoi && string.IsNullOrWhiteSpace(request.Parameters.RoiName))
            {
                return VisionProcessResult.Failure("FindCenterService: RoiName cannot be empty when UseRoi is true.");
            }

            return null;
        }

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

            FindCenterParameters clone = new FindCenterParameters();
            clone.UseRoi = parameters.UseRoi;
            clone.RoiName = parameters.RoiName;
            clone.MinScore = parameters.MinScore;
            return clone;
        }
    }
}
