using System;
using OpenCvSharp;

namespace OpenCVStudio_v0
{
    public sealed class PyrDownExecutor : IImageBlockExecutor
    {
        private const int MinimumLevels = 1;
        private const int MaximumLevels = 8;

        public string BlockType => "PyrDown";

        public Mat Execute(Mat input, ProjectBlock block, ProjectManager projectManager)
        {
            ArgumentNullException.ThrowIfNull(input);
            ArgumentNullException.ThrowIfNull(block);
            ArgumentNullException.ThrowIfNull(projectManager);

            if (input.Empty())
                throw new ArgumentException("В блок PyrDown передано пустое изображение.", nameof(input));

            int levels = projectManager.GetBlockParameter(block.Id, "Levels", 1);
            RotateBorderMode borderMode = projectManager.GetBlockParameter(block.Id, "BorderMode", RotateBorderMode.Reflect101);

            ValidateParameters(input, levels, borderMode);

            Mat current = input.Clone();

            try
            {
                for (int i = 0; i < levels; i++)
                {
                    Mat next = new Mat();

                    try
                    {
                        Cv2.PyrDown(current, next, default, ConvertBorderMode(borderMode));

                        if (next.Empty())
                            throw new InvalidOperationException("PyrDown вернул пустое изображение.");
                    }
                    catch
                    {
                        next.Dispose();
                        throw;
                    }

                    current.Dispose();
                    current = next;
                }

                return current;
            }
            catch
            {
                current.Dispose();
                throw;
            }
        }

        private static BorderTypes ConvertBorderMode(RotateBorderMode mode)
        {
            return mode switch
            {
                RotateBorderMode.Replicate => BorderTypes.Replicate,
                RotateBorderMode.Reflect => BorderTypes.Reflect,
                RotateBorderMode.Reflect101 => BorderTypes.Reflect101,
                _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Данный режим обработки границ не поддерживается PyrDown.")
            };
        }

        private static void ValidateParameters(Mat input, int levels, RotateBorderMode borderMode)
        {
            if (levels < MinimumLevels || levels > MaximumLevels)
                throw new ArgumentOutOfRangeException(nameof(levels), levels, $"Количество уровней должно находиться в диапазоне от {MinimumLevels} до {MaximumLevels}.");

            if (!Enum.IsDefined(borderMode))
                throw new ArgumentOutOfRangeException(nameof(borderMode), borderMode, "Неизвестный режим обработки границ.");

            if (borderMode == RotateBorderMode.Constant)
                throw new ArgumentException("PyrDown не поддерживает режим Constant.", nameof(borderMode));

            int width = input.Width;
            int height = input.Height;

            for (int i = 0; i < levels; i++)
            {
                if (width < 2 || height < 2)
                    throw new ArgumentException("Количество уровней слишком велико для данного изображения.", nameof(levels));

                width /= 2;
                height /= 2;
            }
        }
    }
}