using System;
using OpenCvSharp;

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

        public string BlockType => "PyrUp";

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

            if (input.Empty())
                throw new ArgumentException("В блок PyrUp передано пустое изображение.", 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.PyrUp(current, next, default, ConvertBorderMode(borderMode));

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

                    current.Dispose();
                    current = next;
                }

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

        private static BorderTypes ConvertBorderMode(RotateBorderMode mode)
        {
            return mode switch
            {
                RotateBorderMode.Reflect101 => BorderTypes.Reflect101,
                _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Блок PyrUp поддерживает только режим Reflect101.")
            };
        }

        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.Reflect101)
                throw new ArgumentException("Блок PyrUp поддерживает только режим Reflect101.", nameof(borderMode));

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

            for (int i = 0; i < levels; i++)
            {
                width *= 2;
                height *= 2;

                if (width > int.MaxValue || height > int.MaxValue)
                    throw new ArgumentException("Количество уровней создаёт изображение слишком большого размера.", nameof(levels));

                long pixelCount = width * height;

                if (pixelCount > 500_000_000L)
                    throw new ArgumentException("Результирующее изображение будет слишком большим. Уменьшите количество уровней.", nameof(levels));
            }
        }
    }
}