using System;
using System.Windows.Media;
using OpenCvSharp;
namespace OpenCVStudio_v0
{
    public sealed class RotateExecutor : IImageBlockExecutor
    {
        public string BlockType => "Rotate";
        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));
            double angle = projectManager.GetBlockParameter(block.Id, "Angle", 0.0);
            double scale = projectManager.GetBlockParameter(block.Id, "Scale", 1.0);
            bool expandCanvas = projectManager.GetBlockParameter(block.Id, "ExpandCanvas", true);
            ResizeInterpolation interpolation = projectManager.GetBlockParameter(block.Id, "Interpolation", ResizeInterpolation.Linear);
            RotateBorderMode borderMode = projectManager.GetBlockParameter(block.Id, "BorderMode", RotateBorderMode.Constant);
            double borderValue = projectManager.GetBlockParameter(block.Id, "BorderValue", 0.0);
            ValidateParameters(angle, scale, interpolation, borderMode, borderValue);
            Point2f center = new Point2f((input.Width - 1) / 2.0f, (input.Height - 1) / 2.0f);
            using Mat rotationMatrix = Cv2.GetRotationMatrix2D(center, angle, scale);
            OpenCvSharp. Size outputSize;
            if (expandCanvas)
            {
                outputSize = CalculateExpandedSize(input, rotationMatrix);
                AdjustTranslation(rotationMatrix, center, outputSize);
            }
            else outputSize = input.Size();
            InterpolationFlags interpolationFlag = ConvertInterpolation(interpolation);
            BorderTypes openCvBorderMode = ConvertBorderMode(borderMode);
            Mat output = new Mat();
            try
            {
                Cv2.WarpAffine(input, output, rotationMatrix, outputSize, interpolationFlag, openCvBorderMode, Scalar.All(borderValue));
                if (output.Empty()) throw new InvalidOperationException("Блок поворота вернул пустое изображение.");
                return output;
            }
            catch { output.Dispose(); throw; }
        }
        private static OpenCvSharp. Size CalculateExpandedSize(Mat input, Mat rotationMatrix)
        {
            double cos = Math.Abs(rotationMatrix.At<double>(0, 0));
            double sin = Math.Abs(rotationMatrix.At<double>(0, 1));
            int newWidth = Math.Max(1, (int)Math.Ceiling(input.Height * sin + input.Width * cos));
            int newHeight = Math.Max(1, (int)Math.Ceiling(input.Height * cos + input.Width * sin));
            return new OpenCvSharp. Size(newWidth, newHeight);
        }
        private static void AdjustTranslation(Mat rotationMatrix, Point2f originalCenter, OpenCvSharp.Size outputSize)
        {
            double currentX = rotationMatrix.At<double>(0, 2);
            double currentY = rotationMatrix.At<double>(1, 2);
            double newCenterX = (outputSize.Width - 1) / 2.0;
            double newCenterY = (outputSize.Height - 1) / 2.0;
            rotationMatrix.Set(0, 2, currentX + newCenterX - originalCenter.X);
            rotationMatrix.Set(1, 2, currentY + newCenterY - originalCenter.Y);
        }
        private static InterpolationFlags ConvertInterpolation(ResizeInterpolation interpolation)
        {
            return interpolation switch
            {
                ResizeInterpolation.Nearest => InterpolationFlags.Nearest,
                ResizeInterpolation.Linear => InterpolationFlags.Linear,
                ResizeInterpolation.Cubic => InterpolationFlags.Cubic,
                ResizeInterpolation.Area => InterpolationFlags.Area,
                ResizeInterpolation.Lanczos4 => InterpolationFlags.Lanczos4,
                _ => throw new ArgumentOutOfRangeException(nameof(interpolation), interpolation, "Неизвестный режим интерполяции.")
            };
        }
        private static BorderTypes ConvertBorderMode(RotateBorderMode borderMode)
        {
            return borderMode switch
            {
                RotateBorderMode.Constant => BorderTypes.Constant,
                RotateBorderMode.Replicate => BorderTypes.Replicate,
                RotateBorderMode.Reflect => BorderTypes.Reflect,
                RotateBorderMode.Reflect101 => BorderTypes.Reflect101,
                _ => throw new ArgumentOutOfRangeException(nameof(borderMode), borderMode, "Неизвестный режим заполнения границ.")
            };
        }
        private static void ValidateParameters(double angle, double scale, ResizeInterpolation interpolation, RotateBorderMode borderMode, double borderValue)
        {
            if (double.IsNaN(angle) || double.IsInfinity(angle)) throw new ArgumentException("Угол содержит недопустимое значение.");
            if (double.IsNaN(scale) || double.IsInfinity(scale) || scale <= 0.0 || scale > 100.0)
                throw new ArgumentException("Масштаб должен быть больше 0 и не превышать 100.");
            if (!Enum.IsDefined(interpolation)) throw new ArgumentOutOfRangeException(nameof(interpolation), interpolation, "Неизвестный режим интерполяции.");
            if (!Enum.IsDefined(borderMode)) throw new ArgumentOutOfRangeException(nameof(borderMode), borderMode, "Неизвестный режим заполнения границ.");
            if (double.IsNaN(borderValue) || double.IsInfinity(borderValue) || borderValue < 0.0 || borderValue > 255.0)
                throw new ArgumentException("Значение фона должно быть от 0 до 255.");
        }
    }
}