using System;
using OpenCvSharp;
namespace OpenCVStudio_v0
{
    public sealed class CropExecutor : IImageBlockExecutor
    {
        public string BlockType => "Crop";
        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 x = projectManager.GetBlockParameter(block.Id, "X", 0);
            int y = projectManager.GetBlockParameter(block.Id, "Y", 0);
            int width = projectManager.GetBlockParameter(block.Id, "Width", 640);
            int height = projectManager.GetBlockParameter(block.Id, "Height", 480);
            bool clampToImage = projectManager.GetBlockParameter(block.Id, "ClampToImage", true);
            ValidateParameters(x, y, width, height);
            Rect cropRect = CreateCropRectangle(input, x, y, width, height, clampToImage);
            Mat output = input.Clone(cropRect);
            if (output.Empty()) { output.Dispose(); throw new InvalidOperationException("Блок обрезки вернул пустое изображение."); }
            return output;
        }
        private static Rect CreateCropRectangle(Mat input, int x, int y, int width, int height, bool clampToImage)
        {
            if (!clampToImage)
            {
                ValidateRectangleInsideImage(input, x, y, width, height);
                return new Rect(x, y, width, height);
            }
            if (x >= input.Width || y >= input.Height)
                throw new ArgumentException($"Начальная точка области ({x}, {y}) находится за границами изображения {input.Width} × {input.Height}.");
            int availableWidth = input.Width - x;
            int availableHeight = input.Height - y;
            int resultWidth = Math.Min(width, availableWidth);
            int resultHeight = Math.Min(height, availableHeight);
            if (resultWidth < 1 || resultHeight < 1)
                throw new ArgumentException("После ограничения по границам область обрезки получилась пустой.");
            return new Rect(x, y, resultWidth, resultHeight);
        }
        private static void ValidateRectangleInsideImage(Mat input, int x, int y, int width, int height)
        {
            long right = (long)x + width;
            long bottom = (long)y + height;
            if (x >= input.Width || y >= input.Height || right > input.Width || bottom > input.Height)
                throw new ArgumentException($"Область X={x}, Y={y}, Width={width}, Height={height} выходит за границы изображения {input.Width} × {input.Height}.");
        }
        private static void ValidateParameters(int x, int y, int width, int height)
        {
            if (x < 0) throw new ArgumentException("Координата X не может быть отрицательной.");
            if (y < 0) throw new ArgumentException("Координата Y не может быть отрицательной.");
            if (width < 1) throw new ArgumentException("Ширина области должна быть больше нуля.");
            if (height < 1) throw new ArgumentException("Высота области должна быть больше нуля.");
        }
    }
}