using System;
using System.Collections.Generic;
using System.Text;

using OpenCvSharp;
namespace OpenCVStudio_v0
{
    public sealed class CannyExecutor : IImageBlockExecutor
    {
        public string BlockType => "Canny";
        public Mat Execute(Mat input, ProjectBlock block, ProjectManager projectManager)
        {
            ArgumentNullException.ThrowIfNull(input);
            ArgumentNullException.ThrowIfNull(block);
            ArgumentNullException.ThrowIfNull(projectManager);
            if (input.Empty()) throw new ArgumentException("В Canny передано пустое изображение.", nameof(input));
            double threshold1 = projectManager.GetBlockParameter(block.Id, "Threshold1", 50.0);
            double threshold2 = projectManager.GetBlockParameter(block.Id, "Threshold2", 150.0);
            int apertureSize = projectManager.GetBlockParameter(block.Id, "ApertureSize", 3);
            bool l2Gradient = projectManager.GetBlockParameter(block.Id, "L2Gradient", false);
            ValidateParameters(threshold1, threshold2, apertureSize);
            using Mat grayInput = PrepareGrayInput(input);
            Mat output = new Mat();
            try
            {
                Cv2.Canny(grayInput, output, threshold1, threshold2, apertureSize, l2Gradient);
                if (output.Empty()) throw new InvalidOperationException("Canny вернул пустое изображение.");
                return output;
            }
            catch { output.Dispose(); throw; }
        }
        private static Mat PrepareGrayInput(Mat input)
        {
            if (input.Channels() == 1) return input.Clone();
            Mat gray = new Mat();
            try
            {
                if (input.Channels() == 3)
                {
                    Cv2.CvtColor(input, gray, ColorConversionCodes.BGR2GRAY);
                    return gray;
                }
                if (input.Channels() == 4)
                {
                    Cv2.CvtColor(input, gray, ColorConversionCodes.BGRA2GRAY);
                    return gray;
                }
                throw new NotSupportedException($"Canny не поддерживает изображение с числом каналов {input.Channels()}.");
            }
            catch { gray.Dispose(); throw; }
        }
        private static void ValidateParameters(double threshold1, double threshold2, int apertureSize)
        {
            if (threshold1 < 0 || double.IsNaN(threshold1) || double.IsInfinity(threshold1))
                throw new ArgumentException("Нижний порог Canny указан неправильно.");
            if (threshold2 < 0 || double.IsNaN(threshold2) || double.IsInfinity(threshold2))
                throw new ArgumentException("Верхний порог Canny указан неправильно.");
            if (threshold2 < threshold1) throw new ArgumentException("Верхний порог Canny не должен быть меньше нижнего.");
            if (apertureSize != 3 && apertureSize != 5 && apertureSize != 7)
                throw new ArgumentException("Размер Sobel может быть только 3, 5 или 7.");
        }
    }
}