using System;
using OpenCvSharp;

namespace OpenCVStudio_v0
{
    /// <summary>
    /// Переставляет каналы изображения с помощью Cv2.MixChannels.
    /// 
    /// Вход: цветное изображение (3 канала).
    /// Выход: цветное изображение с переставленными каналами.
    /// 
    /// Пример:
    /// Вход: [B, G, R]
    /// Channel0 = 2 (R)
    /// Channel1 = 1 (G)
    /// Channel2 = 0 (B)
    /// Выход: [R, G, B]
    /// </summary>
    public sealed class MixChannelsExecutor : IImageBlockExecutor
    {
        public string BlockType => "MixChannels";

        public Mat Execute(Mat input, ProjectBlock block, ProjectManager projectManager)
        {
            ArgumentNullException.ThrowIfNull(input);
            ArgumentNullException.ThrowIfNull(block);
            ArgumentNullException.ThrowIfNull(projectManager);

            if (input.Empty())
            {
                throw new ArgumentException("В блок MixChannels передано пустое изображение.", nameof(input));
            }

            if (input.Channels() != 3)
            {
                throw new InvalidOperationException($"Блок «{block.Name}» требует трёхканальное изображение.\n\nПолучено каналов: {input.Channels()}");
            }

            int channel0 = projectManager.GetBlockParameter(block.Id, "Channel0", 2);
            int channel1 = projectManager.GetBlockParameter(block.Id, "Channel1", 1);
            int channel2 = projectManager.GetBlockParameter(block.Id, "Channel2", 0);

            ValidateChannels(channel0, channel1, channel2, block);

            Mat output = new Mat();
            try
            {
                int[] fromTo = new int[]
                {
                    channel0, 0,
                    channel1, 1,
                    channel2, 2
                };

                Cv2.MixChannels(new Mat[] { input }, new Mat[] { output }, fromTo);

                if (output.Empty())
                {
                    throw new InvalidOperationException($"Блок «{block.Name}» вернул пустое изображение.");
                }

                return output;
            }
            catch
            {
                output.Dispose();
                throw;
            }
        }

        private static void ValidateChannels(int channel0, int channel1, int channel2, ProjectBlock block)
        {
            if (channel0 < 0 || channel0 > 2)
            {
                throw new InvalidOperationException($"Блок «{block.Name}»: канал 0 должен быть в диапазоне 0-2. Получено: {channel0}");
            }
            if (channel1 < 0 || channel1 > 2)
            {
                throw new InvalidOperationException($"Блок «{block.Name}»: канал 1 должен быть в диапазоне 0-2. Получено: {channel1}");
            }
            if (channel2 < 0 || channel2 > 2)
            {
                throw new InvalidOperationException($"Блок «{block.Name}»: канал 2 должен быть в диапазоне 0-2. Получено: {channel2}");
            }
        }
    }
}