using System;
using System.Collections.Generic;
using nkast.Aether.Physics2D.Common;
using tainicom.Aether.Physics2D.Fluids;
using CtrEditor.Simulacion.Fluids.Components;

namespace CtrEditor.Simulacion.Fluids
{
    /// <summary>
    /// Clase principal que gestiona la simulación de fluidos basada en FluidSystem2
    /// </summary>
    public class SimulacionFluidos
    {
        public FluidSystem2 SistemaFluido { get; private set; }
        private List<IContenedorFluido> _contenedores = new List<IContenedorFluido>();
        private int _worldWidth;
        private int _worldHeight;
        
        public int ParticlesCount => SistemaFluido?.ParticlesCount ?? 0;
        
        /// <summary>
        /// Constructor para el sistema de simulación de fluidos
        /// </summary>
        /// <param name="ancho">Ancho del área de simulación en metros</param>
        /// <param name="alto">Alto del área de simulación en metros</param>
        /// <param name="maxParticulas">Número máximo de partículas</param>
        /// <param name="gravedad">Vector de gravedad</param>
        public SimulacionFluidos(float ancho, float alto, int maxParticulas, Vector2 gravedad)
        {
            // Convertir parámetros a unidades internas de FluidSystem2
            _worldWidth = (int)(ancho * 100); // Convertir a cm
            _worldHeight = (int)(alto * 100); // Convertir a cm
            
            // Inicializar el sistema
            SistemaFluido = new FluidSystem2(
                gravedad,
                maxParticulas,
                _worldWidth,
                _worldHeight
            );

            // Configurar comportamiento del fluido
            SistemaFluido.ElasticityEnabled = true;
            SistemaFluido.PlasticityEnabled = true;
        }
        
        /// <summary>
        /// Método para agregar una partícula al sistema
        /// </summary>
        /// <param name="posicion">Posición en metros</param>
        public void AgregarParticula(Vector2 posicion)
        {
            // Convertir a sistema de coordenadas de FluidSystem2
            float xAjustado = posicion.X * 100 - _worldWidth/2;
            float yAjustado = posicion.Y * 100;
            
            SistemaFluido.AddParticle(new Vector2(xAjustado, yAjustado));
        }
        
        /// <summary>
        /// Agrega múltiples partículas en un área rectangular
        /// </summary>
        /// <param name="centro">Centro del área</param>
        /// <param name="ancho">Ancho del área</param>
        /// <param name="alto">Alto del área</param>
        /// <param name="cantidad">Cantidad de partículas a agregar</param>
        public void AgregarParticulasEnArea(Vector2 centro, float ancho, float alto, int cantidad)
        {
            Random rnd = new Random();
            for (int i = 0; i < cantidad; i++)
            {
                float x = centro.X - ancho/2 + (float)rnd.NextDouble() * ancho;
                float y = centro.Y - alto/2 + (float)rnd.NextDouble() * alto;
                AgregarParticula(new Vector2(x, y));
            }
        }
        
        /// <summary>
        /// Agrega un contenedor al sistema de fluidos
        /// </summary>
        /// <param name="contenedor">Implementación de IContenedorFluido</param>
        public void AgregarContenedor(IContenedorFluido contenedor)
        {
            _contenedores.Add(contenedor);
        }
        
        /// <summary>
        /// Elimina un contenedor del sistema de fluidos
        /// </summary>
        /// <param name="contenedor">Contenedor a eliminar</param>
        public void RemoverContenedor(IContenedorFluido contenedor)
        {
            _contenedores.Remove(contenedor);
        }
        
        /// <summary>
        /// Actualiza la simulación avanzando un paso de tiempo
        /// </summary>
        /// <param name="deltaTime">Tiempo transcurrido en segundos</param>
        public void Actualizar(float deltaTime)
        {
            // Aplicar restricciones de contenedores personalizados
            foreach (var contenedor in _contenedores)
            {
                for (int i = 0; i < SistemaFluido.ParticlesCount; i++)
                {
                    var particula = SistemaFluido.Particles[i];
                    contenedor.RestringirParticula(particula);
                }
            }
            
            // Actualizar la física del sistema de fluidos
            SistemaFluido.Update(deltaTime);
        }
        
        /// <summary>
        /// Obtiene la densidad del fluido en una posición específica
        /// </summary>
        /// <param name="posicion">Posición en metros</param>
        /// <returns>Densidad relativa de fluido (0-1)</returns>
        public float ObtenerDensidadEnPosicion(Vector2 posicion)
        {
            // Convertir a sistema de coordenadas de FluidSystem2
            float xAjustado = posicion.X * 100 - _worldWidth/2;
            float yAjustado = posicion.Y * 100;
            Vector2 posAjustada = new Vector2(xAjustado, yAjustado);
            
            float densidadTotal = 0;
            int particulasCercanas = 0;
            
            // Buscar partículas cercanas y sumar sus densidades
            foreach (var p in SistemaFluido.Particles)
            {
                float distancia;
Vector2 posAjustadaRef = posAjustada;
Vector2 positionRef = p.Position;
Vector2.Distance(ref posAjustadaRef, ref positionRef, out distancia);
                if (distancia < FluidSystem2.InfluenceRadius)
                {
                    densidadTotal += p.Density;
                    particulasCercanas++;
                }
            }
            
            // Calcular densidad promedio, normalizada
            if (particulasCercanas > 0)
                return Math.Min(1.0f, densidadTotal / (particulasCercanas * FluidSystem2.DensityRest * 1.5f));
            
            return 0;
        }
        
        /// <summary>
        /// Ajusta la gravedad del sistema
        /// </summary>
        /// <param name="gravedad">Nuevo vector de gravedad</param>
        public void AjustarGravedad(Vector2 gravedad)
        {
            SistemaFluido.Gravity = gravedad;
        }
        
        /// <summary>
        /// Limpia todas las partículas del sistema
        /// </summary>
        public void LimpiarParticulas()
        {
            // No hay método clear() directo en FluidSystem2, recreamos el sistema
            SistemaFluido = new FluidSystem2(
                SistemaFluido.Gravity,
                SistemaFluido.MaxParticleLimit,
                _worldWidth,
                _worldHeight
            );
            
            // Restaurar configuración
            SistemaFluido.ElasticityEnabled = true;
            SistemaFluido.PlasticityEnabled = true;
        }
    }
    
    /// <summary>
    /// Interfaz para contenedores de fluido personalizados como tubos, tanques, etc.
    /// </summary>
    public interface IContenedorFluido
    {
        /// <summary>
        /// Aplica restricciones a una partícula para mantenerla dentro o fuera del contenedor
        /// </summary>
        void RestringirParticula(Particle particula);
    }
}
