using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows;
using System.ComponentModel;
using System.Reflection;
using Xceed.Wpf.Toolkit.PropertyGrid.Editors;
using Xceed.Wpf.Toolkit;
using Xceed.Wpf.Toolkit.PropertyGrid;
using CtrEditor.ObjetosSim.Extraccion_Datos;
using CtrEditor.ObjetosSim;
using System.Collections.ObjectModel;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using System.Collections;
using CommunityToolkit.Mvvm.ComponentModel;

namespace CtrEditor
{

    public class ConnectStateToBtnTextConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (bool)value ? "Desconectar PLC" : "Conectar PLC";
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class ConnectStateToImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (bool)value ? "Icons/disconnect.png" : "Icons/connect.png";
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }


    public class ListItemPropertyDescriptor<T> : PropertyDescriptor
    {
        private readonly IList<T> owner;
        private readonly int index;

        public ListItemPropertyDescriptor(IList<T> owner, int index) : base($"[{index}]", null)
        {
            this.owner = owner;
            this.index = index;

        }

        public override AttributeCollection Attributes
        {
            get
            {
                var attributes = TypeDescriptor.GetAttributes(GetValue(null), false);
                //If the Xceed expandable object attribute is not applied then apply it
                if (!attributes.OfType<ExpandableObjectAttribute>().Any())
                {
                    attributes = AddAttribute(new ExpandableObjectAttribute(), attributes);
                }

                //set the xceed order attribute
                attributes = AddAttribute(new PropertyOrderAttribute(index), attributes);

                return attributes;
            }
        }
        private AttributeCollection AddAttribute(Attribute newAttribute, AttributeCollection oldAttributes)
        {
            Attribute[] newAttributes = new Attribute[oldAttributes.Count + 1];
            oldAttributes.CopyTo(newAttributes, 1);
            newAttributes[0] = newAttribute;

            return new AttributeCollection(newAttributes);
        }

        public override bool CanResetValue(object component)
        {
            return false;
        }

        public override object GetValue(object component)
        {
            return Value;
        }

        private T Value
          => owner[index];

        public override void ResetValue(object component)
        {
            throw new NotImplementedException();
        }

        public override void SetValue(object component, object value)
        {
            owner[index] = (T)value;
        }

        public override bool ShouldSerializeValue(object component)
        {
            return false;
        }

        public override Type ComponentType
          => owner.GetType();

        public override bool IsReadOnly
          => false;

        public override Type PropertyType
          => Value?.GetType();

    }
    public class MyExpandableIListConverter<T> : ExpandableObjectConverter
    {
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            if (value is IList<T> list)
            {
                PropertyDescriptorCollection propDescriptions = new PropertyDescriptorCollection(null);
                IEnumerator enumerator = list.GetEnumerator();
                int counter = -1;
                while (enumerator.MoveNext())
                {
                    counter++;
                    propDescriptions.Add(new ListItemPropertyDescriptor<T>(list, counter));

                }
                return propDescriptions;
            }
            else
            {
                return base.GetProperties(context, value, attributes);
            }
        }
    }

    public static class TypeDecorationManager
    {
        public static void AddExpandableObjectConverter(Type T)
        {
            TypeDescriptor.AddAttributes(T, new TypeConverterAttribute(typeof(ExpandableObjectConverter)));
            TypeDescriptor.AddAttributes(T, new ExpandableObjectAttribute());
        }

        public static void AddExpandableIListConverter<I>(Type T)
        {
            TypeDescriptor.AddAttributes(T, new TypeConverterAttribute(typeof(MyExpandableIListConverter<I>)));
            TypeDescriptor.AddAttributes(T, new ExpandableObjectAttribute());
        }
    }

    public class SubclassFilterConverter : IValueConverter
    {
        public Type TargetType { get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is ObservableCollection<osBase> collection && TargetType != null)
            {
                var filteredCollection = new ObservableCollection<osBase>(collection.Where(x => x.GetType() == TargetType));
                return filteredCollection;
            }
            return null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }


    public class BoolToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is bool boolValue)
            {
                return boolValue ? Visibility.Visible : Visibility.Collapsed;
            }
            return Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is Visibility visibility)
            {
                return visibility == Visibility.Visible;
            }
            return false;
        }
    }

    public class DecimalEditor : ITypeEditor
    {
        public FrameworkElement ResolveEditor(PropertyItem propertyItem)
        {
            var doubleUpDown = new DoubleUpDown
            {
                FormatString = "F3",
                Increment = 0.001,
                Maximum = 200000.599,
                MinWidth = 100
            };

            var binding = new Binding("Value")
            {
                Source = propertyItem,
                Converter = new DecimalConverter(),
                Mode = BindingMode.TwoWay,
                UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
            };
            BindingOperations.SetBinding(doubleUpDown, DoubleUpDown.ValueProperty, binding);

            return doubleUpDown;
        }
    }

    public class DecimalConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is double doubleValue)
            {
                return doubleValue.ToString("0.00", culture); // Formatear a dos decimales
            }
            if (value is float floatValue)
            {
                return floatValue.ToString("0.00", culture); // Formatear a dos decimales
            }
            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (double.TryParse(value as string, NumberStyles.Any, CultureInfo.InvariantCulture, out double doubleValue))
            {
                return doubleValue;
            }
            if (float.TryParse(value as string, NumberStyles.Any, CultureInfo.InvariantCulture, out float floatValue))
            {
                return floatValue;
            }
            return value;
        }
    }

    public class ColorToBrushConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is Color color)
            {
                return new SolidColorBrush(color);
            }
            return Binding.DoNothing;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is SolidColorBrush brush)
            {
                return brush.Color;
            }
            return Binding.DoNothing;
        }
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class HiddenAttribute : Attribute
    {
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class DisplayNameAttribute : Attribute
    {
        public string DisplayName { get; }

        public DisplayNameAttribute(string displayName)
        {
            DisplayName = displayName;
        }
    }

    public class StringToBrushConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is string colorString)
            {
                return (Brush)new BrushConverter().ConvertFromString(colorString);
            }
            return Brushes.Transparent;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    public class SumConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values.Length == 2 && values[0] is float value1 && values[1] is float value2)
            {
                return (double)(value1 + value2);
            }
            return DependencyProperty.UnsetValue;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class VerticalPositionConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is double altura && double.TryParse(parameter?.ToString(), out double factor))
            {
                return altura * factor;
            }
            return 0;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class HalfWidthConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is double width)
            {
                return (width / 2.0) - (double.Parse(parameter.ToString()) / 2.0);
            }
            return 0;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class WidthPercentageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is double width)
            {
                return width * 0.25;
            }
            return 0;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class LevelToHeightMultiConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values[0] is float level && values[1] is double containerHeight)
            {
                return containerHeight * (level / 100);
            }
            return 0;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class PercentageToHeightConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is float percentage)
            {
                // Assume the tank height is 100 pixels by default, or use parameter if provided
                double tankHeight = 100;
                if (parameter is double paramHeight)
                    tankHeight = paramHeight;
                else if (parameter is string paramStr && double.TryParse(paramStr, out double parsed))
                    tankHeight = parsed;

                return tankHeight * (percentage / 100.0);
            }
            return 0;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class BoolToColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is bool isTrue)
            {
                return isTrue ? new SolidColorBrush(Colors.Green) : new SolidColorBrush(Colors.Red);
            }
            return new SolidColorBrush(Colors.Gray);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }


    public class BrushToColorNameConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is SolidColorBrush brush)
            {
                if (brush == Brushes.Red) return "Rojo";
                if (brush == Brushes.Blue) return "Azul";
                if (brush == Brushes.Black) return "Negro";
                if (brush == Brushes.Green) return "Verde";
                if (brush == Brushes.Gray) return "Gris";
            }
            return "Unknown";
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is string colorName)
            {
                switch (colorName)
                {
                    case "Rojo":
                        return Brushes.Red;
                    case "Azul":
                        return Brushes.Blue;
                    case "Negro":
                        return Brushes.Black;
                    case "Verde":
                        return Brushes.Green;
                    case "Gris":
                        return Brushes.Gray;
                }
            }
            return Brushes.Transparent;
        }
    }

    public class PixelToMeter
    {
        // Instancia privada estática, parte del patrón Singleton
        private static PixelToMeter? _instance;
        public UnitConverter calc;

        // Constructor privado para el patrón Singleton
        private PixelToMeter()
        {
            // Solo inicializar con valor por defecto si no se ha configurado antes
            calc = new UnitConverter(0.01f);
        }

        // Propiedad pública estática para acceder a la instancia
        public static PixelToMeter Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new PixelToMeter();
                }
                return _instance;
            }
        }
    }

    public class MeterToPixelConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null) return 0; // Aseguramos que el valor no sea nulo

            // Convertimos el valor de entrada en un número flotante
            float meters = System.Convert.ToSingle(value);

            float factor = 1; // Valor por defecto del factor

            // Si el parámetro no es nulo, intentamos convertirlo a float
            if (parameter != null)
            {
                string paramStr = parameter.ToString();

                // Normalizamos el parámetro para asegurar que el punto sea el separador decimal
                paramStr = paramStr.Replace(',', '.');

                // Utilizamos CultureInfo.InvariantCulture para evitar problemas con el separador decimal
                if (float.TryParse(paramStr, NumberStyles.Any, CultureInfo.InvariantCulture, out float parsedFactor))
                {
                    factor = parsedFactor; // Asignamos el factor parseado si la conversión es exitosa
                }
            }

            // Calculamos los píxeles llamando a la instancia de PixelToMeter y multiplicamos por el factor
            return PixelToMeter.Instance.calc.MetersToPixels(meters) * factor;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null) return 0; // Aseguramos que el valor no sea nulo

            // Convertimos el valor de entrada en un número flotante
            float pixels = System.Convert.ToSingle(value);

            float factor = 1; // Valor por defecto del factor

            // Si el parámetro no es nulo, intentamos convertirlo a float
            if (parameter != null)
            {
                string paramStr = parameter.ToString();

                // Normalizamos el parámetro para asegurar que el punto sea el separador decimal
                paramStr = paramStr.Replace(',', '.');

                // Utilizamos CultureInfo.InvariantCulture para evitar problemas con el separador decimal
                if (float.TryParse(paramStr, NumberStyles.Any, CultureInfo.InvariantCulture, out float parsedFactor))
                {
                    factor = parsedFactor; // Asignamos el factor parseado si la conversión es exitosa
                }
            }

            return PixelToMeter.Instance.calc.PixelsToMeters(pixels) * factor;
        }
    }

    public class MarginConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values.Length == 4 &&
                values[0] is double left &&
                values[1] is double top &&
                values[2] is double right &&
                values[3] is double bottom)
            {
                return new Thickness(PixelToMeter.Instance.calc.MetersToPixels((float)left), PixelToMeter.Instance.calc.MetersToPixels((float)top), PixelToMeter.Instance.calc.MetersToPixels((float)right), PixelToMeter.Instance.calc.MetersToPixels((float)bottom));
            }
            return new Thickness();
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException("ConvertBack is not supported.");
        }
    }

    public class DistanceToMarginConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is double distance)
            {
                return new Thickness(0, 0, 0, PixelToMeter.Instance.calc.MetersToPixels((float)distance)); // Ajustar Bottom a 'distance'
            }
            return new Thickness();
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException("ConvertBack is not supported.");
        }
    }

    public class FloatToFormattedStringConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is float floatValue)
            {
                var r = floatValue.ToString("0.00", culture).Replace('.', ','); // Formatear a dos decimales
                return r;
            }
            return value; // Devolver el valor original si no es un float
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is string stringValue && float.TryParse(stringValue.Replace(',', '.'), NumberStyles.Float, culture, out float result))
            {
                return result;
            }
            return value; // Devolver el valor original si no se puede convertir
        }
    }

    public class DoubleToFormattedStringConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is double floatValue)
            {
                return floatValue.ToString("0.00", culture).Replace('.', ','); // Formatear a dos decimales
            }
            return value; // Devolver el valor original si no es un float
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is string stringValue && double.TryParse(stringValue.Replace(',', '.'), NumberStyles.Float, culture, out double result))
            {
                return result;
            }
            return value; // Devolver el valor original si no se puede convertir
        }
    }


    public class UnitConverter
    {
        // La escala representa cuántos metros hay en un píxel
        public float Scale { get; private set; }


        public UnitConverter(float scale)
        {
            if (scale <= 0)
                throw new ArgumentException("Scale must be greater than zero.");

            Scale = scale;
        }

        // Convierte una distancia en metros a píxeles
        public float MetersToPixels(float meters)
        {
            return meters / Scale;
        }

        // Convierte una distancia en píxeles a metros
        public float PixelsToMeters(float pixels)
        {
            return pixels * Scale;
        }

        // Configurar o ajustar la escala
        public void SetScale(float newScale)
        {
            if (newScale <= 0)
                throw new ArgumentException("Scale must be greater than zero.");

            Scale = newScale;
        }
    }

    public class BooleanToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (bool)value ? Visibility.Visible : Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (value is Visibility visibility) && visibility == Visibility.Visible;
        }
    }
}
