use std::ops::RangeInclusive;

/// A compact source span representation using u32 offsets.
///
/// This is 8 bytes vs 32 bytes for `Option<RangeInclusive<usize>>`.
/// Uses `(u32::MAX, u32::MAX)` as a sentinel for "no span".
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Span(u32, u32);

impl Span {
    /// Sentinel value representing no span.
    pub const NONE: Span = Span(u32::MAX, u32::MAX);

    /// Create a new span from start and end byte offsets.
    #[inline]
    pub fn new(start: usize, end: usize) -> Self {
        Span(start as u32, end as u32)
    }

    /// Create a span from a RangeInclusive.
    #[inline]
    pub fn from_range(range: RangeInclusive<usize>) -> Self {
        Span(*range.start() as u32, *range.end() as u32)
    }

    /// Check if this span is the "no span" sentinel.
    #[inline]
    pub fn is_none(&self) -> bool {
        self.0 == u32::MAX && self.1 == u32::MAX
    }

    /// Convert to Option<RangeInclusive<usize>> for compatibility.
    #[inline]
    pub fn to_range(&self) -> Option<RangeInclusive<usize>> {
        if self.is_none() {
            None
        } else {
            Some(self.0 as usize..=self.1 as usize)
        }
    }

    /// Get the start offset, or None if this is the sentinel.
    #[inline]
    pub fn start(&self) -> Option<usize> {
        if self.is_none() {
            None
        } else {
            Some(self.0 as usize)
        }
    }

    /// Get the end offset, or None if this is the sentinel.
    #[inline]
    pub fn end(&self) -> Option<usize> {
        if self.is_none() {
            None
        } else {
            Some(self.1 as usize)
        }
    }
}

impl From<RangeInclusive<usize>> for Span {
    fn from(range: RangeInclusive<usize>) -> Self {
        Span::from_range(range)
    }
}

impl From<Option<RangeInclusive<usize>>> for Span {
    fn from(opt: Option<RangeInclusive<usize>>) -> Self {
        match opt {
            Some(range) => Span::from_range(range),
            None => Span::NONE,
        }
    }
}

/// Trait for anything that can provide a source code span
pub trait Spannable {
    fn span(&self) -> Option<RangeInclusive<usize>>;
}

/// Blanket implementation for ANTLR parser rule contexts
impl<'a, T: antlr_rust::parser_rule_context::ParserRuleContext<'a>> Spannable for T {
    fn span(&self) -> Option<RangeInclusive<usize>> {
        Some(crate::antlr::interval(self))
    }
}
