using System;
using OpenCvSharp;

namespace OpenCVStudio_v0
{
    /// <summary>
    /// Выполняет поиск контуров на текущем изображении pipeline.
    ///
    /// Текущее изображение не изменяет.
    /// Найденные контуры сохраняет в PipelineContext.Contours.
    /// </summary>
    internal sealed class FindContoursExecutor : IPipelineBlockExecutor
    {
        public string BlockType => "FindContours";

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

            ValidateInputImage(context.Image, block);

            ContourRetrievalMode retrievalMode = projectManager.GetBlockParameter(block.Id, "RetrievalMode", ContourRetrievalMode.Tree);
            ContourApproximationMode approximationMode = projectManager.GetBlockParameter(block.Id, "ApproximationMode", ContourApproximationMode.Simple);

            RetrievalModes openCvRetrievalMode = ConvertRetrievalMode(retrievalMode);
            ContourApproximationModes openCvApproximationMode = ConvertApproximationMode(approximationMode);

            /*
             * FindContours получает отдельную копию изображения.
             *
             * Благодаря этому текущее изображение pipeline
             * гарантированно остаётся неизменным.
             */
            using Mat contourSource = context.Image.Clone();

            Cv2.FindContours(contourSource, out OpenCvSharp. Point[][] contours, out HierarchyIndex[] hierarchy, openCvRetrievalMode, openCvApproximationMode);

          

            context.ContourAreas = null;
            context.ArcLengths = null;
            context.BoundingRects = null;
            context.MinAreaRects = null;
            context.FitEllipses = null;

            /*
             * Новые данные заменяют результат
             * предыдущего блока FindContours.
             */
            context.Contours = new ContourData(contours, hierarchy);

            context.ResultText = $"Найдено контуров: {contours.Length}";
        }

        /// <summary>
        /// Проверяет изображение перед поиском контуров.
        /// </summary>
        private static void ValidateInputImage(Mat image, ProjectBlock block)
        {
            ArgumentNullException.ThrowIfNull(image);

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

            if (image.Channels() != 1)
                throw new InvalidOperationException($"Блок «{block.Name}» принимает только одноканальное изображение. Перед FindContours добавьте блок преобразования в оттенки серого или пороговой обработки.");

            if (image.Depth() != MatType.CV_8U)
                throw new InvalidOperationException($"Блок «{block.Name}» принимает только 8-битное одноканальное изображение CV_8UC1. Текущий тип: {image.Type()}.");
        }

        /// <summary>
        /// Преобразует наш режим получения контуров
        /// в перечисление OpenCvSharp.
        /// </summary>
        private static RetrievalModes ConvertRetrievalMode(ContourRetrievalMode mode)
        {
            return mode switch
            {
                ContourRetrievalMode.External => RetrievalModes.External,
                ContourRetrievalMode.List => RetrievalModes.List,
                ContourRetrievalMode.CComp => RetrievalModes.CComp,
                ContourRetrievalMode.Tree => RetrievalModes.Tree,
                _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Неизвестный режим получения контуров.")
            };
        }

        /// <summary>
        /// Преобразует наш режим аппроксимации
        /// в перечисление OpenCvSharp.
        /// </summary>
        private static ContourApproximationModes ConvertApproximationMode(ContourApproximationMode mode)
        {
            return mode switch
            {
                ContourApproximationMode.None => ContourApproximationModes.ApproxNone,
                ContourApproximationMode.Simple => ContourApproximationModes.ApproxSimple,
                ContourApproximationMode.TC89L1 => ContourApproximationModes.ApproxTC89L1,
                ContourApproximationMode.TC89KCOS => ContourApproximationModes.ApproxTC89KCOS,
                _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Неизвестный режим аппроксимации контуров.")
            };
        }
    }
}