using System;
using System.ComponentModel;

namespace OpenCVStudio_v0
{
    public sealed class GaussianBlurBlockProperties
    {
        private const int MinimumKernelSize = 1;
        private const int MaximumKernelSize = 999;

        private readonly ProjectManager projectManager;
        private readonly ProjectBlock projectBlock;

        private int kernelWidth;
        private int kernelHeight;

        private double sigmaX;
        private double sigmaY;

        private RotateBorderMode borderMode;

        public GaussianBlurBlockProperties(ProjectManager projectManager, ProjectBlock projectBlock)
        {
            this.projectManager = projectManager ?? throw new ArgumentNullException(nameof(projectManager));
            this.projectBlock = projectBlock ?? throw new ArgumentNullException(nameof(projectBlock));

            kernelWidth = projectManager.GetBlockParameter(projectBlock.Id, "KernelWidth", 5);
            kernelHeight = projectManager.GetBlockParameter(projectBlock.Id, "KernelHeight", 5);
            sigmaX = projectManager.GetBlockParameter(projectBlock.Id, "SigmaX", 0.0);
            sigmaY = projectManager.GetBlockParameter(projectBlock.Id, "SigmaY", 0.0);
            borderMode = projectManager.GetBlockParameter(projectBlock.Id, "BorderMode", RotateBorderMode.Reflect101);
        }

        [Category("Ядро")]
        [DisplayName("Ширина ядра")]
        [Description("Ширина ядра фильтра. Должна быть положительным нечётным числом.")]
        public int KernelWidth
        {
            get => kernelWidth;

            set
            {
                ValidateKernelSize(value, "Ширина ядра");

                if (kernelWidth == value)
                    return;

                kernelWidth = value;

                projectManager.SetBlockParameter(projectBlock.Id, "KernelWidth", value);
            }
        }

        [Category("Ядро")]
        [DisplayName("Высота ядра")]
        [Description("Высота ядра фильтра. Должна быть положительным нечётным числом.")]
        public int KernelHeight
        {
            get => kernelHeight;

            set
            {
                ValidateKernelSize(value, "Высота ядра");

                if (kernelHeight == value)
                    return;

                kernelHeight = value;

                projectManager.SetBlockParameter(projectBlock.Id, "KernelHeight", value);
            }
        }

        [Category("Гауссов фильтр")]
        [DisplayName("Sigma X")]
        [Description("Стандартное отклонение по горизонтали. 0 — рассчитать автоматически по размеру ядра.")]
        public double SigmaX
        {
            get => sigmaX;

            set
            {
                ValidateSigma(value, "Sigma X");

                if (Math.Abs(sigmaX - value) < double.Epsilon)
                    return;

                sigmaX = value;

                projectManager.SetBlockParameter(projectBlock.Id, "SigmaX", value);
            }
        }

        [Category("Гауссов фильтр")]
        [DisplayName("Sigma Y")]
        [Description("Стандартное отклонение по вертикали. 0 — использовать значение Sigma X.")]
        public double SigmaY
        {
            get => sigmaY;

            set
            {
                ValidateSigma(value, "Sigma Y");

                if (Math.Abs(sigmaY - value) < double.Epsilon)
                    return;

                sigmaY = value;

                projectManager.SetBlockParameter(projectBlock.Id, "SigmaY", value);
            }
        }

        [Category("Границы")]
        [DisplayName("Обработка границ")]
        [Description("Определяет способ обработки пикселей около краёв изображения.")]
        public RotateBorderMode BorderMode
        {
            get => borderMode;

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

                if (borderMode == value)
                    return;

                borderMode = value;

                projectManager.SetBlockParameter(projectBlock.Id, "BorderMode", value);
            }
        }

        [Category("Блок")]
        [DisplayName("Название")]
        [ReadOnly(true)]
        public string BlockName => projectBlock.Name;

        [Category("Блок")]
        [DisplayName("Тип")]
        [ReadOnly(true)]
        public string BlockType => projectBlock.BlockType;

        [Category("Блок")]
        [DisplayName("Позиция")]
        [ReadOnly(true)]
        public int Position => projectBlock.Order + 1;

        private static void ValidateKernelSize(int value, string parameterName)
        {
            if (value < MinimumKernelSize || value > MaximumKernelSize)
                throw new ArgumentOutOfRangeException(nameof(value), value, $"{parameterName} должна быть от {MinimumKernelSize} до {MaximumKernelSize}.");

            if (value % 2 == 0)
                throw new ArgumentException($"{parameterName} должна быть нечётной.", nameof(value));
        }

        private static void ValidateSigma(double value, string parameterName)
        {
            if (double.IsNaN(value) || double.IsInfinity(value) || value < 0.0 || value > 10000.0)
                throw new ArgumentOutOfRangeException(nameof(value), value, $"{parameterName} должна быть от 0 до 10000.");
        }
    }
}