using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.FuncionesBase;
using DocumentFormat.OpenXml.Spreadsheet;
using Newtonsoft.Json;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using Color = System.Windows.Media.Color;
using Colors = System.Windows.Media.Colors;
using JsonIgnoreAttribute = Newtonsoft.Json.JsonIgnoreAttribute;

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

    public partial class osFramePlate : osBase, IosBase
    {
        [JsonIgnore]
        public float offsetY;
        [JsonIgnore]
        public float offsetX;
        [JsonIgnore]
        public float offsetAngulo;

        public static string NombreClase()
        {
            return "Marco de Panel";
        }

        public static string NombreCategoria()
        {
            return "Decorativos";
        }
        private string nombre = NombreClase();

        [property: Category("Identificación")]
        [property: Description("Nombre identificativo del objeto")]
        [property: Name("Nombre")]
        public override string Nombre
        {
            get => nombre;
            set => SetProperty(ref nombre, value);
        }

        [ObservableProperty]
        [property: Description("Índice de capa para objetos en el marco")]
        [property: Category("Configuración")]
        [property: Name("Índice Z del Marco")]
        int zindex_FramePlate;

        partial void OnZindex_FramePlateChanged(int value)
        {
            UpdateZIndex(value);
        }

        [ObservableProperty]
        [property: Category("Apariencia")]
        [property: Description("Color de fondo del marco")]
        [property: Name("Color")]
        Color color;

        [ObservableProperty]
        [property: Category("Apariencia")]
        [property: Description("Color del texto del título")]
        [property: Name("Color del Título")]
        Color color_Titulo;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Texto del título del marco")]
        [property: Name("Título")]
        string titulo;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Alto del área del título")]
        [property: Name("Alto del Título")]
        public float alto_Titulo;

        // Encoder X
        [ObservableProperty]
        [property: Description("This is a link to a Encoder for X.")]
        [property: Category("Encoders:")]
        [property: ItemsSource(typeof(osBaseItemsSource<osEncoderMotorLineal>))]
        private string encoder_X;

        [ObservableProperty]
        [property: Description("K Pulses per meter for Moving")]
        [property: Category("Encoders:")]
        public float k_encoder_X;

        [ObservableProperty]
        [property: ReadOnly(true)]
        [property: Description("Actual Position X")]
        [property: Category("Encoders:")]
        public float encoder_X_Position;


        [ObservableProperty]
        [property: Description("X in meter offset Left. Position when the encoder is 0")]
        [property: Category("Encoders:")]
        public float offset_encoder_X;

        // Encoder Y
        [ObservableProperty]
        [property: Description("This is a link to a Encoder for Y.")]
        [property: Category("Encoders:")]
        [property: ItemsSource(typeof(osBaseItemsSource<osEncoderMotorLineal>))]
        private string encoder_Y;

        [ObservableProperty]
        [property: Description("K Pulses per meter for Moving")]
        [property: Category("Encoders:")]
        public float k_encoder_Y;

        [ObservableProperty]
        [property: ReadOnly(true)]
        [property: Description("Actual Position Y")]
        [property: Category("Encoders:")]
        public float encoder_Y_Position;

        [ObservableProperty]
        [property: Description("Y in meter offset Top. Position when the encoder is 0")]
        [property: Category("Encoders:")]
        public float offset_encoder_Y;

        // Encoder Rotation
        [ObservableProperty]
        [property: Description("This is a link to a Encoder for Rotation.")]
        [property: Category("Encoders:")]
        [property: ItemsSource(typeof(osBaseItemsSource<osEncoderMotorLineal>))]
        private string encoder_Rotation;

        [ObservableProperty]
        [property: Description("K Pulses per degree for Rotation")]
        [property: Category("Encoders:")]
        public float k_encoder_Rotation;

        [ObservableProperty]
        [property: ReadOnly(true)]
        [property: Description("Actual Rotation Angle")]
        [property: Category("Encoders:")]
        public float encoder_Rotation_Angle;

        [ObservableProperty]
        [property: Description("Angle in degrees offset. Angle when the encoder is 0")]
        [property: Category("Encoders:")]
        public float offset_encoder_Rotation;

        partial void OnK_encoder_YChanged(float value)
        {
            UpdatePosition();
        }

        partial void OnOffset_encoder_YChanged(float value)
        {
            UpdatePosition();
        }

        partial void OnK_encoder_XChanged(float value)
        {
            UpdatePosition();
        }

        partial void OnOffset_encoder_XChanged(float value)
        {
            UpdatePosition();
        }

        partial void OnK_encoder_RotationChanged(float value)
        {
            UpdatePosition();
        }

        partial void OnOffset_encoder_RotationChanged(float value)
        {
            UpdatePosition();
        }

        [JsonIgnore]
        private osEncoderMotorLineal EncoderX;

        [JsonIgnore]
        private osEncoderMotorLineal EncoderY;

        [JsonIgnore]
        private osEncoderMotorLineal EncoderRotation;

        [JsonIgnore]
        private bool isUpdatingFromEncoderX = false;

        [JsonIgnore]
        private bool isUpdatingFromEncoderY = false;

        [JsonIgnore]
        private bool isUpdatingFromEncoderRotation = false;

        partial void OnEncoder_XChanged(string value)
        {
            if (EncoderX != null)
                EncoderX.PropertyChanged -= OnEncoderXPropertyChanged;
            if (_mainViewModel != null && value != null && value.Length > 0)
            {
                EncoderX = (osEncoderMotorLineal)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osEncoderMotorLineal && s.Nombre == value), null);
                if (EncoderX != null)
                    EncoderX.PropertyChanged += OnEncoderXPropertyChanged;
            }
        }

        partial void OnEncoder_YChanged(string value)
        {
            if (EncoderY != null)
                EncoderY.PropertyChanged -= OnEncoderYPropertyChanged;
            if (_mainViewModel != null && value != null && value.Length > 0)
            {
                EncoderY = (osEncoderMotorLineal)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osEncoderMotorLineal && s.Nombre == value), null);
                if (EncoderY != null)
                    EncoderY.PropertyChanged += OnEncoderYPropertyChanged;
            }
        }

        partial void OnEncoder_RotationChanged(string value)
        {
            if (EncoderRotation != null)
                EncoderRotation.PropertyChanged -= OnEncoderRotationPropertyChanged;
            if (_mainViewModel != null && value != null && value.Length > 0)
            {
                EncoderRotation = (osEncoderMotorLineal)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osEncoderMotorLineal && s.Nombre == value), null);
                if (EncoderRotation != null)
                    EncoderRotation.PropertyChanged += OnEncoderRotationPropertyChanged;
            }
        }

        private void OnEncoderXPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (!isUpdatingFromEncoderX)
            {
                isUpdatingFromEncoderX = true;
                // Actualizamos el nombre si este fue modificado
                if (e.PropertyName == nameof(osEncoderMotorLineal.Nombre))
                    Encoder_X = ((osEncoderMotorLineal)sender).Nombre;

                if (e.PropertyName == nameof(osEncoderMotorLineal.Valor_Actual))
                {
                    UpdatePosition();
                }

                isUpdatingFromEncoderX = false;
            }
        }

        private void OnEncoderYPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (!isUpdatingFromEncoderY)
            {
                isUpdatingFromEncoderY = true;
                // Actualizamos el nombre si este fue modificado
                if (e.PropertyName == nameof(osEncoderMotorLineal.Nombre))
                    Encoder_Y = ((osEncoderMotorLineal)sender).Nombre;

                if (e.PropertyName == nameof(osEncoderMotorLineal.Valor_Actual))
                {
                    UpdatePosition();
                }

                isUpdatingFromEncoderY = false;
            }
        }

        private void OnEncoderRotationPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (!isUpdatingFromEncoderRotation)
            {
                isUpdatingFromEncoderRotation = true;
                // Actualizamos el nombre si este fue modificado
                if (e.PropertyName == nameof(osEncoderMotorLineal.Nombre))
                    Encoder_Rotation = ((osEncoderMotorLineal)sender).Nombre;

                if (e.PropertyName == nameof(osEncoderMotorLineal.Valor_Actual))
                {
                    UpdatePosition();
                }

                isUpdatingFromEncoderRotation = false;
            }
        }

        public void UpdatePosition()
        {
            // Update X position if encoder is available
            if (EncoderX != null && K_encoder_X != 0)
            {
                Encoder_X_Position = EncoderX.Valor_Actual;
                Left = (Encoder_X_Position / k_encoder_X) + offset_encoder_X;
            }
            // Update Y position if encoder is available
            if (EncoderY != null && K_encoder_Y != 0)
            {
                Encoder_Y_Position = EncoderY.Valor_Actual;
                Top = (Encoder_Y_Position / k_encoder_Y) + offset_encoder_Y;
            }
            // Update rotation angle if encoder is available
            if (EncoderRotation != null && K_encoder_Rotation != 0)
            {
                Encoder_Rotation_Angle = EncoderRotation.Valor_Actual;
                Angulo = (Encoder_Rotation_Angle / k_encoder_Rotation) + offset_encoder_Rotation;
            }
        }

        public override void TopChanging(float oldValue, float newValue)
        {
            offsetY = newValue - oldValue;
        }
        public override void LeftChanging(float oldValue, float newValue)
        {
            offsetX = newValue - oldValue;
        }

        public override void AnguloChanging(float oldValue, float newValue)
        {
            offsetAngulo = newValue - oldValue;
        }

        [ObservableProperty]
        [property: Description("Show/Hide the plate background")]
        [property: Category("Appearance:")]
        private bool showPlate;

        [ObservableProperty]
        [property: Description("Use horizontal center as pivot point for rotation")]
        [property: Category("Pivot:")]
        private bool pivotCenterX;

        [ObservableProperty]
        [property: Description("Use vertical center as pivot point for rotation")]
        [property: Category("Pivot:")]
        private bool pivotCenterY;

        partial void OnPivotCenterXChanged(bool value)
        {
            // Notificar cambio de pivot para actualizar objetos conectados
            OnPropertyChanged(nameof(PivotCenterX));
            // Notificar cambios en las propiedades de pivot para el RenderTransform
            OnPropertyChanged(nameof(RenderTransformCenterX));
            OnPropertyChanged(nameof(RenderTransformCenterY));
        }

        partial void OnPivotCenterYChanged(bool value)
        {
            // Notificar cambio de pivot para actualizar objetos conectados
            OnPropertyChanged(nameof(PivotCenterY));
            // Notificar cambios en las propiedades de pivot para el RenderTransform
            OnPropertyChanged(nameof(RenderTransformCenterX));
            OnPropertyChanged(nameof(RenderTransformCenterY));
        }

        public osFramePlate()
        {
            Ancho = 0.5f;
            Alto = 0.5f;
            Alto_Titulo = 0.2f;
            Color = Colors.WhiteSmoke;
            Titulo = "Frame";
            Zindex_FramePlate = 0;
            ShowPlate = true;  // Default value
        }

        public override void AnchoChanged(float value)
        {
            base.AnchoChanged(value);
            OnPropertyChanged(nameof(RenderTransformCenterX));
        }

        public override void AltoChanged(float value)
        {
            base.AltoChanged(value);
            OnPropertyChanged(nameof(RenderTransformCenterY));
        }

        partial void OnAlto_TituloChanged(float value)
        {
            OnPropertyChanged(nameof(RenderTransformCenterY));
        }

        public override void ucLoaded()
        {
            base.ucLoaded();
            // El UserControl se ha cargado
            OnEncoder_XChanged(Encoder_X);
            OnEncoder_YChanged(Encoder_Y);
            OnEncoder_RotationChanged(Encoder_Rotation);
            UpdateZIndex(Zindex_FramePlate);
        }

        public override void ucUnLoaded()
        {
            base.ucUnLoaded();
            // El UserControl se esta eliminando
            // eliminar el objeto de simulacion
        }

        /// <summary>
        /// Calcula el punto de pivot basado en los checkboxes PivotCenterX y PivotCenterY
        /// </summary>
        /// <returns>Punto de pivot en coordenadas absolutas</returns>
        public (float X, float Y) GetPivotPoint()
        {
            float pivotX = Left + (PivotCenterX ? Ancho / 2 : 0);
            float pivotY = Top + (PivotCenterY ? Alto / 2 : 0);
            return (pivotX, pivotY);
        }

        /// <summary>
        /// Centro X para el RenderTransform en píxeles del control
        /// </summary>
        [JsonIgnore]
        public double RenderTransformCenterX
        {
            get
            {
                if (PivotCenterX)
                {
                    // Centro horizontal del rectángulo principal
                    return PixelToMeter.Instance.calc.MetersToPixels(Ancho) / 2.0;
                }
                else
                {
                    // Left del rectángulo principal (0)
                    return 0.0;
                }
            }
        }

        /// <summary>
        /// Centro Y para el RenderTransform en píxeles del control
        /// </summary>
        [JsonIgnore]
        public double RenderTransformCenterY
        {
            get
            {
                // Altura del título y del rectángulo principal
                double titleHeight = PixelToMeter.Instance.calc.MetersToPixels(Alto_Titulo);
                double frameHeight = PixelToMeter.Instance.calc.MetersToPixels(Alto);

                if (PivotCenterY)
                {
                    // Centro vertical de todo el control (título + rectángulo)
                    return (titleHeight + frameHeight) / 2.0;
                }
                else
                {
                    // Top absoluto del control (0)
                    return 0.0;
                }
            }
        }

    }

    public partial class ucFramePlate : UserControl, IDataContainer
    {
        public osBase? Datos { get; set; }
        public int zIndex_fromFrames { get; set; }

        public ucFramePlate()
        {
            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 Highlight(bool State) { }
        public ZIndexEnum ZIndex_Base()
        {
            return ZIndexEnum.Decorativos;
        }

    }
}



