using Newtonsoft.Json;
using System.Windows.Controls;
using CtrEditor.Simulacion;
using System.Reflection;
using System;
using System.Windows.Data;
using System.Windows;
using System.Windows.Media;
using CtrEditor.ObjetosSim.UserControls;
using System.Collections;
using System.Windows.Input;
using System.Windows.Input;
using System.Windows.Shapes;
using Xceed.Wpf.Toolkit.PropertyGrid;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using Xceed.Wpf.Toolkit;
using Xceed.Wpf.Toolkit.PropertyGrid.Editors;
using CtrEditor.ObjetosSim.Extraccion_Datos;
using System.Windows.Controls.Primitives;

namespace CtrEditor.ObjetosSim
{
    public static class UserControlFactory
    {
        public static UserControl? GetControlForType(Type tipoObjeto)
        {
            // Obtener el nombre del tipo de objeto
            string typeName = tipoObjeto.Name;

            // Cambiar las primeras dos letras de 'os' a 'uc'
            if (typeName.StartsWith("os"))
            {
                string newTypeName = "uc" + typeName.Substring(2);

                // Obtener el ensamblado donde se encuentra el tipo UserControl
                Assembly assembly = Assembly.GetExecutingAssembly();

                // Buscar el tipo en los ensamblados cargados
                Type? controlType = AppDomain.CurrentDomain.GetAssemblies()
                    .SelectMany(a => a.GetTypes())
                    .FirstOrDefault(t => t.Name == newTypeName);

                if (controlType != null && typeof(UserControl).IsAssignableFrom(controlType))
                {
                    // Crear una instancia del tipo encontrado
                    return (UserControl?)Activator.CreateInstance(controlType);
                }
            }

            return null;
        }

        public static osBase? GetInstanceForType(Type tipoObjeto)
        {
            // Verifica si el tipo pasado es un subtipo de osBase
            if (!typeof(osBase).IsAssignableFrom(tipoObjeto))
            {
                throw new ArgumentException("El tipo pasado no es un subtipo de osBase", nameof(tipoObjeto));
            }

            // Crear una instancia del tipo especificado
            osBase obj = (osBase?)Activator.CreateInstance(tipoObjeto);
            obj.InicializaNuevoObjeto();
            return obj;
        }

        public static osBase? CreateInstanceAndPopulate(Type tipoObjeto, string jsonString)
        {
            osBase? instance = GetInstanceForType(tipoObjeto);
            if (instance != null)
            {
                // Deserializa los datos desde jsonString y popula la instancia
                JsonConvert.PopulateObject(jsonString, instance);
            }
            return instance;
        }

        public static void AssignDatos(UserControl userControl, osBase datos, SimulationManagerBEPU simulationManager)
        {
            if (userControl is IDataContainer dataContainer)
            {
                dataContainer.Datos = datos;
                userControl.DataContext = datos;
                datos.VisualRepresentation = userControl;
                datos.simulationManager = simulationManager;
            }
        }

        public static void LimpiarPropiedadesosDatos(PropertyGrid propertyGrid)
        {
            // Forzar la actualización de bindings pendientes antes de limpiar
            ForzarActualizacionBindings(propertyGrid);

            // Clear previous properties
            propertyGrid.SelectedObject = null;
            propertyGrid.PropertyDefinitions.Clear();
        }

        public static void ForzarActualizacionBindings(PropertyGrid propertyGrid)
        {
            try
            {
                // Verificar si el PropertyGrid pertenece al PLCControl para no interferir con su edición
                var plcControl = FindParentOfType<LibS7Adv.PLCControl>(propertyGrid);
                if (plcControl != null)
                {
                    // Debug - log que encontramos el PLCControl
                    System.Diagnostics.Debug.WriteLine("PLCControl detectado - saltando forzado de foco");
                    System.IO.File.AppendAllText("c:\\temp\\debug_focus.txt", 
                        $"{DateTime.Now}: PLCControl detectado - saltando forzado de foco\n");
                    // Si es del PLCControl, solo forzar actualización de bindings sin robar el foco
                    var plcBindingExpression = BindingOperations.GetBindingExpression(propertyGrid, PropertyGrid.SelectedObjectProperty);
                    plcBindingExpression?.UpdateSource();
                    ForzarActualizacionControlesEditores(propertyGrid);
                    return;
                }

                // Debug - log que NO es PLCControl
                System.Diagnostics.Debug.WriteLine("NO es PLCControl - aplicando forzado de foco normal");
                System.IO.File.AppendAllText("c:\\temp\\debug_focus.txt", 
                    $"{DateTime.Now}: NO es PLCControl - aplicando forzado de foco normal\n");

                // Método 1: Forzar el foco fuera del PropertyGrid para activar LostFocus (solo para PropertyGrid principal)
                if (propertyGrid.IsKeyboardFocusWithin)
                {
                    // Crear un elemento temporal para robar el foco
                    var tempButton = new System.Windows.Controls.Button();
                    var parent = propertyGrid.Parent as Panel;
                    if (parent != null)
                    {
                        parent.Children.Add(tempButton);
                        tempButton.Focus();
                        parent.Children.Remove(tempButton);
                    }
                    else
                    {
                        // Alternativa: mover el foco al PropertyGrid mismo
                        propertyGrid.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
                    }
                }

                // Método 2: Forzar la actualización de todos los bindings del PropertyGrid
                var propertyGridBindingExpression = BindingOperations.GetBindingExpression(propertyGrid, PropertyGrid.SelectedObjectProperty);
                propertyGridBindingExpression?.UpdateSource();

                // Método 3: Buscar controles de edición activos y forzar su actualización
                ForzarActualizacionControlesEditores(propertyGrid);

                // Pequeña pausa para permitir que se procesen los eventos
                System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(
                    System.Windows.Threading.DispatcherPriority.Background,
                    new Action(() => { }));
            }
            catch (Exception ex)
            {
                // Log del error si es necesario, pero no interrumpir el flujo
                System.Diagnostics.Debug.WriteLine($"Error al forzar actualización de bindings: {ex.Message}");
            }
        }

        private static void ForzarActualizacionControlesEditores(DependencyObject parent)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);

                // Buscar TextBox, ComboBox, y otros controles de edición comunes
                if (child is TextBox textBox)
                {
                    var expression = textBox.GetBindingExpression(TextBox.TextProperty);
                    expression?.UpdateSource();
                }
                else if (child is ComboBox comboBox)
                {
                    var expression = comboBox.GetBindingExpression(ComboBox.SelectedValueProperty);
                    expression?.UpdateSource();
                    var textExpression = comboBox.GetBindingExpression(ComboBox.TextProperty);
                    textExpression?.UpdateSource();
                }
                else if (child is CheckBox checkBox)
                {
                    var expression = checkBox.GetBindingExpression(CheckBox.IsCheckedProperty);
                    expression?.UpdateSource();
                }

                // Recursión para buscar en controles hijos
                ForzarActualizacionControlesEditores(child);
            }
        }

        public static void CargarPropiedadesosDatos(osBase selectedObject, PropertyGrid propertyGrid)
        {
            // Forzar la actualización de bindings pendientes antes de cambiar el objeto
            ForzarActualizacionBindings(propertyGrid);

            // Limpia las propiedades previas
            propertyGrid.SelectedObject = null;
            propertyGrid.PropertyDefinitions.Clear();

            // Obtén las propiedades del objeto (excluyendo las que tengan HiddenAttribute)
            var properties = TypeDescriptor.GetProperties(selectedObject)
                                            .Cast<PropertyDescriptor>()
                                            .Where(prop => !prop.Attributes.OfType<HiddenAttribute>().Any())
                                            .OrderBy(prop => prop.Name);

            foreach (var property in properties)
            {
                if (property.Name.EndsWith("_oculto"))
                    continue;

                var displayNameAttr = property.Attributes.OfType<DisplayNameAttribute>().FirstOrDefault();
                var customNameAttr = property.Attributes.OfType<NameAttribute>().FirstOrDefault();

                string displayName;
                if (customNameAttr != null)
                {
                    displayName = customNameAttr.DisplayName;
                }
                else if (displayNameAttr != null)
                {
                    displayName = displayNameAttr.DisplayName;
                }
                else
                {
                    displayName = property.Name.Replace("_", " ");
                }

                var propertyDefinition = new PropertyDefinition
                {
                    TargetProperties = new[] { property.Name },
                    DisplayName = displayName
                };

                // Aquí se añaden todas las propiedades; para "ImagePath" no se requiere
                // ninguna configuración adicional, pues el EditorTemplateDefinition en XAML se
                // aplicará automáticamente.
                if (property.PropertyType == typeof(double) ||
                    property.PropertyType == typeof(float) ||
                    property.PropertyType == typeof(string) ||
                    property.PropertyType == typeof(int) ||
                    property.PropertyType == typeof(bool) ||
                    property.PropertyType == typeof(osBase) ||
                    property.PropertyType == typeof(osVMmotorSim) ||
                    property.PropertyType == typeof(osBuscarCoincidencias) ||
                    property.PropertyType == typeof(Color))
                {
                    propertyGrid.PropertyDefinitions.Add(propertyDefinition);
                }
            }

            propertyGrid.SelectedObject = selectedObject;
        }

        /// <summary>
        /// Busca un control padre de un tipo específico en el árbol visual
        /// </summary>
        /// <typeparam name="T">El tipo de control padre a buscar</typeparam>
        /// <param name="child">El control hijo desde donde empezar la búsqueda</param>
        /// <returns>El control padre del tipo especificado o null si no se encuentra</returns>
        private static T FindParentOfType<T>(DependencyObject child) where T : DependencyObject
        {
            DependencyObject parent = VisualTreeHelper.GetParent(child);
            
            if (parent == null)
                return null;
                
            if (parent is T parentOfType)
                return parentOfType;
                
            return FindParentOfType<T>(parent);
        }



    }
}

