use strum::{AsRefStr, EnumString};

use crate::diagnostic::span::Span;


#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Token {
    pub value: TokenValue,
    pub span: Span,
}


#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TokenValue {
    IntLit(i64), // TODO: more literal types
    Ident(String),
    TypeIdent(TypeIdent),
    
    Plus, Minus, Star, Slash, Percent,
    PlusPlus, MinusMinus,
    PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual,
    Equal, DoubleEqual, Not, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or,

    LParen, RParen,
    LBrace, RBrace,
    LBracket, RBracket,
    Comma, Semicolon,

    If, Else, While, For, Return, Break, Continue, Static,

    Eof,
    Unrecognized,
}
impl TokenValue {
    pub fn as_type_ident(&self) -> Option<TypeIdent> {
        if let TokenValue::TypeIdent(t) = self {
            Some(t.clone())
        } else {
            None
        }
    }
    pub fn as_ident(&self) -> Option<String> {
        if let TokenValue::Ident(s) = self {
            Some(s.clone())
        } else {
            None
        }
    }
}
impl std::fmt::Display for TokenValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            TokenValue::IntLit(i) => write!(f, "literal int {}", i),
            TokenValue::Ident(s) => write!(f, "identifier {}", s),
            TokenValue::TypeIdent(t) => write!(f, "type {}", t.as_ref()),
            TokenValue::Plus => write!(f, "`+`"),
            TokenValue::Minus => write!(f, "`-`"),
            TokenValue::PlusPlus => write!(f, "`++`"),
            TokenValue::MinusMinus => write!(f, "`--`"),
            TokenValue::PlusEqual => write!(f, "`+=`"),
            TokenValue::MinusEqual => write!(f, "`-=`"),
            TokenValue::StarEqual => write!(f, "`*=`"),
            TokenValue::SlashEqual => write!(f, "`/=`"),
            TokenValue::PercentEqual => write!(f, "`%=`"),
            TokenValue::Star => write!(f, "`*`"),
            TokenValue::Slash => write!(f, "`/`"),
            TokenValue::Percent => write!(f, "`%`"),
            TokenValue::Equal => write!(f, "`=`"),
            TokenValue::DoubleEqual => write!(f, "`==`"),
            TokenValue::Not => write!(f, "`!`"),
            TokenValue::And => write!(f, "`&&`"),
            TokenValue::Or => write!(f, "`||`"),
            TokenValue::NotEqual => write!(f, "`!=`"),
            TokenValue::Less => write!(f, "`<`"),
            TokenValue::LessEqual => write!(f, "`<=`"),
            TokenValue::Greater => write!(f, "`>`"),
            TokenValue::GreaterEqual => write!(f, "`>=`"),
            TokenValue::LParen => write!(f, "`(`"),
            TokenValue::RParen => write!(f, "`)`"),
            TokenValue::LBrace => write!(f, "`{{`"),
            TokenValue::RBrace => write!(f, "`}}`"),
            TokenValue::LBracket => write!(f, "`[`"),
            TokenValue::RBracket => write!(f, "`]`"),
            TokenValue::Comma => write!(f, "`,`"),
            TokenValue::Semicolon => write!(f, "`;`"),
            TokenValue::If => write!(f, "if"),
            TokenValue::Else => write!(f, "else"),
            TokenValue::While => write!(f, "while"),
            TokenValue::For => write!(f, "for"),
            TokenValue::Return => write!(f, "return"),
            TokenValue::Break => write!(f, "break"),
            TokenValue::Continue => write!(f, "continue"),
            TokenValue::Static => write!(f, "static"),
            TokenValue::Eof => write!(f, "end of input"),
            TokenValue::Unrecognized => write!(f, "unrecognized"),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TokenKind {
    IntLit,
    Ident,
    TypeIdent,
    
    Plus, Minus, Star, Slash, Percent,
    PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual,
    Equal, DoubleEqual, NotEqual, Less, LessEqual, Greater, GreaterEqual,

    LParen, RParen,
    LBrace, RBrace,
    LBracket, RBracket,
    Comma, Semicolon,

    If, Else, While, For, Return, Break, Continue, Static,

    Eof,
    Unrecognized,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, AsRefStr)]
pub enum TypeIdent {
    #[strum(serialize = "int")]
    Int,
    #[strum(serialize = "void")]
    Void,
}
