using System;
using OpenCvSharp;

namespace OpenCVStudio_v0
{
    /// <summary>
    /// Рисует контуры, сохранённые в PipelineContext.
    ///
    /// Исходное изображение напрямую не изменяется.
    /// Создаётся новое изображение, которое затем
    /// становится текущим изображением pipeline.
    /// </summary>
    internal sealed class DrawContoursExecutor : IPipelineBlockExecutor
    {
        public string BlockType => "DrawContours";

        public void Execute(PipelineContext context, ProjectBlock block, ProjectManager projectManager)
        {
            ArgumentNullException.ThrowIfNull(context);
            ArgumentNullException.ThrowIfNull(block);
            ArgumentNullException.ThrowIfNull(projectManager);

            ValidateContext(context, block);

            int blue = projectManager.GetBlockParameter(block.Id, "Blue", 0);
            int green = projectManager.GetBlockParameter(block.Id, "Green", 255);
            int red = projectManager.GetBlockParameter(block.Id, "Red", 0);
            int thickness = projectManager.GetBlockParameter(block.Id, "Thickness", 2);

            blue = Math.Clamp(blue, 0, 255);
            green = Math.Clamp(green, 0, 255);
            red = Math.Clamp(red, 0, 255);
            thickness = Math.Clamp(thickness, 1, 20);

            Scalar contourColor = new Scalar(blue, green, red);

            Mat resultImage = CreateColorImage(context.Image);

            try
            {
                ContourData contourData = context.Contours!;

                Cv2.DrawContours(resultImage, contourData.Contours, -1, contourColor, thickness, LineTypes.AntiAlias);

                context.ReplaceImage(resultImage);

                /*
                 * После ReplaceImage владение resultImage
                 * переходит PipelineContext.
                 */
                resultImage = null!;

                context.ResultText = $"Отрисовано контуров: {contourData.Count}";
            }
            finally
            {
                /*
                 * Если до ReplaceImage произошла ошибка,
                 * освобождаем созданное изображение здесь.
                 */
                resultImage?.Dispose();
            }
        }

        /// <summary>
        /// Проверяет наличие изображения и контуров.
        /// </summary>
        private static void ValidateContext(PipelineContext context, ProjectBlock block)
        {
            if (context.Image.Empty())
                throw new InvalidOperationException($"Блок «{block.Name}» получил пустое изображение.");

            if (context.Contours == null)
                throw new InvalidOperationException($"Перед блоком «{block.Name}» должен быть выполнен блок FindContours.");

            if (context.Contours.IsEmpty)
            {
                /*
                 * Отсутствие контуров не является ошибкой.
                 * Блок просто ничего не нарисует.
                 */
                return;
            }
        }

        /// <summary>
        /// Создаёт трёхканальную копию изображения,
        /// пригодную для цветного отображения контуров.
        /// </summary>
        private static Mat CreateColorImage(Mat source)
        {
            Mat result = new Mat();

            try
            {
                switch (source.Channels())
                {
                    case 1:
                        Cv2.CvtColor(source, result, ColorConversionCodes.GRAY2BGR);
                        break;

                    case 3:
                        source.CopyTo(result);
                        break;

                    case 4:
                        Cv2.CvtColor(source, result, ColorConversionCodes.BGRA2BGR);
                        break;

                    default:
                        throw new InvalidOperationException($"DrawContours поддерживает изображения с 1, 3 или 4 каналами. Текущее количество каналов: {source.Channels()}.");
                }

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