using System;
using System.ComponentModel;

namespace OpenCVStudio_v0
{
    /// <summary>
    /// Параметры блока InRange.
    /// </summary>
    public sealed class InRangeBlockProperties
    {
        private readonly ProjectManager projectManager;
        private readonly ProjectBlock projectBlock;
        private double lower0;
        private double lower1;
        private double lower2;
        private double lower3;
        private double upper0;
        private double upper1;
        private double upper2;
        private double upper3;

        public InRangeBlockProperties(ProjectManager projectManager, ProjectBlock projectBlock)
        {
            this.projectManager = projectManager ?? throw new ArgumentNullException(nameof(projectManager));
            this.projectBlock = projectBlock ?? throw new ArgumentNullException(nameof(projectBlock));
            lower0 = projectManager.GetBlockParameter(projectBlock.Id, "Lower0", 0.0);
            lower1 = projectManager.GetBlockParameter(projectBlock.Id, "Lower1", 0.0);
            lower2 = projectManager.GetBlockParameter(projectBlock.Id, "Lower2", 0.0);
            lower3 = projectManager.GetBlockParameter(projectBlock.Id, "Lower3", 0.0);
            upper0 = projectManager.GetBlockParameter(projectBlock.Id, "Upper0", 255.0);
            upper1 = projectManager.GetBlockParameter(projectBlock.Id, "Upper1", 255.0);
            upper2 = projectManager.GetBlockParameter(projectBlock.Id, "Upper2", 255.0);
            upper3 = projectManager.GetBlockParameter(projectBlock.Id, "Upper3", 255.0);
        }

        [Category("Канал 0")]
        [DisplayName("Нижняя граница")]
        [Description("Минимальное допустимое значение канала 0. B для BGR, H для HSV, Gray для серого.")]
        [DefaultValue(0.0)]
        public double Lower0
        {
            get => lower0;
            set => SetRangeValue(ref lower0, value, "Lower0");
        }

        [Category("Канал 0")]
        [DisplayName("Верхняя граница")]
        [Description("Максимальное допустимое значение канала 0.")]
        [DefaultValue(255.0)]
        public double Upper0
        {
            get => upper0;
            set => SetRangeValue(ref upper0, value, "Upper0");
        }

        [Category("Канал 1")]
        [DisplayName("Нижняя граница")]
        [Description("Минимальное допустимое значение канала 1. G для BGR, S для HSV.")]
        [DefaultValue(0.0)]
        public double Lower1
        {
            get => lower1;
            set => SetRangeValue(ref lower1, value, "Lower1");
        }

        [Category("Канал 1")]
        [DisplayName("Верхняя граница")]
        [Description("Максимальное допустимое значение канала 1.")]
        [DefaultValue(255.0)]
        public double Upper1
        {
            get => upper1;
            set => SetRangeValue(ref upper1, value, "Upper1");
        }

        [Category("Канал 2")]
        [DisplayName("Нижняя граница")]
        [Description("Минимальное допустимое значение канала 2. R для BGR, V для HSV.")]
        [DefaultValue(0.0)]
        public double Lower2
        {
            get => lower2;
            set => SetRangeValue(ref lower2, value, "Lower2");
        }

        [Category("Канал 2")]
        [DisplayName("Верхняя граница")]
        [Description("Максимальное допустимое значение канала 2.")]
        [DefaultValue(255.0)]
        public double Upper2
        {
            get => upper2;
            set => SetRangeValue(ref upper2, value, "Upper2");
        }

        [Category("Канал 3")]
        [DisplayName("Нижняя граница")]
        [Description("Минимальное допустимое значение канала 3. Обычно используется для Alpha.")]
        [DefaultValue(0.0)]
        public double Lower3
        {
            get => lower3;
            set => SetRangeValue(ref lower3, value, "Lower3");
        }

        [Category("Канал 3")]
        [DisplayName("Верхняя граница")]
        [Description("Максимальное допустимое значение канала 3.")]
        [DefaultValue(255.0)]
        public double Upper3
        {
            get => upper3;
            set => SetRangeValue(ref upper3, value, "Upper3");
        }

        [Category("Подсказка")]
        [DisplayName("BGR")]
        [ReadOnly(true)]
        public string BgrHint => "Канал 0 = B, канал 1 = G, канал 2 = R";

        [Category("Подсказка")]
        [DisplayName("HSV")]
        [ReadOnly(true)]
        public string HsvHint => "Канал 0 = H, канал 1 = S, канал 2 = V";

        [Category("Подсказка")]
        [DisplayName("Результат")]
        [ReadOnly(true)]
        public string ResultHint => "Бинарная маска CV_8UC1: 0 или 255";

        [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 void SetRangeValue(ref double field, double value, string parameterName)
        {
            double correctedValue = Math.Clamp(value, -1000000.0, 1000000.0);
            if (Math.Abs(field - correctedValue) < double.Epsilon)
            {
                return;
            }
            field = correctedValue;
            projectManager.SetBlockParameter(projectBlock.Id, parameterName, correctedValue);
        }
    }
}