using CtrEditor.ObjetosSim;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;

namespace CtrEditor.Serialization
{
    /// <summary>
    /// Clase para gestionar qué propiedades deben ser locales en objetos globales
    /// </summary>
    public static class PropiedadesLocalesManager
    {
        // Definición de propiedades por defecto que se guardan localmente para todos los objetos
        private static readonly List<string> PropiedadesPorDefecto = new List<string>
        {
            "Left", "Top", "Ancho", "Alto"
        };

        // Diccionario que contiene conjuntos de propiedades locales por tipo de objeto
        private static readonly Dictionary<Type, List<string>> PropiedadesPorTipo = new Dictionary<Type, List<string>>();

        /// <summary>
        /// Registra un conjunto de propiedades locales para un tipo específico de objeto
        /// </summary>
        /// <typeparam name="T">Tipo de objeto (debe heredar de osBase)</typeparam>
        /// <param name="propiedades">Lista de nombres de propiedades a registrar como locales</param>
        public static void RegistrarPropiedadesParaTipo<T>(List<string> propiedades) where T : osBase
        {
            PropiedadesPorTipo[typeof(T)] = propiedades;
        }

        /// <summary>
        /// Obtiene la lista de propiedades que deben ser locales para un objeto concreto
        /// </summary>
        /// <param name="objeto">Objeto del que obtener las propiedades locales</param>
        /// <returns>Lista de nombres de propiedades</returns>
        public static List<string> ObtenerPropiedadesLocales(osBase objeto)
        {
            var resultado = new List<string>(PropiedadesPorDefecto);

            if (objeto != null && PropiedadesPorTipo.TryGetValue(objeto.GetType(), out var propiedadesExtra))
            {
                foreach (var prop in propiedadesExtra)
                {
                    if (!resultado.Contains(prop))
                        resultado.Add(prop);
                }
            }

            return resultado;
        }

        /// <summary>
        /// Crea un objeto dinámico que permite acceder a las propiedades locales de un objeto
        /// </summary>
        /// <param name="objeto">Objeto del que crear una vista dinámica</param>
        /// <returns>Objeto dinámico con propiedades locales</returns>
        public static dynamic CrearAccesoDinamico(osBase objeto)
        {
            return new PropiedadesLocalesDinamicas(objeto);
        }

        /// <summary>
        /// Clase interna que implementa un acceso dinámico a propiedades
        /// </summary>
        private class PropiedadesLocalesDinamicas : DynamicObject
        {
            private readonly osBase _objeto;
            private readonly List<string> _propiedadesLocales;

            public PropiedadesLocalesDinamicas(osBase objeto)
            {
                _objeto = objeto;
                _propiedadesLocales = ObtenerPropiedadesLocales(objeto);
            }

            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                var nombrePropiedad = binder.Name;

                if (_propiedadesLocales.Contains(nombrePropiedad))
                {
                    var propInfo = _objeto.GetType().GetProperty(nombrePropiedad);
                    if (propInfo != null && propInfo.CanRead)
                    {
                        result = propInfo.GetValue(_objeto);
                        return true;
                    }
                }

                result = null;
                return false;
            }

            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                var nombrePropiedad = binder.Name;

                if (_propiedadesLocales.Contains(nombrePropiedad))
                {
                    var propInfo = _objeto.GetType().GetProperty(nombrePropiedad);
                    if (propInfo != null && propInfo.CanWrite)
                    {
                        try
                        {
                            var valorConvertido = Convert.ChangeType(value, propInfo.PropertyType);
                            propInfo.SetValue(_objeto, valorConvertido);
                            return true;
                        }
                        catch (Exception)
                        {
                            // Silenciar errores de conversión
                        }
                    }
                }

                return false;
            }

            public override IEnumerable<string> GetDynamicMemberNames()
            {
                return _propiedadesLocales;
            }
        }
    }
}