namespace OpenCVStudio_v0
{
    /// <summary>
    /// Определяет, в какой слот PipelineContext
    /// источник должен записать изображение.
    /// </summary>
    public enum SourceImageSlot
    {
        /// <summary>
        /// Блок не является источником изображения.
        /// </summary>
        None = 0,

        /// <summary>
        /// Основное изображение pipeline.
        /// </summary>
        Main = 10,

        /// <summary>
        /// Второе изображение для двухвходовых операций.
        /// </summary>
        Second = 20
    }

    /// <summary>
    /// Описание типа блока, доступного в библиотеке.
    /// </summary>
    public sealed class BlockDescriptor
    {
        public string BlockType { get; }
        public string DisplayName { get; }
        public string Description { get; }
        public BlockCategory Category { get; }
        public BlockDataType InputType { get; }
        public BlockDataType OutputType { get; }
        /// <summary>
        /// Реализован ли исполнитель блока.
        /// Не реализованный блок отображается в каталоге,
        /// но его нельзя добавить в pipeline.
        /// </summary>
        public bool IsImplemented { get; }
        /// <summary>
        /// Разрешено ли добавлять несколько блоков
        /// одного типа в текущий проект.
        /// </summary>
        public bool AllowMultiple { get; }
        /// <summary>
        /// Разрешено ли пользователю перемещать блок
        /// кнопками вверх и вниз.
        /// </summary>
        public bool CanMove { get; }
        /// <summary>
        /// Нужно ли показывать на блоке кнопку
        /// выбора файла изображения.
        /// </summary>
        public bool HasFileSelection { get; }
        /// <summary>
        /// Слот изображения, заполняемый источником.
        /// None означает, что блок не является источником.
        /// </summary>
        public SourceImageSlot SourceSlot { get; }
        /// <summary>
        /// Является ли блок закреплённым источником.
        /// </summary>
        public bool IsImageSource => SourceSlot != SourceImageSlot.None;

        public BlockDescriptor(string blockType, string displayName, string description, BlockCategory category, BlockDataType inputType, BlockDataType outputType, bool isImplemented = false, bool allowMultiple = true, bool canMove = true, bool hasFileSelection = false, SourceImageSlot sourceSlot = SourceImageSlot.None)
        {
            if (string.IsNullOrWhiteSpace(blockType))
            {
                throw new ArgumentException("Не указан тип блока.", nameof(blockType));
            }
            if (string.IsNullOrWhiteSpace(displayName))
            {
                throw new ArgumentException("Не указано отображаемое имя блока.", nameof(displayName));
            }
            BlockType = blockType.Trim();
            DisplayName = displayName.Trim();
            Description = description?.Trim() ?? string.Empty;
            Category = category;
            InputType = inputType;
            OutputType = outputType;
            IsImplemented = isImplemented;
            AllowMultiple = allowMultiple;
            CanMove = canMove;
            HasFileSelection = hasFileSelection;
            SourceSlot = sourceSlot;
            ValidateSourceConfiguration();
        }

        private void ValidateSourceConfiguration()
        {
            if (SourceSlot == SourceImageSlot.None)
                return;
            if (InputType != BlockDataType.None)
            {
                throw new ArgumentException($"Источник «{DisplayName}» не должен иметь входной тип данных.");
            }
            if (OutputType != BlockDataType.Image)
            {
                throw new ArgumentException($"Источник «{DisplayName}» должен выдавать изображение.");
            }
            // Только основной источник фиксирован
            if (SourceSlot == SourceImageSlot.Main && CanMove)
            {
                throw new ArgumentException($"Основной источник «{DisplayName}» не должен быть перемещаемым.");
            }
            // Только основной источник должен быть единственным
            if (SourceSlot == SourceImageSlot.Main && AllowMultiple)
            {
                throw new ArgumentException($"Основной источник «{DisplayName}» не должен разрешать несколько экземпляров.");
            }
            if (!HasFileSelection)
            {
                throw new ArgumentException($"Источник «{DisplayName}» должен иметь выбор файла.");
            }
        }

        public override string ToString()
        {
            string status = IsImplemented ? string.Empty : " [позже]";
            return $"{DisplayName}{status}";
        }
    }
}