use lasso::Spur;
use logos::Lexer;

use {lasso::Rodeo, logos::Logos};

#[derive(Default)]
pub struct Lextras {
    pub interner: Rodeo,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum IntLit {
    Signed(i64),
    Unsigned(u64),
}

#[derive(Logos, Copy, Clone, Debug, PartialEq, Eq)]
#[logos(extras = Lextras)]
#[logos(skip r"[ \t\n\f]+")]
#[logos(skip r"-- .*")]
#[rustfmt::skip]
pub enum Token {
    #[token("(")] LeftParen,
    #[token(")")] RightParen,
    #[token("{")] LeftCurly,
    #[token("}")] RightCurly,
    #[token(".")] Dot,
    #[token(",")] Comma,
    #[token(":")] Colon,
    #[token(";")] Semicolon,
    #[token("_")] Underscore,

    #[token("←")]  //____
    #[token("<-")] LArrow,
    #[token("→")]  //____
    #[token("->")] RArrow,

    #[token(":>")] Pipe,

    #[token("+")] Plus,
    #[token("-")] Minus,
    #[token("*")] Star,
    #[token("/")] Slash,
    #[token("&")] And,
    #[token("|")] VLine,
    #[token("~")] Tilde,
    
    #[token("<")] Lt,
    #[token(">")] Gt,
    #[token("=")] Equ,
    #[token("≠") ] //__
    #[token("/=")] Nequ,
    #[token("≤") ] //___
    #[token("<=")] LtEqu,
    #[token("≥") ] //___,
    #[token(">=")] GtEqu,

    #[token("switch")] Switch,
    #[token("else")] Else,
    #[token("loop")] Loop,
    #[token("continue")] Continue,
    #[token("break")] Break,
    #[token("ret")] Ret,
    #[token("const")] Const,
    #[token("var")] Var,
    #[token("func")] Func,

    #[regex(
        r"\p{XID_Start}\p{XID_Continue}*",
        |l| l.extras.interner.get_or_intern(l.slice())
    )] Ident(Spur),

    #[token("»", better_string)]
    #[regex(
        "\"[^\"]*\"",
        |l| {
            let slice = l.slice();
            l.extras.interner.get_or_intern(&slice[1..slice.len() - 1])
        }
    )] String(Spur),

    #[regex(
        "-?[0-9]+",
        |l| {
            Some(if let Some(slice) = l.slice().strip_prefix('-') {
                IntLit::Signed(slice.parse::<i64>().ok()?)
            } else {
                IntLit::Unsigned(l.slice().parse::<u64>().ok()?)
            })
        }
    )] Int(IntLit),

    Invalid,
}

// For Evy, with love.
fn better_string(lexer: &mut Lexer<Token>) -> Option<Spur> {
    let mut count = 1;
    for (ix, chr) in lexer.remainder().char_indices() {
        match chr {
            '«' => count -= 1,
            '»' => count += 1,
            _ => (),
        }

        if count == 0 {
            let slice = &lexer.remainder()[..ix];
            lexer.bump(ix + '«'.len_utf8());
            return Some(lexer.extras.interner.get_or_intern(slice));
        }
    }
    None
}