
using LibS7Adv;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Diagnostics;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using System.ComponentModel;
using CtrEditor.FuncionesBase;

namespace CtrEditor.ObjetosSim
{
    /// <summary>
    /// Interaction logic for ucGearEncoder.xaml
    /// </summary>
    public partial class osGearEncoder : osBase, IosBase
    {
        public static string NombreCategoria() => "SensoresComandos";
        private osBase Motor = null;
        private Stopwatch Stopwatch = new Stopwatch();
        private double stopwatch_last = 0;

        // Otros datos y métodos relevantes para la simulación

        public static string NombreClase()
        {
            return "Encoder de Engranaje";
        }

        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: Category("Enlace PLC")]
        [property: Description("Tag PLC para señal de pulso")]
        [property: Name("Tag PLC")]
        public string tag;

        [ObservableProperty]
        [property: Category("Simulación")]
        [property: Description("Estado actual del pulso")]
        [property: Name("Pulso")]
        public bool pulso;

        partial void OnPulsoChanged(bool value)
        {
            if (value)
            {
                var dTime = Stopwatch.Elapsed.TotalMilliseconds - stopwatch_last;
                stopwatch_last = Stopwatch.Elapsed.TotalMilliseconds;
                Tiempo_Pulso = (float)dTime;
            }

            EscribirBitTag(Tag, Pulso);
            if (value)
                Color_oculto = Brushes.LightGreen;
            else
                Color_oculto = Brushes.Gray;
        }

        [ObservableProperty]
        [property: Category("Apariencia")]
        [property: Description("Color visual del encoder")]
        [property: Name("Color")]
        private Brush color_oculto;

        [ObservableProperty]
        [property: Category("Información")]
        [property: Description("Velocidad actual del encoder")]
        [property: Name("Velocidad Actual")]
        public float velocidadActual;

        public override void AnguloChanged(float value)
        {
            // Generar pulsos cuadrados en función del ángulo
            Pulso = DetectarDiente();
        }

        [ObservableProperty]
        [property: Category("Información")]
        [property: Description("Tiempo entre pulsos en ms")]
        [property: Name("Tiempo de Pulso")]
        public float tiempo_Pulso;

        [ObservableProperty]
        [property: Category("Información")]
        [property: Description("Pulsos generados por segundo")]
        [property: Name("Pulsos por Segundo")]
        public float pulsos_Por_Segundo;

        partial void OnPulsos_Por_SegundoChanged(float value)
        {
            if (VelocidadActual > 0)
                Giros_segundo_a_100 = (float)((pulsos_Por_Segundo / Dientes) * 100 / VelocidadActual);
        }
        [ObservableProperty]
        [property: Category("Encoder")]
        [property: Description("Ejecutar homing del encoder")]
        [property: Name("Homing")]
        private bool homing;

        partial void OnHomingChanged(bool value)
        {
            homing = false;
            Angulo = 0;
        }

        [ObservableProperty]
        [property: Category("Encoder")]
        [property: Description("Número de dientes del encoder")]
        [property: Name("Cantidad de Dientes")]
        public float dientes;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Radio interno del encoder")]
        [property: Name("Radio Interno")]
        public float radio_Interno;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Radio externo del encoder")]
        [property: Name("Radio Externo")]
        public float radio_Externo;

        [ObservableProperty]
        [property: Category("Encoder")]
        [property: Description("Ancho de los dientes (0-1)")]
        [property: Name("Ancho de Dientes")]
        public float ancho_Dientes;

        [ObservableProperty]
        [property: Category("Encoder")]
        [property: Description("Giros por segundo al 100%")]
        [property: Name("Giros/seg al 100%")]
        public float giros_segundo_a_100;

        public override void OnResize(float Delta_Width, float Delta_Height)
        {
            Radio_Externo += Delta_Width;
            Radio_Interno += Delta_Height;
        }

        [ObservableProperty]
        [property: Category("Enlace PLC")]
        [property: Description("Motor enlazado al encoder")]
        [property: Name("Motor Enlazado")]
        [property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
        string id_Motor;

        partial void OnId_MotorChanged(string value)
        {
            if (Motor != null)
                Motor.PropertyChanged -= OnMotorPropertyChanged;
            if (_mainViewModel != null && value != null && value.Length > 0)
            {
                Motor = (osVMmotorSim)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osVMmotorSim && s.Nombre == value), null);
                if (Motor != null)
                    Motor.PropertyChanged += OnMotorPropertyChanged;
            }
        }

        private void OnMotorPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(osVMmotorSim.Nombre))
            {
                Id_Motor = ((osVMmotorSim)sender).Nombre;
            }
        }

        public osGearEncoder()
        {
            Ancho_Dientes = 0.5f;
            Dientes = 10;
            Radio_Interno = 0.5f;
            Radio_Externo = 0.6f;
            Color_oculto = Brushes.Gray;
            Stopwatch.Start();
        }

        public override void UpdateGeometryStart()
        {
            // Se llama antes de la simulacion

        }
        public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
        {

        }

        public bool DetectarDiente()
        {
            double angleStep = 360.0 / Dientes;
            double halfToothWidthAngle = (angleStep * Ancho_Dientes) / 2;

            // Normalize the Angulo to be within 0-360 degrees
            double normalizedAngle = Angulo % 360;
            if (normalizedAngle < 0) normalizedAngle += 360;

            // Calculate the position of the first tooth's center at angle 0
            double firstToothCenterAngle = 0;

            // Calculate the angular distance from the current angle to the first tooth's center
            double angularDistance = Math.Abs(firstToothCenterAngle - normalizedAngle);
            if (angularDistance > 180) angularDistance = 360 - angularDistance;

            // Check if the normalized angle falls within the segment of the first tooth
            if (angularDistance <= halfToothWidthAngle)
            {
                return true;
            }

            // Calculate the number of steps to reach the nearest tooth's center
            int steps = (int)Math.Round(normalizedAngle / angleStep);

            // Calculate the angular position of the nearest tooth's center
            double nearestToothCenterAngle = steps * angleStep;

            // Calculate the angular distance from the current angle to the nearest tooth's center
            angularDistance = Math.Abs(nearestToothCenterAngle - normalizedAngle);
            if (angularDistance > 180) angularDistance = 360 - angularDistance;

            // Check if the normalized angle falls within the segment of the nearest tooth
            return angularDistance <= halfToothWidthAngle;
        }


        public override void UpdateControl(int elapsedMilliseconds)
        {
            // Calculamos la velocidad
            if (Motor != null)
            {
                if (Motor is osVMmotorSim motor)
                {
                    if (motor.Sentido_contrario)
                        VelocidadActual = -motor.Velocidad;
                    else
                        VelocidadActual = motor.Velocidad;

                    // Calcular la cantidad de giros por segundo
                    double girosPorSegundo = (VelocidadActual / 100.0) * Giros_segundo_a_100;

                    // Calcular la fracción del segundo que ha pasado
                    double segundosTranscurridos = elapsedMilliseconds / 1000.0;

                    // Calcular el incremento de ángulo
                    double incrementoAngulo = (girosPorSegundo * 360.0) * segundosTranscurridos;

                    // Actualizar el ángulo
                    Angulo = (float)(Angulo + incrementoAngulo) % 360;
                }
            }
        }

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

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

        public ucGearEncoder()
        {
            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.Estaticos;
        }

    }
}

