use std::ops;

use crate::lexer::span::Span;

// We want to make an arena allocator for the AST, not just because of it's performance benefits but
// also because it allows us to associate an ID to every node.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeId(u32);

impl NodeId {
    pub const DUMMY: NodeId = NodeId(u32::MAX);
}

#[derive(Debug, Default)]
pub struct NodeIdCounter(u32);

impl NodeIdCounter {
    pub fn next(&mut self) -> NodeId {
        let id = NodeId(self.0);
        self.0 += 1;
        id
    }
}

#[derive(Debug, Clone)]
pub struct Node<T> {
    pub id: NodeId,
    pub value: T,
    pub span: Span,
}

impl<T> Node<T> {
    pub fn new(id: NodeId, value: T, span: Span) -> Self {
        Self { id, value, span }
    }

    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Node<U> {
        Node {
            id: self.id,
            value: f(self.value),
            span: self.span,
        }
    }
}

impl<T> ops::Deref for Node<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.value
    }
}
