// SPDX-FileCopyrightText: 2026 dcel contributors
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use std::collections::{BTreeMap, BTreeSet};

use maplike::Get;

use crate::{Dcel, EdgeId, Face, FaceId, HalfEdge, HalfEdgeId, Vertex, VertexId};

#[derive(Debug)]
pub struct HalfEdgesCounter {
    map: BTreeMap<usize, usize>,
}

impl HalfEdgesCounter {
    pub fn new() -> Self {
        Self {
            map: BTreeMap::new(),
        }
    }

    pub fn visit_face_edges<
        VW,
        HEW,
        FW,
        VC,
        HEC: Get<usize, Value = HalfEdge<HEW>>,
        FC: Get<usize, Value = Face<FW>>,
    >(
        &mut self,
        dcel: &Dcel<VW, HEW, FW, VC, HEC, FC>,
        face: FaceId,
    ) {
        for edge in dcel.face_edges(face) {
            self.visit_edge(edge);
        }
    }

    pub fn visit_face_half_edges<
        VW,
        HEW,
        FW,
        VC,
        HEC: Get<usize, Value = HalfEdge<HEW>>,
        FC: Get<usize, Value = Face<FW>>,
    >(
        &mut self,
        dcel: &Dcel<VW, HEW, FW, VC, HEC, FC>,
        face: FaceId,
    ) {
        for half_edge in dcel.face_half_edges(face) {
            self.visit_half_edge(half_edge);
        }
    }

    pub fn visit_half_edge(&mut self, half_edge: HalfEdgeId) {
        *self.map.entry(half_edge.id()).or_default() += 1;
    }

    pub fn visit_edge(&mut self, edge: EdgeId) {
        self.visit_half_edge(edge.lesser());
        self.visit_half_edge(edge.greater());
    }

    pub fn visited_half_edges(&self) -> impl Iterator<Item = HalfEdgeId> {
        self.map.keys().map(|key| HalfEdgeId(*key))
    }

    pub fn visited_half_edge_counts(&self) -> impl Iterator<Item = (HalfEdgeId, usize)> {
        self.map
            .iter()
            .map(|(key, value)| (HalfEdgeId(*key), *value))
    }

    pub fn visited_edges<VW, HEW, FW, VC, HEC: Get<usize, Value = HalfEdge<HEW>>, FC>(
        &self,
        dcel: &Dcel<VW, HEW, FW, VC, HEC, FC>,
    ) -> impl Iterator<Item = EdgeId> {
        let mut deduplicating_set = BTreeSet::new();

        self.visited_half_edges().filter_map(move |half_edge| {
            if deduplicating_set.contains(&half_edge.id()) {
                return None;
            }

            deduplicating_set.insert(half_edge.id());
            Some(dcel.full_edge(half_edge))
        })
    }

    pub fn inner_edges<VW, HEW, FW, VC, HEC: Get<usize, Value = HalfEdge<HEW>>, FC>(
        &self,
        dcel: &Dcel<VW, HEW, FW, VC, HEC, FC>,
    ) -> impl Iterator<Item = EdgeId> {
        self.visited_edges::<VW, HEW, FW, VC, HEC, FC>(dcel)
            .filter(|edge| self.is_inner_edge(*edge))
    }

    pub fn outer_edges<VW, HEW, FW, VC, HEC: Get<usize, Value = HalfEdge<HEW>>, FC>(
        &self,
        dcel: &Dcel<VW, HEW, FW, VC, HEC, FC>,
    ) -> impl Iterator<Item = (HalfEdgeId, HalfEdgeId)> {
        self.visited_half_edges().filter_map(move |half_edge| {
            let twin = dcel.twin(half_edge);

            if self.half_edge_visit_count(twin) == 0 {
                Some((half_edge, twin))
            } else {
                None
            }
        })
    }

    pub fn edge_visit_count(&self, edge: EdgeId) -> usize {
        self.half_edge_visit_count(edge.lesser()) + self.half_edge_visit_count(edge.greater())
    }

    pub fn half_edge_visit_count(&self, half_edge: HalfEdgeId) -> usize {
        self.map.get(&half_edge.id()).copied().unwrap_or_default()
    }

    pub fn is_inner_edge(&self, edge: EdgeId) -> bool {
        self.half_edge_visit_count(edge.lesser()) >= 1
            && self.half_edge_visit_count(edge.greater()) >= 1
    }

    pub fn is_outer_edge(&self, edge: EdgeId) -> bool {
        self.half_edge_visit_count(edge.lesser()) <= 0
            || self.half_edge_visit_count(edge.greater()) <= 0
    }
}

#[derive(Debug)]
pub struct VertexesCounter {
    map: BTreeMap<usize, usize>,
}

impl VertexesCounter {
    pub fn new() -> Self {
        Self {
            map: BTreeMap::new(),
        }
    }

    pub fn visit_face_vertices<
        VW,
        HEW,
        FW,
        VC: Get<usize, Value = Vertex<VW>>,
        HEC: Get<usize, Value = HalfEdge<HEW>>,
        FC: Get<usize, Value = Face<FW>>,
    >(
        &mut self,
        dcel: &Dcel<VW, HEW, FW, VC, HEC, FC>,
        face: FaceId,
    ) {
        for vertex in dcel.face_vertices(face) {
            self.visit_vertex(vertex);
        }
    }

    pub fn visit_vertex(&mut self, vertex: VertexId) {
        *self.map.entry(vertex.id()).or_insert(0) += 1;
    }

    pub fn visited_vertices(&self) -> impl Iterator<Item = VertexId> {
        self.map.keys().map(|&id| VertexId(id))
    }
}

pub struct VertexTracker<VW> {
    map: BTreeMap<VW, VertexId>,
}

impl<VW: Eq + Ord> VertexTracker<VW> {
    pub fn new() -> Self {
        Self {
            map: BTreeMap::new(),
        }
    }

    pub fn visit_vertex(&mut self, weight: VW, vertex: VertexId) {
        self.map.insert(weight, vertex);
    }

    pub fn vertex(&mut self, weight: VW) -> Option<VertexId> {
        self.map.get(&weight).cloned()
    }
}

pub struct EdgesTracker {
    map: BTreeMap<(usize, usize), usize>,
}

impl EdgesTracker {
    pub fn new() -> Self {
        Self {
            map: BTreeMap::new(),
        }
    }

    pub fn visit_vertices_edge(&mut self, from: VertexId, to: VertexId, half_edge: HalfEdgeId) {
        self.map.insert((from.id(), to.id()), half_edge.id());
    }

    pub fn vertices_half_edge(&self, from: VertexId, to: VertexId) -> Option<HalfEdgeId> {
        self.map
            .get(&(from.id(), to.id()))
            .map(|id| HalfEdgeId(*id))
    }
}
