using System;
using OpenCvSharp;

namespace OpenCVStudio_v0
{
    /// <summary>
    /// Выполняет морфологическую эрозию изображения.
    /// Уменьшает светлые области и удаляет
    /// мелкие светлые элементы.
    /// </summary>
    public sealed class ErodeExecutor : IImageBlockExecutor
    {
        public string BlockType => "Erode";

        public Mat Execute(Mat input, ProjectBlock block, ProjectManager projectManager)
        {
            ArgumentNullException.ThrowIfNull(input);
            ArgumentNullException.ThrowIfNull(block);
            ArgumentNullException.ThrowIfNull(projectManager);
            if (input.Empty())
            {
                throw new ArgumentException("В блок эрозии передано пустое изображение.", nameof(input));
            }
            int kernelWidth = projectManager.GetBlockParameter(block.Id, "KernelWidth", 3);
            int kernelHeight = projectManager.GetBlockParameter(block.Id, "KernelHeight", 3);
            int iterations = projectManager.GetBlockParameter(block.Id, "Iterations", 1);
            MorphologyKernelShape kernelShape = projectManager.GetBlockParameter(block.Id, "KernelShape", MorphologyKernelShape.Rectangle);
            ValidateParameters(kernelWidth, kernelHeight, iterations, block);
            MorphShapes morphologyShape = ConvertKernelShape(kernelShape);
            using Mat kernel = Cv2.GetStructuringElement(morphologyShape, new OpenCvSharp. Size(kernelWidth, kernelHeight), new OpenCvSharp.Point(-1, -1));
            Mat output = new Mat();
            try
            {
                Cv2.Erode(src: input, dst: output, element: kernel, anchor: new OpenCvSharp.Point(-1, -1), iterations: iterations, borderType: BorderTypes.Default);
                if (output.Empty())
                {
                    throw new InvalidOperationException($"Блок «{block.Name}» вернул пустое изображение.");
                }
                return output;
            }
            catch
            {
                output.Dispose();
                throw;
            }
        }

        private static void ValidateParameters(int kernelWidth, int kernelHeight, int iterations, ProjectBlock block)
        {
            if (kernelWidth < 1)
            {
                throw new InvalidOperationException($"Блок «{block.Name}»: ширина ядра должна быть не меньше 1.");
            }
            if (kernelHeight < 1)
            {
                throw new InvalidOperationException($"Блок «{block.Name}»: высота ядра должна быть не меньше 1.");
            }
            if (kernelWidth > 999)
            {
                throw new InvalidOperationException($"Блок «{block.Name}»: ширина ядра не должна превышать 999.");
            }
            if (kernelHeight > 999)
            {
                throw new InvalidOperationException($"Блок «{block.Name}»: высота ядра не должна превышать 999.");
            }
            if (iterations < 1)
            {
                throw new InvalidOperationException($"Блок «{block.Name}»: количество итераций должно быть не меньше 1.");
            }
            if (iterations > 100)
            {
                throw new InvalidOperationException($"Блок «{block.Name}»: количество итераций не должно превышать 100.");
            }
        }

        private static MorphShapes ConvertKernelShape(MorphologyKernelShape kernelShape)
        {
            return kernelShape switch
            {
                MorphologyKernelShape.Rectangle => MorphShapes.Rect,
                MorphologyKernelShape.Ellipse => MorphShapes.Ellipse,
                MorphologyKernelShape.Cross => MorphShapes.Cross,
                _ => throw new ArgumentOutOfRangeException(nameof(kernelShape), kernelShape, "Неизвестная форма ядра.")
            };
        }
    }
}