using System;
using OpenCvSharp;

namespace OpenCVStudio_v0
{
    public sealed class PhaseExecutor : IImageBlockExecutor
    {
        public string BlockType => "Phase";

        public Mat Execute(Mat input, ProjectBlock block, ProjectManager projectManager)
        {
            ArgumentNullException.ThrowIfNull(input);
            ArgumentNullException.ThrowIfNull(block);
            ArgumentNullException.ThrowIfNull(projectManager);
            if (input.Empty())
            {
                throw new InvalidOperationException("Phase: входное изображение пустое.");
            }
            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);
            bool angleInDegrees = projectManager.GetBlockParameter(block.Id, "AngleInDegrees", true);
            ValidateParameters(kernelSize, blurSize, scale);
            using Mat gray = ConvertToGray(input);
            using Mat prepared = PrepareImage(gray, useBlur, blurSize);
            using Mat gradientX = new Mat();
            using Mat gradientY = new Mat();
            Cv2.Sobel(prepared, gradientX, MatType.CV_32F, 1, 0, kernelSize, scale, 0, BorderTypes.Default);
            Cv2.Sobel(prepared, gradientY, MatType.CV_32F, 0, 1, kernelSize, scale, 0, BorderTypes.Default);
            using Mat angle = new Mat();
            Cv2.Phase(gradientX, gradientY, angle, angleInDegrees);
            Mat result = new Mat();
            double conversionScale = angleInDegrees ? 255.0 / 360.0 : 255.0 / (2.0 * Math.PI);
            angle.ConvertTo(result, MatType.CV_8UC1, conversionScale);
            return result;
        }

        private static Mat ConvertToGray(Mat input)
        {
            Mat gray = new Mat();
            try
            {
                switch (input.Channels())
                {
                    case 1:
                        input.CopyTo(gray);
                        break;
                    case 3:
                        Cv2.CvtColor(input, gray, ColorConversionCodes.BGR2GRAY);
                        break;
                    case 4:
                        Cv2.CvtColor(input, gray, ColorConversionCodes.BGRA2GRAY);
                        break;
                    default:
                        throw new InvalidOperationException("Phase поддерживает изображения с 1, 3 или 4 каналами.");
                }
                return gray;
            }
            catch
            {
                gray.Dispose();
                throw;
            }
        }

        private static Mat PrepareImage(Mat gray, bool useBlur, int blurSize)
        {
            Mat prepared = new Mat();
            try
            {
                if (useBlur)
                {
                    Cv2.GaussianBlur(gray, prepared, new OpenCvSharp. Size(blurSize, blurSize), 0);
                }
                else
                {
                    gray.CopyTo(prepared);
                }
                return prepared;
            }
            catch
            {
                prepared.Dispose();
                throw;
            }
        }

        private static void ValidateParameters(int kernelSize, int blurSize, double scale)
        {
            if (kernelSize != 1 && kernelSize != 3 && kernelSize != 5 && kernelSize != 7)
            {
                throw new InvalidOperationException("Phase: размер ядра Sobel должен быть 1, 3, 5 или 7.");
            }
            if (blurSize < 1 || blurSize % 2 == 0)
            {
                throw new InvalidOperationException("Phase: размер ядра размытия должен быть положительным нечётным числом.");
            }
            if (double.IsNaN(scale) || double.IsInfinity(scale))
            {
                throw new InvalidOperationException("Phase: масштаб Sobel должен быть конечным числом.");
            }
        }
    }
}