using System;
using OpenCvSharp;
namespace OpenCVStudio_v0
{
    public sealed class TranslateExecutor : IImageBlockExecutor
    {
        public string BlockType => "Translate";
        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 shiftX = projectManager.GetBlockParameter(block.Id, "ShiftX", 0);
            int shiftY = projectManager.GetBlockParameter(block.Id, "ShiftY", 0);
            bool expandCanvas = projectManager.GetBlockParameter(block.Id, "ExpandCanvas", false);
            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(shiftX, shiftY, interpolation, borderMode, borderValue);
            CalculateTransformation(input, shiftX, shiftY, expandCanvas, out OpenCvSharp.Size outputSize, out double matrixShiftX, out double matrixShiftY);
            using Mat transformationMatrix = CreateTransformationMatrix(matrixShiftX, matrixShiftY);
            Mat output = new Mat();
            try
            {
                Cv2.WarpAffine(input, output, transformationMatrix, outputSize, ConvertInterpolation(interpolation), ConvertBorderMode(borderMode), Scalar.All(borderValue));
                if (output.Empty()) throw new InvalidOperationException("Блок сдвига вернул пустое изображение.");
                return output;
            }
            catch { output.Dispose(); throw; }
        }
        private static Mat CreateTransformationMatrix(double shiftX, double shiftY)
        {
            Mat matrix = Mat.Eye(2, 3, MatType.CV_64FC1);
            matrix.Set(0, 2, shiftX);
            matrix.Set(1, 2, shiftY);
            return matrix;
        }
        private static void CalculateTransformation(Mat input, int shiftX, int shiftY, bool expandCanvas, out OpenCvSharp.Size outputSize, out double matrixShiftX, out double matrixShiftY)
        {
            if (!expandCanvas)
            {
                outputSize = input.Size();
                matrixShiftX = shiftX;
                matrixShiftY = shiftY;
                return;
            }
            long expandedWidth = (long)input.Width + Math.Abs((long)shiftX);
            long expandedHeight = (long)input.Height + Math.Abs((long)shiftY);
            if (expandedWidth > int.MaxValue || expandedHeight > int.MaxValue)
                throw new ArgumentException("Размер изображения после расширения холста слишком велик.");
            outputSize = new OpenCvSharp.Size((int)expandedWidth, (int)expandedHeight);
            matrixShiftX = Math.Max(shiftX, 0);
            matrixShiftY = Math.Max(shiftY, 0);
        }
        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(int shiftX, int shiftY, ResizeInterpolation interpolation, RotateBorderMode borderMode, double borderValue)
        {
            if (shiftX < -100000 || shiftX > 100000) throw new ArgumentException("Сдвиг X должен быть от -100000 до 100000.");
            if (shiftY < -100000 || shiftY > 100000) throw new ArgumentException("Сдвиг Y должен быть от -100000 до 100000.");
            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.");
        }
    }
}