using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.FuncionesBase;
using LibS7Adv;
using Newtonsoft.Json;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;


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

    public partial class osVMmotorSim : osBase, IosBase
    {
        public static string NombreCategoria() => "Estaticos";

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

        private VMSimMotor motState = new VMSimMotor();

        public static string NombreClase()
        {
            return "Motor VetroMeccanica";
        }
        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: JsonIgnore]
        [property: Category("Apariencia")]
        [property: Description("Imagen visual del motor")]
        [property: Name("Imagen")]
        public ImageSource imageSource_oculta;

        [ObservableProperty]
        [property: Category("Apariencia")]
        [property: Description("Tamaño visual del motor")]
        [property: Name("Tamaño")]
        public float tamano;

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

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Tiempo de actualización en milisegundos")]
        [property: Name("Tiempo de Actualización")]
        public float refresh_Time_ms;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Velocidad proporcional en porcentaje")]
        [property: Name("Velocidad Proporcional")]
        public float proporcional_Speed;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Velocidad máxima para rampa")]
        [property: Name("Velocidad Máxima Rampa")]
        public float max_Speed_for_Ramp;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Contacto normalmente cerrado de trip del VFD")]
        [property: Name("VFD Trip NC")]
        bool vFD_Trip_NC;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Tiempo de rampa para acelerar/desacelerar")]
        [property: Name("Tiempo de Rampa")]
        public float tiempoRampa;

        [ObservableProperty]
        [property: Description("Habilita lectura del encoder del motor simulado en PLC")]
        [property: Category("Encoder")]
        [property: Name("Motor con Encoder")]
        bool motor_With_Encoder;

        [ObservableProperty]
        [property: Description("Valor actual de la posición del encoder")]
        [property: Category("Encoder")]
        [property: Name("Posición Actual")]
        public float actual_Position;

        partial void OnTiempoRampaChanged(float value)
        {
            if (value < 0.1f)
                value = 0.1f;
            tiempoRampa = value;
        }

        [ObservableProperty]
        [property: Category("Información")]
        [property: Description("Estado de encendido del motor")]
        [property: Name("Encendido")]
        bool encendido;

        [ObservableProperty]
        [property: Category("Enlace PLC")]
        [property: Description("Número del motor en el PLC")]
        [property: Name("Número de Motor PLC")]
        int pLC_NumeroMotor;

        partial void OnPLC_NumeroMotorChanged(int value)
        {
            if (PLC_DB_Motor == 0)
            {
                PLC_DB_Motor = PLC_NumeroMotor - 30 + 300;
            }
        }

        [ObservableProperty]
        [property: Category("Enlace PLC")]
        [property: Description("Bloque de datos del motor en el PLC")]
        [property: Name("DB del Motor")]
        int pLC_DB_Motor;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Relación de transmisión")]
        [property: Name("Ratio")]
        public float ratio;

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

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Invierte el sentido de giro")]
        [property: Name("Sentido Contrario")]
        public bool sentido_contrario;


        partial void OnVelocidadChanged(float value)
        {
            if (value > 0)
                ImageSource_oculta = ImageFromPath("/imagenes/motorVerde.png");
            else
                ImageSource_oculta = ImageFromPath("/imagenes/motorNegro.png");
        }

        public osVMmotorSim()
        {
            Tamano = 0.30f;
            PLC_NumeroMotor = 31;
            Proporcional_Speed = 100;
            Max_Speed_for_Ramp = 100;
            TiempoRampa = 3;
            ImageSource_oculta = ImageFromPath("/imagenes/motor2.png");
            Refresh_Time_ms = 500;
        }

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

        }

        [JsonIgnore]
        private float elapsedTimeAccumulator = 0;

        public override void UpdatePLC(PLCViewModel plc, int TotalMilliseconds)
        {

            elapsedTimeAccumulator += TotalMilliseconds;
            float randomFactor = (float)(new Random().NextDouble() * 0.1); // 10% random factor
            float adjustedRefreshTime = Refresh_Time_ms * (1 + randomFactor);

            if (elapsedTimeAccumulator >= adjustedRefreshTime)
            {
                motState.UpdatePLC(plc, this, TotalMilliseconds);
                elapsedTimeAccumulator = 0;
            }
            Velocidad = (Proporcional_Speed / 100) * (motState.STATUS_VFD_ACT_Speed_Hz / 10);
            Sentido_contrario = motState.OUT_Reversal;

        }

        public override void UpdateControl(int TotalMilliseconds)
        {
            // Calculamos la velocidad
            motState.UpdateSpeed(Max_Speed_for_Ramp, TiempoRampa, TotalMilliseconds);

        }

        public override void ucLoaded()
        {
            // El UserControl ya se ha cargado y podemos obtener las coordenadas para 
            // crear el objeto de simulacion
            base.ucLoaded();
            OnVelocidadChanged(Velocidad);

        }
    }

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

        public ucVMmotorSim()
        {
            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;
        }

    }

    public class VMSimMotor
    {
        public bool _STATUS_VFD_Ready;
        public float STATUS_VFD_ACT_Speed_Hz;
        public bool Motor_Running;
        public bool STATUS_VFD_Trip;
        public bool STATUS_VFD_Warning;
        public bool STATUS_VFD_Coasting;
        public bool OUT_Run;
        public bool OUT_Stop;
        public bool OUT_Reversal;
        public float OUT_OUT_VFD_REQ_Speed_Hz;

        public void UpdatePLC(PLCViewModel plc, osVMmotorSim Data, int TotalMilliseconds)
        {
            var DB_Motor = Data.PLC_DB_Motor;

            if (DB_Motor == 0)
                return;

            // Add timestamp to trace when the read occurs
            var timestamp = DateTime.Now;

            if (Data.Motor_With_Encoder)
                Data.Actual_Position = (float)plc.LeerTagDInt($"\"DB MotorSimulate\".Motors[{DB_Motor}].ActualPosition");
            else
                Data.Actual_Position = 0;

            // Read ControlWord and track the raw response
            var rawResponse = plc.LeerTagDInt($"\"DB MotorSimulate\".Motors[{DB_Motor}].ControlWord");
            int controlWord = rawResponse ?? 0;
            var control = VMMotorBitPacker.UnpackControlWord(controlWord);


            // Update local state from ControlWord
            OUT_Run = control.run;
            OUT_Stop = !control.stop;
            OUT_Reversal = control.reversal;
            OUT_OUT_VFD_REQ_Speed_Hz = control.reqSpeedHz;

            // Update motor state based on enable status
            if (Data.Encendido)
            {
                _STATUS_VFD_Ready = true;
                Motor_Running = OUT_Run && !OUT_Stop;
                STATUS_VFD_Trip = false;
                STATUS_VFD_Warning = false;
                STATUS_VFD_Coasting = false;
            }
            else
            {
                _STATUS_VFD_Ready = false;
                Motor_Running = false;
                STATUS_VFD_Trip = true;
                STATUS_VFD_Warning = false;
                STATUS_VFD_Coasting = false;
            }

            if (Data.VFD_Trip_NC)
                STATUS_VFD_Trip = !STATUS_VFD_Trip;


            // Pack all status bits and speed into StatusWord
            int statusWord = VMMotorBitPacker.PackStatusWord(
                _STATUS_VFD_Ready,
                Motor_Running,
                STATUS_VFD_Trip,
                STATUS_VFD_Warning,
                STATUS_VFD_Coasting,
                (short)STATUS_VFD_ACT_Speed_Hz
            );

            // Write StatusWord in one operation
            plc.EscribirTagDInt($"\"DB MotorSimulate\".Motors[{DB_Motor}].StatusWord", statusWord);
        }

        private float CalcSpeedRamp(float max_Speed_for_Ramp, float TiempoRampa, float actual, float expected, int TotalMilliseconds)
        {
            float hzIncrementsRamp = (max_Speed_for_Ramp * 10) / (TiempoRampa * (1000.0f / TotalMilliseconds));
            float delta = expected - actual;
            // Conrtolar si la diferencia no es mayor de lo que falta
            if (Math.Abs(hzIncrementsRamp) > Math.Abs(delta))
                hzIncrementsRamp = Math.Abs(delta);
            if (delta < 0)
                return -hzIncrementsRamp;
            else
                return hzIncrementsRamp;
        }

        public void UpdateSpeed(float max_Speed_for_Ramp, float TiempoRampa, int TotalMilliseconds)
        {
            // Calculamos la velocidad
            STATUS_VFD_ACT_Speed_Hz += CalcSpeedRamp(max_Speed_for_Ramp, TiempoRampa, STATUS_VFD_ACT_Speed_Hz, OUT_OUT_VFD_REQ_Speed_Hz, TotalMilliseconds);

        }
    }



}
