using HydraulicSimulator.Models;
using System.Collections.Generic;

namespace CtrEditor.HydraulicSimulator
{
    /// <summary>
    /// Interfaz base para objetos que tienen componentes hidráulicos
    /// </summary>
    public interface IHydraulicComponent
    {
        /// <summary>
        /// Indica si este objeto tiene componentes hidráulicos activos
        /// </summary>
        bool HasHydraulicComponents { get; }
        
        /// <summary>
        /// Obtiene los nodos hidráulicos asociados a este objeto
        /// </summary>
        /// <returns>Lista de definiciones de nodos</returns>
        List<HydraulicNodeDefinition> GetHydraulicNodes();
        
        /// <summary>
        /// Obtiene los elementos hidráulicos asociados a este objeto
        /// </summary>
        /// <returns>Lista de definiciones de elementos hidráulicos</returns>
        List<HydraulicElementDefinition> GetHydraulicElements();
        
        /// <summary>
        /// Actualiza las propiedades hidráulicas del objeto antes de la simulación
        /// </summary>
        void UpdateHydraulicProperties();
        
        /// <summary>
        /// Aplica los resultados de la simulación hidráulica al objeto
        /// </summary>
        /// <param name="flows">Caudales calculados</param>
        /// <param name="pressures">Presiones calculadas</param>
        void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures);
    }
    
    /// <summary>
    /// Interfaz para objetos que pueden recibir información de caudal
    /// </summary>
    public interface IHydraulicFlowReceiver
    {
        /// <summary>
        /// Establece el caudal actual del objeto
        /// </summary>
        /// <param name="flow">Caudal en m³/s</param>
        void SetFlow(double flow);
        
        /// <summary>
        /// Obtiene el caudal actual del objeto
        /// </summary>
        /// <returns>Caudal en m³/s</returns>
        double GetFlow();
    }
    
    /// <summary>
    /// Interfaz para objetos que pueden recibir información de presión
    /// </summary>
    public interface IHydraulicPressureReceiver
    {
        /// <summary>
        /// Establece la presión actual del objeto
        /// </summary>
        /// <param name="pressure">Presión en Pa</param>
        void SetPressure(double pressure);
        
        /// <summary>
        /// Obtiene la presión actual del objeto
        /// </summary>
        /// <returns>Presión en Pa</returns>
        double GetPressure();
    }
    
    /// <summary>
    /// Interfaz para objetos que son bombas hidráulicas
    /// </summary>
    public interface IHydraulicPump : IHydraulicComponent, IHydraulicFlowReceiver, IHydraulicPressureReceiver
    {
        /// <summary>
        /// Cabeza de la bomba a caudal cero (m)
        /// </summary>
        double PumpHead { get; set; }
        
        /// <summary>
        /// Caudal máximo de la bomba (m³/s)
        /// </summary>
        double MaxFlow { get; set; }
        
        /// <summary>
        /// Velocidad relativa de la bomba (0.0 a 1.0)
        /// </summary>
        double SpeedRatio { get; set; }
        
        /// <summary>
        /// Indica si la bomba está encendida
        /// </summary>
        bool IsRunning { get; set; }
        
        /// <summary>
        /// Dirección de la bomba (+1 o -1)
        /// </summary>
        int PumpDirection { get; set; }
    }
    
    /// <summary>
    /// Interfaz para objetos que son válvulas hidráulicas
    /// </summary>
    public interface IHydraulicValve : IHydraulicComponent, IHydraulicFlowReceiver, IHydraulicPressureReceiver
    {
        /// <summary>
        /// Coeficiente Kv de la válvula completamente abierta (m³/h/√bar)
        /// </summary>
        double KvFull { get; set; }
        
        /// <summary>
        /// Apertura actual de la válvula (0.0 a 1.0)
        /// </summary>
        double Opening { get; set; }
        
        /// <summary>
        /// Indica si la válvula está completamente cerrada
        /// </summary>
        bool IsClosed { get; }
        
        /// <summary>
        /// Indica si la válvula está completamente abierta
        /// </summary>
        bool IsFullyOpen { get; }
    }
    
    /// <summary>
    /// Interfaz para objetos que son tuberías hidráulicas
    /// </summary>
    public interface IHydraulicPipe : IHydraulicComponent, IHydraulicFlowReceiver, IHydraulicPressureReceiver
    {
        /// <summary>
        /// Longitud de la tubería (m)
        /// </summary>
        double Length { get; set; }
        
        /// <summary>
        /// Diámetro interno de la tubería (m)
        /// </summary>
        double Diameter { get; set; }
        
        /// <summary>
        /// Rugosidad absoluta de la tubería (m)
        /// </summary>
        double Roughness { get; set; }
    }
    
    /// <summary>
    /// Interfaz para objetos que son tanques hidráulicos
    /// </summary>
    public interface IHydraulicTank : IHydraulicComponent, IHydraulicPressureReceiver
    {
        /// <summary>
        /// Presión del tanque (Pa)
        /// </summary>
        double TankPressure { get; set; }
        
        /// <summary>
        /// Nivel actual del tanque (m)
        /// </summary>
        double Level { get; set; }
        
        /// <summary>
        /// Área de la sección transversal del tanque (m²)
        /// </summary>
        double CrossSectionalArea { get; set; }
        
        /// <summary>
        /// Indica si el tanque tiene presión fija
        /// </summary>
        bool IsFixedPressure { get; }
    }
    
    /// <summary>
    /// Definición de un nodo hidráulico
    /// </summary>
    public class HydraulicNodeDefinition
    {
        /// <summary>
        /// Nombre único del nodo
        /// </summary>
        public string Name { get; set; }
        
        /// <summary>
        /// Indica si el nodo tiene presión fija
        /// </summary>
        public bool IsFixedPressure { get; set; }
        
        /// <summary>
        /// Presión del nodo (Pa), si es de presión fija
        /// </summary>
        public double? Pressure { get; set; }
        
        /// <summary>
        /// Descripción del nodo
        /// </summary>
        public string Description { get; set; } = "";
        
        public HydraulicNodeDefinition(string name, bool isFixedPressure = false, double? pressure = null, string description = "")
        {
            Name = name;
            IsFixedPressure = isFixedPressure;
            Pressure = pressure;
            Description = description;
        }
    }
    
    /// <summary>
    /// Definición de un elemento hidráulico
    /// </summary>
    public class HydraulicElementDefinition
    {
        /// <summary>
        /// Nombre único del elemento
        /// </summary>
        public string Name { get; set; }
        
        /// <summary>
        /// Nodo de origen
        /// </summary>
        public string FromNode { get; set; }
        
        /// <summary>
        /// Nodo de destino
        /// </summary>
        public string ToNode { get; set; }
        
        /// <summary>
        /// Elemento hidráulico
        /// </summary>
        public Element Element { get; set; }
        
        /// <summary>
        /// Descripción del elemento
        /// </summary>
        public string Description { get; set; } = "";
        
        public HydraulicElementDefinition(string name, string fromNode, string toNode, Element element, string description = "")
        {
            Name = name;
            FromNode = fromNode;
            ToNode = toNode;
            Element = element;
            Description = description;
        }
    }
    
    /// <summary>
    /// Tipos de elementos hidráulicos
    /// </summary>
    public enum HydraulicElementType
    {
        Pipe,
        Pump,
        Valve,
        MinorLoss,
        Tank,
        Junction
    }
    
    /// <summary>
    /// Configuración de un elemento hidráulico
    /// </summary>
    public class HydraulicElementConfiguration
    {
        public HydraulicElementType Type { get; set; }
        public Dictionary<string, object> Parameters { get; set; } = new Dictionary<string, object>();
        
        public HydraulicElementConfiguration(HydraulicElementType type)
        {
            Type = type;
        }
        
        /// <summary>
        /// Establece un parámetro de configuración
        /// </summary>
        public void SetParameter(string key, object value)
        {
            Parameters[key] = value;
        }
        
        /// <summary>
        /// Obtiene un parámetro de configuración
        /// </summary>
        public T GetParameter<T>(string key, T defaultValue = default(T))
        {
            if (Parameters.ContainsKey(key) && Parameters[key] is T)
            {
                return (T)Parameters[key];
            }
            return defaultValue;
        }
    }
}
