//! Adapter that removes directionality of edges of a directed graph.

use crate::core::{
    GraphBase, Neighbors,
    marker::{Direction, Undirected},
};

use gryf_derive::{EdgeSet, Guarantee, VertexSet};

/// Adapter that removes directionality of edges of a directed graph.
#[derive(Debug, VertexSet, EdgeSet, Guarantee)]
#[gryf_crate]
pub struct Undirect<G> {
    #[graph]
    graph: G,
}

impl<G> Undirect<G> {
    /// Creates a new graph which removes directionality of edges of the given
    /// graph.
    pub fn new(graph: G) -> Self {
        Self { graph }
    }

    /// Consumes the adapter and returns the wrapped graph.
    pub fn into_inner(self) -> G {
        self.graph
    }
}

impl<G> GraphBase for Undirect<G>
where
    G: GraphBase,
{
    type VertexId = G::VertexId;
    type EdgeId = G::EdgeId;
    type EdgeType = Undirected;
}

impl<G> Neighbors for Undirect<G>
where
    G: Neighbors,
{
    type NeighborRef<'a>
        = G::NeighborRef<'a>
    where
        Self: 'a;

    type NeighborsIter<'a>
        = G::NeighborsIter<'a>
    where
        Self: 'a;

    fn neighbors_undirected(&self, from: &Self::VertexId) -> Self::NeighborsIter<'_> {
        self.graph.neighbors_undirected(from)
    }

    fn neighbors_directed(
        &self,
        from: &Self::VertexId,
        _dir: Direction,
    ) -> Self::NeighborsIter<'_> {
        self.neighbors_undirected(from)
    }

    fn degree_undirected(&self, id: &Self::VertexId) -> usize {
        self.graph.degree_undirected(id)
    }

    fn degree_directed(&self, id: &Self::VertexId, _dir: Direction) -> usize {
        self.graph.degree_undirected(id)
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        core::{GraphAdd, marker::Directed},
        storage::AdjList,
    };

    use super::*;

    #[test]
    fn neighbors() {
        let mut graph = AdjList::<_, _, Directed, _>::default();

        let v0 = graph.add_vertex(());
        let v1 = graph.add_vertex(());
        let v2 = graph.add_vertex(());

        graph.add_edge(&v0, &v1, ());
        graph.add_edge(&v2, &v0, ());

        let graph = Undirect::new(graph);

        assert_eq!(
            graph.neighbors_directed(&v0, Direction::Outgoing).count(),
            2
        );
        assert_eq!(
            graph.neighbors_directed(&v0, Direction::Incoming).count(),
            2
        );
        assert_eq!(graph.degree_directed(&v0, Direction::Outgoing), 2);
        assert_eq!(graph.degree_directed(&v0, Direction::Incoming), 2);
    }
}
