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

using OpenCvSharp;
namespace OpenCVStudio_v0
{
    public sealed class MorphologyExecutor : IImageBlockExecutor
    {
        public string BlockType => "Morphology";
        public Mat Execute(Mat input, ProjectBlock block, ProjectManager projectManager)
        {
            ArgumentNullException.ThrowIfNull(input);
            ArgumentNullException.ThrowIfNull(block);
            ArgumentNullException.ThrowIfNull(projectManager);
            if (input.Empty()) throw new ArgumentException("В Morphology передано пустое изображение.", nameof(input));
            MorphologyOperationMode operation = projectManager.GetBlockParameter(block.Id, "Operation", MorphologyOperationMode.Open);
            MorphologyKernelShape kernelShape = projectManager.GetBlockParameter(block.Id, "KernelShape", MorphologyKernelShape.Rectangle);
            int kernelWidth = projectManager.GetBlockParameter(block.Id, "KernelWidth", 3);
            int kernelHeight = projectManager.GetBlockParameter(block.Id, "KernelHeight", 3);
            int iterations = projectManager.GetBlockParameter(block.Id, "Iterations", 1);
            ValidateParameters(kernelWidth, kernelHeight, iterations);
            MorphShapes openCvKernelShape = ConvertKernelShape(kernelShape);
            using Mat kernel = Cv2.GetStructuringElement(openCvKernelShape, new OpenCvSharp. Size(kernelWidth, kernelHeight));
            Mat output = new Mat();
            try
            {
                ExecuteOperation(input, output, kernel, operation, iterations);
                if (output.Empty()) throw new InvalidOperationException("Morphology вернул пустое изображение.");
                return output;
            }
            catch { output.Dispose(); throw; }
        }
        private static void ExecuteOperation(Mat input, Mat output, Mat kernel, MorphologyOperationMode operation, int iterations)
        {
            switch (operation)
            {
                case MorphologyOperationMode.Erode:
                    Cv2.Erode(input, output, kernel, iterations: iterations);
                    break;
                case MorphologyOperationMode.Dilate:
                    Cv2.Dilate(input, output, kernel, iterations: iterations);
                    break;
                case MorphologyOperationMode.Open:
                    Cv2.MorphologyEx(input, output, MorphTypes.Open, kernel, iterations: iterations);
                    break;
                case MorphologyOperationMode.Close:
                    Cv2.MorphologyEx(input, output, MorphTypes.Close, kernel, iterations: iterations);
                    break;
                case MorphologyOperationMode.Gradient:
                    Cv2.MorphologyEx(input, output, MorphTypes.Gradient, kernel, iterations: iterations);
                    break;
                case MorphologyOperationMode.TopHat:
                    Cv2.MorphologyEx(input, output, MorphTypes.TopHat, kernel, iterations: iterations);
                    break;
                case MorphologyOperationMode.BlackHat:
                    Cv2.MorphologyEx(input, output, MorphTypes.BlackHat, kernel, iterations: iterations);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(operation), operation, "Неизвестная морфологическая операция.");
            }
        }
        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, "Неизвестная форма ядра.")
            };
        }
        private static void ValidateParameters(int kernelWidth, int kernelHeight, int iterations)
        {
            if (kernelWidth < 1 || kernelWidth > 255 || kernelWidth % 2 == 0) throw new ArgumentException("Ширина ядра должна быть нечётной и находиться от 1 до 255.");
            if (kernelHeight < 1 || kernelHeight > 255 || kernelHeight % 2 == 0) throw new ArgumentException("Высота ядра должна быть нечётной и находиться от 1 до 255.");
            if (iterations < 1 || iterations > 100) throw new ArgumentException("Количество повторов должно быть от 1 до 100.");
        }
    }
}