use std::path::PathBuf;


#[derive(Clone)]
pub struct SourceSpan {
    /// The source characters which this span represents.
    pub string: String,
    /// The location of this span in the merged source file.
    pub in_merged: SourceLocation,
    /// The location of this span in the original source file.
    pub in_source: Option<SourceLocation>,
}

impl SourceSpan {
    pub fn location(&self) -> &SourceLocation {
        self.in_source.as_ref().unwrap_or(&self.in_merged)
    }
}


#[derive(Clone)]
pub struct SourceLocation {
    /// File path the source was loaded from.
    pub path: Option<PathBuf>,
    /// Position of the first character of the string.
    pub start: Position,
    /// Position of the final character of the string.
    pub end: Position,
}

impl std::fmt::Display for SourceLocation {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        let y = self.start.line + 1;
        let x = self.start.column + 1;
        match &self.path {
            Some(path) => write!(f, "{}:{y}:{x}", path.as_os_str().to_string_lossy()),
            None => write!(f, "<unknown>:{y}:{x}"),
        }
    }
}


#[derive(Clone, Copy)]
pub struct Position {
    /// The number of lines that precede this line in the source file.
    pub line: usize,
    /// The number of characters that precede this character in the line.
    pub column: usize,
}

impl Position {
    pub const ZERO: Self = Self { line: 0, column: 0 };

    pub fn to_next_char(&mut self) {
        self.column += 1;
    }

    pub fn to_next_line(&mut self) {
        self.line += 1;
        self.column = 0;
    }

    pub fn advance(&mut self, c: char) {
        match c {
            '\n' => self.to_next_line(),
            _ => self.to_next_char(),
        }
    }
}

impl std::fmt::Display for Position {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        let y = self.line + 1;
        let x = self.column + 1;
        write!(f, "{y}:{x}")
    }
}