using System;
using OpenCvSharp;

namespace OpenCVStudio_v0
{
    public sealed class MagnitudeExecutor : IImageBlockExecutor
    {
        public string BlockType => "Magnitude";

        public Mat Execute(Mat input, ProjectBlock block, ProjectManager projectManager)
        {
            if (input == null)
            {
                throw new ArgumentNullException(nameof(input));
            }
            if (input.Empty())
            {
                throw new InvalidOperationException("Magnitude: изображение пустое.");
            }
            MagnitudeDirection direction = projectManager.GetBlockParameter(block.Id, "Direction", MagnitudeDirection.Both);
            int kernelSize = projectManager.GetBlockParameter(block.Id, "KernelSize", 3);
            bool useBlur = projectManager.GetBlockParameter(block.Id, "UseBlur", true);
            int blurSize = projectManager.GetBlockParameter(block.Id, "BlurSize", 3);
            double scale = projectManager.GetBlockParameter(block.Id, "Scale", 1.0);
            using Mat gray = new Mat();
            if (input.Channels() == 3)
            {
                Cv2.CvtColor(input, gray, ColorConversionCodes.BGR2GRAY);
            }
            else
            {
                input.CopyTo(gray);
            }
            if (useBlur)
            {
                Cv2.GaussianBlur(gray, gray, new OpenCvSharp. Size(blurSize, blurSize), 0);
            }
            using Mat gradX = new Mat();
            using Mat gradY = new Mat();
            if (direction == MagnitudeDirection.Both || direction == MagnitudeDirection.X)
            {
                Cv2.Sobel(gray, gradX, MatType.CV_32F, 1, 0, kernelSize, scale);
            }
            if (direction == MagnitudeDirection.Both || direction == MagnitudeDirection.Y)
            {
                Cv2.Sobel(gray, gradY, MatType.CV_32F, 0, 1, kernelSize, scale);
            }
            Mat result = new Mat();
            if (direction == MagnitudeDirection.Both)
            {
                using Mat magnitude32 = new Mat();
                Cv2.Magnitude(gradX, gradY, magnitude32);
                Cv2.ConvertScaleAbs(magnitude32, result);
            }
            else if (direction == MagnitudeDirection.X)
            {
                Cv2.ConvertScaleAbs(gradX, result);
            }
            else
            {
                Cv2.ConvertScaleAbs(gradY, result);
            }
            return result;
        }
    }
}