using System;
using System.Collections.Generic;
using System.Text;

using System.ComponentModel;
namespace OpenCVStudio_v0
{
    public sealed class AdaptiveThresholdBlockProperties
    {
        private readonly ProjectManager projectManager; private readonly ProjectBlock projectBlock;
        private AdaptiveThresholdMethodMode method; private AdaptiveThresholdTypeMode thresholdType; private double maxValue; private int blockSize; private double c;
        public AdaptiveThresholdBlockProperties(ProjectManager projectManager, ProjectBlock projectBlock)
        {
            this.projectManager = projectManager ?? throw new ArgumentNullException(nameof(projectManager));
            this.projectBlock = projectBlock ?? throw new ArgumentNullException(nameof(projectBlock));
            method = projectManager.GetBlockParameter(projectBlock.Id, "Method", AdaptiveThresholdMethodMode.Gaussian);
            thresholdType = projectManager.GetBlockParameter(projectBlock.Id, "ThresholdType", AdaptiveThresholdTypeMode.Binary);
            maxValue = projectManager.GetBlockParameter(projectBlock.Id, "MaxValue", 255.0);
            blockSize = projectManager.GetBlockParameter(projectBlock.Id, "BlockSize", 11);
            c = projectManager.GetBlockParameter(projectBlock.Id, "C", 2.0);
        }
        [Category("Алгоритм")]
        [DisplayName("Метод")]
        [Description("Способ расчёта локального порога.")]
        public AdaptiveThresholdMethodMode Method
        {
            get => method;
            set
            {
                if (method == value) return;
                method = value;
                projectManager.SetBlockParameter(projectBlock.Id, "Method", value);
            }
        }
        [Category("Алгоритм")]
        [DisplayName("Тип порога")]
        [Description("Обычная или инвертированная бинаризация.")]
        public AdaptiveThresholdTypeMode ThresholdType
        {
            get => thresholdType;
            set
            {
                if (thresholdType == value) return;
                thresholdType = value;
                projectManager.SetBlockParameter(projectBlock.Id, "ThresholdType", value);
            }
        }
        [Category("Порог")]
        [DisplayName("Максимальное значение")]
        [Description("Значение белого пикселя после бинаризации.")]
        public double MaxValue
        {
            get => maxValue;
            set
            {
                ValidateMaxValue(value);
                if (Math.Abs(maxValue - value) < double.Epsilon) return;
                maxValue = value;
                projectManager.SetBlockParameter(projectBlock.Id, "MaxValue", value);
            }
        }
        [Category("Локальная область")]
        [DisplayName("Размер блока")]
        [Description("Нечётный размер локальной области, например 3, 5, 11 или 21.")]
        public int BlockSize
        {
            get => blockSize;
            set
            {
                ValidateBlockSize(value);
                if (blockSize == value) return;
                blockSize = value;
                projectManager.SetBlockParameter(projectBlock.Id, "BlockSize", value);
            }
        }
        [Category("Локальная область")]
        [DisplayName("Поправка C")]
        [Description("Значение, вычитаемое из вычисленного локального порога.")]
        public double C
        {
            get => c;
            set
            {
                ValidateC(value);
                if (Math.Abs(c - value) < double.Epsilon) return;
                c = value;
                projectManager.SetBlockParameter(projectBlock.Id, "C", 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 ValidateMaxValue(double value)
        {
            if (double.IsNaN(value) || double.IsInfinity(value) || value < 0 || value > 255)
                throw new ArgumentOutOfRangeException(nameof(value), value, "Максимальное значение должно быть от 0 до 255.");
        }
        private static void ValidateBlockSize(int value)
        {
            if (value < 3 || value > 255) throw new ArgumentOutOfRangeException(nameof(value), value, "Размер блока должен быть от 3 до 255.");
            if (value % 2 == 0) throw new ArgumentException("Размер блока должен быть нечётным.");
        }
        private static void ValidateC(double value)
        {
            if (double.IsNaN(value) || double.IsInfinity(value) || value < -255 || value > 255)
                throw new ArgumentOutOfRangeException(nameof(value), value, "Поправка C должна быть от -255 до 255.");
        }
    }
}