using System;
using System.Collections.Generic;
using System.Text;

using System;
using OpenCvSharp;
namespace OpenCVStudio_v0
{
    public sealed class ClaheExecutor : IImageBlockExecutor
    {
        public string BlockType => "CLAHE";
        public Mat Execute(Mat input, ProjectBlock block, ProjectManager projectManager)
        {
            ArgumentNullException.ThrowIfNull(input);
            ArgumentNullException.ThrowIfNull(block);
            ArgumentNullException.ThrowIfNull(projectManager);
            if (input.Empty()) throw new ArgumentException("В блок CLAHE передано пустое изображение.", nameof(input));
            double clipLimit = projectManager.GetBlockParameter(block.Id, "ClipLimit", 2.0);
            int tileGridWidth = projectManager.GetBlockParameter(block.Id, "TileGridWidth", 8);
            int tileGridHeight = projectManager.GetBlockParameter(block.Id, "TileGridHeight", 8);
            ValidateParameters(clipLimit, tileGridWidth, tileGridHeight);
            using Mat grayInput = PrepareGrayInput(input);
            Mat output = new Mat();
            try
            {
                using CLAHE clahe = Cv2.CreateCLAHE(clipLimit, new OpenCvSharp. Size(tileGridWidth, tileGridHeight));
                clahe.Apply(grayInput, output);
                if (output.Empty()) throw new InvalidOperationException("CLAHE вернул пустое изображение.");
                return output;
            }
            catch { output.Dispose(); throw; }
        }
        private static Mat PrepareGrayInput(Mat input)
        {
            if (input.Channels() == 1) return EnsureByteImage(input);
            Mat gray = new Mat();
            try
            {
                switch (input.Channels())
                {
                    case 3:
                        Cv2.CvtColor(input, gray, ColorConversionCodes.BGR2GRAY);
                        break;
                    case 4:
                        Cv2.CvtColor(input, gray, ColorConversionCodes.BGRA2GRAY);
                        break;
                    default:
                        throw new NotSupportedException($"CLAHE не поддерживает изображение с количеством каналов: {input.Channels()}.");
                }
                if (gray.Type() == MatType.CV_8UC1) return gray;
                Mat converted = new Mat();
                try
                {
                    gray.ConvertTo(converted, MatType.CV_8UC1);
                    return converted;
                }
                catch { converted.Dispose(); throw; }
                finally { gray.Dispose(); }
            }
            catch { gray.Dispose(); throw; }
        }
        private static Mat EnsureByteImage(Mat input)
        {
            if (input.Type() == MatType.CV_8UC1) return input.Clone();
            Mat converted = new Mat();
            try
            {
                input.ConvertTo(converted, MatType.CV_8UC1);
                return converted;
            }
            catch { converted.Dispose(); throw; }
        }
        private static void ValidateParameters(double clipLimit, int tileGridWidth, int tileGridHeight)
        {
            if (double.IsNaN(clipLimit) || double.IsInfinity(clipLimit) || clipLimit <= 0 || clipLimit > 100)
                throw new ArgumentException("ClipLimit должен быть больше 0 и не больше 100.");
            if (tileGridWidth < 1 || tileGridWidth > 100)
                throw new ArgumentException("Количество областей по ширине должно быть от 1 до 100.");
            if (tileGridHeight < 1 || tileGridHeight > 100)
                throw new ArgumentException("Количество областей по высоте должно быть от 1 до 100.");
        }
    }
}