using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Simulacion;
using System.IO;
using System.Windows.Media.Imaging;
using Tesseract;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV;
using System.Drawing;
using Image = System.Windows.Controls.Image;
using Point = System.Drawing.Point;
using Rectangle = System.Windows.Shapes.Rectangle;
using Size = System.Drawing.Size;
using Ookii.Dialogs.Wpf;
using Rect = System.Windows.Rect;
using System.ComponentModel;


namespace CtrEditor.ObjetosSim.Extraccion_Datos
{
    /// <summary>
    /// Interaction logic for ucBuscarCoincidencias.xaml
    /// </summary>
    /// 

    public partial class osBuscarCoincidencias : osBase, IosBase
    {
        [ObservableProperty]
        List<Rect> search_rectangles;

        public static string NombreClase()
        {
            return "Search Templates";
        }
        private string nombre = NombreClase();

        public override string Nombre
        {
            get => nombre;
            set => SetProperty(ref nombre, value);
        }

        [ObservableProperty]
        bool search_templates;

        partial void OnSearch_templatesChanged(bool oldValue, bool newValue)
        {
            if (Search_templates)
                BuscarCoincidencias();
            Search_templates = false;
        }

        [ObservableProperty]
        bool export_ocr;

        [ObservableProperty]
        string text_export_ocr;

        partial void OnExport_ocrChanged(bool value)
        {
            if (Export_ocr)
            {
                Text_export_ocr = "";
                if (!string.IsNullOrEmpty(Nombre) && _mainViewModel != null)
                {
                    foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables)
                    {
                        if (objetoSimulable != this && objetoSimulable.Group_Panel == Nombre)
                        {
                            if (objetoSimulable is osExtraccionTag osExtraccionTag)
                            {
                                osExtraccionTag.CaptureImageAreaAndDoOCR();
                                Text_export_ocr += osExtraccionTag.Tag_extract;
                            }
                        }
                    }
                }
            }
            Export_ocr = false;
        }

        public override void TopChanging(float oldValue, float newValue)
        {
            UpdateChildsTop(newValue - oldValue);
        }
        public override void LeftChanging(float oldValue, float newValue)
        {
            UpdateChildsLeft(newValue - oldValue);
        }

        [ObservableProperty]
        [property: Description("Width of the object.")]
        [property: Category("Position:")]
        public float ancho;

        [ObservableProperty]
        [property: Description("Height of the object.")]
        [property: Category("Position:")]
        public float alto;

        [ObservableProperty]
        public float angulo;

        [ObservableProperty]
        string tag_extract;

        [ObservableProperty]
        string clase;

        [ObservableProperty]
        string tag_name;

        [ObservableProperty]
        float opacity_oculto;

        [ObservableProperty]
        bool show_debug_ocr;

        [ObservableProperty]
        float threshold;

        [ObservableProperty]
        float coincidencias;


        public osBuscarCoincidencias()
        {
            Ancho = 1;
            Alto = 1;
            Angulo = 0;
            Opacity_oculto = 0.1f;
            Threshold = 0.6f;
        }

        private void ShowPreviewWindow(Stream imageStream)
        {
            // Create a new window for preview
            Window previewWindow = new Window
            {
                Title = "Preview Captured Image",
                Width = 500,
                Height = 500,
                Content = new Image
                {
                    Source = BitmapFrame.Create(imageStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad),
                    Stretch = Stretch.Uniform
                }
            };

            previewWindow.ShowDialog();
        }

        private async void BuscarCoincidencias()
        {
            var progressDialog = new ProgressDialog
            {
                WindowTitle = "Procesando",
                Text = "Buscando coincidencias...",
                ShowTimeRemaining = true,
                ShowCancelButton = false
            };
            progressDialog.DoWork += (sender, e) => BuscarCoincidenciasAsync(progressDialog);
            progressDialog.RunWorkerCompleted += (sender, e) =>
            {
                if (e.Error != null)
                {
                    MessageBox.Show(e.Error.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            };

            progressDialog.Show();
        }



        private void BuscarCoincidenciasAsync(ProgressDialog progressDialog)
        {
            // Reset the Canvas children
            Application.Current.Dispatcher.Invoke(() =>
            {
                var clearShapes = _mainViewModel.MainCanvas.Children.OfType<System.Windows.Shapes.Shape>().Where(s => s.Tag as string == "BuscarCoincidencias").ToList();
                foreach (var shape in clearShapes)
                {
                    _mainViewModel.MainCanvas.Children.Remove(shape);
                }
                if (_mainViewModel?.MainCanvas.Children[0] is Image imagenDeFondo)
                {
                    // Asegurarse de que la imagen origen está disponible
                    if (imagenDeFondo.Source is BitmapSource bitmapSource)
                    {
                        progressDialog.ReportProgress(10);
                        // Obtener los DPI de la imagen original
                        float originalDpiX = (float)bitmapSource.DpiX;
                        float originalDpiY = (float)bitmapSource.DpiY;

                        // Estándar DPI en el que el Canvas renderiza la imagen
                        float canvasDpiX = 96; // WPF usually renders at 96 DPI
                        float canvasDpiY = 96;

                        // Calcular el ratio de escala entre el Canvas y la imagen original
                        float scaleFactorX = originalDpiX / canvasDpiX;
                        float scaleFactorY = originalDpiY / canvasDpiY;

                        // Ajustar las coordenadas de recorte en función del ratio de escala
                        int x = (int)MeterToPixels(Left * scaleFactorX);
                        int y = (int)MeterToPixels(Top * scaleFactorY);
                        int width = (int)MeterToPixels(Ancho * scaleFactorX);
                        int height = (int)MeterToPixels(Alto * scaleFactorY);

                        // Validar y ajustar el tamaño del recorte para que se mantenga dentro de los límites de la imagen
                        if (x < 0) x = 0;
                        if (y < 0) y = 0;
                        if (x + width > bitmapSource.PixelWidth) width = bitmapSource.PixelWidth - x;
                        if (y + height > bitmapSource.PixelHeight) height = bitmapSource.PixelHeight - y;

                        // Recortar el área deseada utilizando las coordenadas ajustadas
                        CroppedBitmap croppedBitmap = new CroppedBitmap(bitmapSource, new Int32Rect(x, y, width, height));

                        // Convertir CroppedBitmap a Mat directamente
                        Mat templateMat = BitmapSourceToMat(croppedBitmap);

                        int scale = 4;

                        // Convertir la plantilla a escala de grises y redimensionarla
                        Mat templateGray = new Mat();
                        CvInvoke.CvtColor(templateMat, templateGray, ColorConversion.Bgr2Gray);
                        Mat templateGrayResized = new Mat();
                        CvInvoke.Resize(templateGray, templateGrayResized, new Size(templateGray.Width / scale, templateGray.Height / scale), 0, 0, Inter.Linear);
                        progressDialog.ReportProgress(20);
                        // Cargar la imagen principal completa en un Mat
                        Mat mainImageMat = BitmapSourceToMat(bitmapSource);

                        // Convertir la imagen principal a escala de grises y redimensionarla
                        Mat mainImageGray = new Mat();
                        CvInvoke.CvtColor(mainImageMat, mainImageGray, ColorConversion.Bgr2Gray);
                        Mat mainImageGrayResized = new Mat();
                        CvInvoke.Resize(mainImageGray, mainImageGrayResized, new Size(mainImageGray.Width / scale, mainImageGray.Height / scale), 0, 0, Inter.Linear);

                        progressDialog.ReportProgress(50);

                        // Realizar la coincidencia de plantillas
                        Mat result = new Mat();
                        CvInvoke.MatchTemplate(mainImageGray, templateGray, result, TemplateMatchingType.CcoeffNormed);

                        // Establecer un umbral de coincidencia
                        if (Threshold < 0.4)
                            Threshold = 0.4f;
                        double threshold = Threshold;

                        int ConteoPositivos = 0;

                        // Lista para mantener áreas ya aceptadas
                        List<Rectangle> acceptedRectangles = new List<Rectangle>();

                        if (search_rectangles != null)
                            search_rectangles.Clear();
                        else
                            search_rectangles = new List<Rect>();

                        // Obtener los puntos que superan el umbral
                        float[] resultArray = result.GetData(false) as float[];
                        if (resultArray != null)
                        {
                            for (int i = 0; i < resultArray.Length; i++)
                            {
                                if (resultArray[i] >= threshold)
                                {
                                    int row = i / result.Cols;
                                    int col = i % result.Cols;

                                    Rect newRect = new Rect();
                                    newRect.X = col / scaleFactorX;
                                    newRect.Y = row / scaleFactorY;
                                    newRect.Width = width / scaleFactorX;
                                    newRect.Height = height / scaleFactorY;

                                    // Crear un rectángulo para la coincidencia actual
                                    Rectangle matchRect = new Rectangle
                                    {
                                        Stroke = new SolidColorBrush(Colors.Red),
                                        StrokeThickness = 2,
                                        Width = newRect.Width,
                                        Height = newRect.Height,
                                        Tag = "BuscarCoincidencias"
                                    };

                                    Canvas.SetLeft(matchRect, newRect.X);
                                    Canvas.SetTop(matchRect, newRect.Y);

                                    // Verificar si la coincidencia actual está dentro de algún rectángulo aceptado
                                    bool isOverlap = acceptedRectangles.Any(r =>
                                        new Rect(Canvas.GetLeft(r), Canvas.GetTop(r), r.Width, r.Height).IntersectsWith(
                                            new Rect(Canvas.GetLeft(matchRect), Canvas.GetTop(matchRect), matchRect.Width, matchRect.Height)
                                        ));

                                    // Si no hay superposición, agregar el rectángulo al Canvas y a la lista de aceptados
                                    if (!isOverlap)
                                    {
                                        Canvas.SetZIndex(matchRect, 40);
                                        _mainViewModel.MainCanvas.Children.Add(matchRect);
                                        acceptedRectangles.Add(matchRect);
                                        search_rectangles.Add(newRect);

                                        ConteoPositivos++;
                                        Coincidencias = ConteoPositivos;
                                        progressDialog.ReportProgress(90);
                                        if (ConteoPositivos > 20)
                                            return;
                                    }
                                }
                            }
                        }
                    }
                }
            });
        }


        // Método para convertir BitmapSource a Mat
        private Mat BitmapSourceToMat(BitmapSource bitmapSource)
        {
            Bitmap bitmap;
            using (MemoryStream outStream = new MemoryStream())
            {
                BitmapEncoder enc = new BmpBitmapEncoder();
                enc.Frames.Add(BitmapFrame.Create(bitmapSource));
                enc.Save(outStream);
                bitmap = new Bitmap(outStream);
            }
            return bitmap.ToMat();
        }

        public override void ucLoaded()
        {
            // El UserControl ya se ha cargado y podemos obtener las coordenadas para 
            // crear el objeto de simulacion
            base.ucLoaded();

        }

    }

    public partial class ucBuscarCoincidencias : UserControl, IDataContainer
    {
        public osBase? Datos { get; set; }

        public ucBuscarCoincidencias()
        {
            InitializeComponent();
            this.Loaded += OnLoaded;
            this.Unloaded += OnUnloaded;
        }
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            Datos?.ucLoaded();
        }
        private void OnUnloaded(object sender, RoutedEventArgs e)
        {
            Datos?.ucUnLoaded();
        }
        public void Resize(float width, float height)
        {
            if (Datos is osBuscarCoincidencias datos)
            {
                datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
                datos.Alto = PixelToMeter.Instance.calc.PixelsToMeters(height);
            }
        }
        public void Move(float LeftPixels, float TopPixels)
        {
            if (Datos != null)
            {
                Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
                Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
            }
        }
        public void Rotate(float Angle)
        {
            if (Datos != null)
                if (Datos is osBuscarCoincidencias datos)
                    datos.Angulo = Angle;
        }
        public void Highlight(bool State) { }
        public int ZIndex()
        {
            return 1;
        }

    }
}



