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

namespace CtrEditor.ObjetosSim
{
    /// <summary>
    /// Interaction logic for ucTransporteGuiasUnion.xaml
    /// </summary>
    public partial class osTransporteGuiasUnion : osBase, IosBase
    {
        public static string NombreCategoria() => "Estaticos";
        private osBase _osMotorA = null;
        private osBase _osMotorB = null;

        Dictionary<Rectangle, simTransporte> SimGeometriaT;
        Dictionary<Rectangle, simGuia> SimGeometriaG;
        Dictionary<Rectangle, Storyboard> Storyboards;
        Dictionary<Rectangle, BoolReference> TransportsDirection;
        Dictionary<Rectangle, FloatReference> TransportsVelocidad;

        public static string NombreClase()
        {
            return "Transporte Guías Unión";
        }
        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("Apariencia")]
        [property: Description("Color del transporte")]
        [property: Name("Color")]
        Color color;

        [ObservableProperty]
        [property: Category("Enlace PLC")]
        [property: Description("Tag para activar enlace con motor")]
        [property: Name("Tag Activación Motor")]
        string tag_ReleActivatedMotor;

        [ObservableProperty]
        [property: Category("Enlace PLC")]
        [property: Description("Motor A enlazado")]
        [property: Name("Motor A")]
        [property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
        string id_MotorA;

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

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


        [ObservableProperty]
        [property: Category("Enlace PLC")]
        [property: Description("Motor B enlazado")]
        [property: Name("Motor B")]
        [property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
        string id_MotorB;

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

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


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

        partial void OnVelocidadActualAChanged(float value)
        {
            if (_visualRepresentation is ucTransporteGuiasUnion uc)
            {
                var transporte = uc.TransporteA;
                SetSpeed(transporte);
            }
        }

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Invertir dirección del motor A")]
        [property: Name("Invertir A")]
        bool invertirDireccionA;

        partial void OnInvertirDireccionAChanged(bool value)
        {
            if (_visualRepresentation is ucTransporteGuiasUnion uc)
            {
                var transporte = uc.TransporteA;

                SetSpeed(transporte);
                ActualizarStoryboards(transporte);
            }
        }


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

        partial void OnVelocidadActualBChanged(float value)
        {
            if (_visualRepresentation is ucTransporteGuiasUnion uc)
            {
                var transporte = uc.TransporteB;
                SetSpeed(transporte);
            }
        }

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Invertir dirección del motor B")]
        [property: Name("Invertir B")]
        bool invertirDireccionB;

        partial void OnInvertirDireccionBChanged(bool value)
        {
            if (_visualRepresentation is ucTransporteGuiasUnion uc)
            {
                var transporte = uc.TransporteB;

                SetSpeed(transporte);
                ActualizarStoryboards(transporte);
            }
        }

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

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Ancho de las secciones rectas")]
        [property: Name("Ancho Recto")]
        public float anchoRecto;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Ancho de la sección central")]
        [property: Name("Ancho Central")]
        public float anchoCentral;


        partial void OnAnchoRectoChanged(float value)
        {
            AnchoTransporte_oculto = anchoRecto + anchoCentral;
        }

        partial void OnAnchoCentralChanged(float value)
        {
            AnchoTransporte_oculto = anchoRecto + anchoCentral;
        }

        [ObservableProperty]
        [property: Category("Información")]
        [property: Description("Ancho total del transporte calculado")]
        [property: Name("Ancho Total")]
        public float anchoTransporte_oculto;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Coeficiente de fricción")]
        [property: Name("Coeficiente Fricción")]
        public float frictionCoefficient;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Velocidad máxima a 50Hz")]
        [property: Name("Velocidad Max 50Hz")]
        public float velMax50hz;

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

        [ObservableProperty]
        [property: Category("Información")]
        [property: Description("Estado de marcha")]
        [property: Name("En Marcha")]
        public bool esMarcha;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Distancia entre elementos")]
        [property: Name("Distancia")]
        private float distance;

        [ObservableProperty]
        [property: Category("Configuración")]
        [property: Description("Alto de las guías")]
        [property: Name("Alto Guía")]
        private float altoGuia;

        void ActualizarStoryboards(Rectangle transporte)
        {
            if (!Storyboards.Keys.Contains(transporte)) return;

            var direccion = TransportsDirection[transporte].Value;
            var velocidad = TransportsVelocidad[transporte].Value;

            Storyboards[transporte] = CrearAnimacionMultiStoryBoardTrasnporte(Storyboards[transporte], transporte, direccion);
            ActualizarAnimacionMultiStoryBoardTransporte(Storyboards[transporte], velocidad);
        }

        void SetSpeed(Rectangle transporte)
        {
            if (!Storyboards.Keys.Contains(transporte)) return;

            var invertirDireccion = TransportsDirection[transporte].Value;
            var velocidad = TransportsVelocidad[transporte].Value;

            if (invertirDireccion)
                SimGeometriaT[transporte]?.SetSpeed(-velocidad);
            else
                SimGeometriaT[transporte]?.SetSpeed(velocidad);

            ActualizarAnimacionMultiStoryBoardTransporte(Storyboards[transporte], velocidad);
        }

        private void ActualizarGeometrias()
        {
            if (_visualRepresentation is ucTransporteGuiasUnion uc)
            {
                foreach (var transporte in SimGeometriaT)
                {
                    UpdateRectangle(transporte.Value, transporte.Key, Alto, AnchoTransporte_oculto, Angulo);
                    ActualizarStoryboards(transporte.Key);
                    SetSpeed(transporte.Key);
                }
                foreach (var l in SimGeometriaG)
                    UpdateOrCreateLine(l.Value, l.Key);

            }
        }

        public override void OnMoveResizeRotate()
        {
            ActualizarGeometrias();
        }


        public osTransporteGuiasUnion()
        {
            AnchoRecto = 0.5f;
            AnchoCentral = 0.5f;
            Alto = 0.10f;
            AltoGuia = 0.03f;
            Distance = 0.02f;
            SimGeometriaT = new Dictionary<Rectangle, simTransporte>();
            SimGeometriaG = new Dictionary<Rectangle, simGuia>();
            Storyboards = new Dictionary<Rectangle, Storyboard>();
            TransportsDirection = new Dictionary<Rectangle, BoolReference>();
            TransportsVelocidad = new Dictionary<Rectangle, FloatReference>();
            Tag_ReleActivatedMotor = "1";
        }

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

        public override void SimulationStop()
        {
            // Se llama al detener la simulacion
            if (_visualRepresentation is ucTransporteGuiasUnion uc)
            {
                SetSpeed(uc.TransporteB);
                SetSpeed(uc.TransporteA);
            }
        }

        public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
        {
            if (_osMotorA != null)
            {
                if (_osMotorA is osVMmotorSim motor)
                    if (LeerBitTag(Tag_ReleActivatedMotor))
                        VelocidadActualA = motor.Velocidad;
                    else
                        VelocidadActualA = 0;
            }

            if (_osMotorB != null)
            {
                if (_osMotorB is osVMmotorSim motor)
                    if (LeerBitTag(Tag_ReleActivatedMotor))
                        VelocidadActualB = motor.Velocidad;
                    else
                        VelocidadActualB = 0;
            }
        }

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

            // El UserControl ya se ha cargado y podemos obtener las coordenadas para 
            // crear el objeto de simulacion
            if (_visualRepresentation is ucTransporteGuiasUnion uc)
            {
                foreach (var child in uc.Canvas.Children)
                    if (child is Rectangle rect)
                        if (rect.Name.StartsWith("Transporte"))
                        {
                            SimGeometriaT.Add(rect, AddRectangle(simulationManager, rect, Alto, AnchoTransporte_oculto, Angulo));
                            Storyboards.Add(rect, CrearAnimacionMultiStoryBoardTrasnporte(rect, false));
                        }

                foreach (var child in uc.GuiaSuperior.Canvas.Children)
                    if (child is Rectangle rect)
                        SimGeometriaG.Add(rect, AddLine(simulationManager, rect));

                foreach (var child in uc.GuiaInferior.Canvas.Children)
                    if (child is Rectangle rect)
                        SimGeometriaG.Add(rect, AddLine(simulationManager, rect));

                TransportsDirection.Add(uc.TransporteA, new BoolReference(() => InvertirDireccionA, value => InvertirDireccionA = value));
                TransportsDirection.Add(uc.TransporteB, new BoolReference(() => InvertirDireccionB, value => InvertirDireccionB = value));

                TransportsVelocidad.Add(uc.TransporteA, new FloatReference(() => VelocidadActualA, value => VelocidadActualA = value));
                TransportsVelocidad.Add(uc.TransporteB, new FloatReference(() => VelocidadActualB, value => VelocidadActualB = value));

            }
            OnId_MotorAChanged(Id_MotorA); // Link Id_Motor = Motor
            OnId_MotorBChanged(Id_MotorB); // Link Id_Motor = Motor
        }


        public override void ucUnLoaded()
        {
            // El UserControl se esta eliminando
            // eliminar el objeto de simulacion
            foreach (var s in SimGeometriaT)
                simulationManager.Remove(s.Value);
            foreach (var s in SimGeometriaG)
                simulationManager.Remove(s.Value);
        }


    }

    public class BoolReference
    {
        private Func<bool> getter;
        private Action<bool> setter;

        public BoolReference(Func<bool> getter, Action<bool> setter)
        {
            this.getter = getter;
            this.setter = setter;
        }

        public bool Value
        {
            get => getter();
            set => setter(value);
        }
    }

    public class FloatReference
    {
        private Func<float> getter;
        private Action<float> setter;

        public FloatReference(Func<float> getter, Action<float> setter)
        {
            this.getter = getter;
            this.setter = setter;
        }

        public float Value
        {
            get => getter();
            set => setter(value);
        }
    }


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

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


    }
}
