// Copyright (C) 2025 Christian Mauduit <ufoot@ufoot.org>

use crate::gate::*;
use crate::mesh::*;
use crate::mesh_topo::topology::*;

/// A mesh wrapper that applies topology rules to filter connections.
///
/// `MeshWithTopology` combines a base mesh with topology rules to create dynamic,
/// conditional connectivity. It wraps any mesh and filters its successors based on
/// the topology's `allowed()` function.
///
/// # How It Works
///
/// When you query successors from a `MeshWithTopology`, it:
/// 1. Gets all successors from the underlying mesh
/// 2. Filters them using the topology's `allowed()` function
/// 3. Returns only the connections that are currently allowed
///
/// This allows the same base mesh to have different effective connectivity
/// based on the topology state.
///
/// # Lifetime
///
/// The mesh and topology are held by reference with lifetime `'a`, allowing
/// you to modify the topology externally and have those changes reflected
/// in the mesh's behavior.
///
/// # Example
///
/// ```
/// use shortestpath::{Mesh, Gradient, mesh_2d::Full2D, mesh_topo::{MeshWithTopology, TopologyBool}};
///
/// let base_mesh = Full2D::new(5, 5);
/// let mut topology = TopologyBool::new(true);
///
/// // Create mesh with topology
/// let mesh = MeshWithTopology::new(&base_mesh, &topology);
///
/// // Use for pathfinding
/// let mut gradient = Gradient::from_mesh(&mesh);
/// gradient.set_distance(12, 0.0);
/// gradient.spread(&mesh);
///
/// // Topology can be changed externally
/// topology.set(false); // Now all connections are blocked
/// ```
#[derive(Clone)]
pub struct MeshWithTopology<'a, IntoIter>
where
    IntoIter: Iterator<Item = Gate>,
{
    /// Reference to the underlying base mesh
    mesh: &'a dyn Mesh<IntoIter = IntoIter>,
    /// Reference to the topology that controls which connections are allowed
    topology: &'a dyn Topology,
}

impl<'a, IntoIter> std::fmt::Debug for MeshWithTopology<'a, IntoIter>
where
    IntoIter: Iterator<Item = Gate>,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("MeshWithTopology")
            .field("mesh", &"<dyn Mesh>")
            .field("topology", &"<dyn Topology>")
            .finish()
    }
}

impl<'a, IntoIter> MeshWithTopology<'a, IntoIter>
where
    IntoIter: Iterator<Item = Gate>,
{
    /// Creates a new mesh with topology rules applied.
    ///
    /// # Arguments
    ///
    /// * `mesh` - The base mesh providing the underlying connectivity
    /// * `topology` - The topology that determines which connections are allowed
    ///
    /// # Example
    ///
    /// ```
    /// use shortestpath::{mesh_2d::Full2D, mesh_topo::{MeshWithTopology, TopologyBool}};
    ///
    /// let base_mesh = Full2D::new(10, 10);
    /// let topology = TopologyBool::new(true);
    /// let mesh = MeshWithTopology::new(&base_mesh, &topology);
    /// ```
    pub fn new(
        mesh: &'a impl Mesh<IntoIter = IntoIter>,
        topology: &'a impl Topology,
    ) -> MeshWithTopology<'a, IntoIter> {
        MeshWithTopology { mesh, topology }
    }
}

impl<'a, IntoIter> Mesh for MeshWithTopology<'a, IntoIter>
where
    IntoIter: Iterator<Item = Gate>,
{
    type IntoIter = std::vec::IntoIter<Gate>;

    fn successors(&self, from: usize) -> std::vec::IntoIter<Gate> {
        self.mesh
            .successors(from)
            .filter(|to| self.topology.allowed(from, to.target).unwrap_or(false))
            .collect::<Vec<Gate>>()
            .into_iter()
    }

    fn len(&self) -> usize {
        self.mesh.len()
    }
}
