using System;
using OpenCvSharp;

namespace OpenCVStudio_v0
{
    /// <summary>
    /// Аппроксимирует каждый найденный контур эллипсом.
    ///
    /// Изображение и исходные контуры не изменяются.
    /// Для расчёта эллипса контур должен содержать
    /// не менее пяти точек.
    /// </summary>
    internal sealed class FitEllipseExecutor : IPipelineBlockExecutor
    {
        public string BlockType => "FitEllipse";

        public void Execute(PipelineContext context, ProjectBlock block, ProjectManager projectManager)
        {
            ArgumentNullException.ThrowIfNull(context);
            ArgumentNullException.ThrowIfNull(block);
            ArgumentNullException.ThrowIfNull(projectManager);
            ValidateContext(context, block);
            ContourData contourData = context.Contours!;
            RotatedRect?[] ellipses = new RotatedRect?[contourData.Count];
            for (int contourIndex = 0; contourIndex < contourData.Count; contourIndex++)
            {
                OpenCvSharp.Point[] contour = contourData.Contours[contourIndex];
                ellipses[contourIndex] = CalculateEllipse(contour);
            }
            FitEllipseData result = new FitEllipseData(ellipses);
            context.FitEllipses = result;
            context.ResultText = CreateResultText(result);
        }

        private static RotatedRect? CalculateEllipse(OpenCvSharp.Point[]? contour)
        {
            if (contour == null || contour.Length < 5)
            {
                return null;
            }
            return Cv2.FitEllipse(contour);
        }

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

        private static string CreateResultText(FitEllipseData data)
        {
            if (data.Count == 0)
            {
                return "Контуры отсутствуют. Эллипсы не рассчитаны.";
            }
            if (data.ValidCount == 0)
            {
                return $"Получено контуров: {data.Count}. Эллипсы не рассчитаны: каждый контур содержит меньше 5 точек.";
            }
            int largestIndex = -1;
            RotatedRect largestEllipse = default;
            double largestArea = 0;
            for (int index = 0; index < data.Ellipses.Length; index++)
            {
                RotatedRect? ellipseValue = data.Ellipses[index];
                if (!ellipseValue.HasValue)
                {
                    continue;
                }
                RotatedRect ellipse = ellipseValue.Value;
                double area = Math.PI * ellipse.Size.Width * ellipse.Size.Height / 4.0;
                if (largestIndex >= 0 && area <= largestArea)
                {
                    continue;
                }
                largestIndex = index;
                largestEllipse = ellipse;
                largestArea = area;
            }
            return $"Рассчитано эллипсов: {data.ValidCount} из {data.Count}; крупнейший: контур {largestIndex}; центр X={largestEllipse.Center.X:F2}, Y={largestEllipse.Center.Y:F2}; оси {largestEllipse.Size.Width:F2} × {largestEllipse.Size.Height:F2}; угол {largestEllipse.Angle:F2}°.";
        }
    }
}