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

use crate::Kind;

/// Represents a lexical token.
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub struct Token<'a> {
    kind: Kind,
    value: &'a str,
    line: usize,
    position: usize,
}

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

    /// Creates a new token.
    pub const fn new(kind: Kind, value: &'a str, line: usize, position: usize) -> Self {
        Self {
            kind,
            value,
            line,
            position,
        }
    }
}

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

    /// Gets the kind of token.
    pub const fn kind(&self) -> Kind {
        self.kind
    }

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

    /// Gets the 0-indexed line number.
    pub const fn line(&self) -> usize {
        self.line
    }

    /// Gets the 0-indexed position of the first byte within the line.
    pub const fn position(&self) -> usize {
        self.position
    }
}

impl<'a> Display for Token<'a> {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        write!(f, "[{}, ", self.kind)?;
        self.kind.fmt_value(f, self.value)?;
        write!(f, ", {}, {}]", self.line, self.position)
    }
}
