use std::fmt::{Display, Error, Formatter};

use crate::Token;

/// Represents tokenized source code.
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub struct Lex<'a> {
    source: &'a str,
    tokens: Vec<Token<'a>>,
}

impl<'a> Lex<'a> {
    //! Constructors

    /// Creates a new lex.
    pub const fn new(source: &'a str, tokens: Vec<Token<'a>>) -> Self {
        Self { source, tokens }
    }
}

impl<'a> Lex<'a> {
    //! Properties

    /// Gets the raw source code.
    pub const fn source(&self) -> &str {
        self.source
    }

    /// Gets the tokens.
    pub const fn tokens(&self) -> &Vec<Token<'a>> {
        &self.tokens
    }
}

impl<'a> Display for Lex<'a> {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        let mut line: usize = 0;
        for token in &self.tokens {
            if line == token.line() {
                line += 1;
                writeln!(f, "line {}:", line)?;
            }
            let kind: String = token.kind().to_string();
            write!(f, "\t{: <6} {: <15} ", token.position(), kind)?;
            token.kind().fmt_value(f, token.value())?;
            writeln!(f)?;
        }
        Ok(())
    }
}
