mod macros_lexer;
use std::{collections::HashMap, fmt::Debug};

use guarded::guarded_unwrap;
use macro_token_iterator::MacroArgs;
pub use macros_lexer::lex_rsml_macros;

mod macros_parser;
pub use macros_parser::parse_rsml_macros;

mod macro_token_iterator;
pub use macro_token_iterator::MacroTokenIterator;

use crate::{lex_rsml, Token};

pub type TokenPair<TokenType = Token> = (TokenType, String);

#[derive(Debug, Clone)]
pub struct Macro {
    pub token_pairs: Vec<TokenPair>,
    arg_places: Option<HashMap<usize, usize>>
}
impl<'a> Macro {
    fn new(body: String, args: Option<HashMap<&'a str, usize>>) -> Self {
        let mut lexer: logos::Lexer<'_, Token> = lex_rsml(&body);
        
        let mut token_pairs: Vec<TokenPair> = vec![];

        if let Some(args) = args {
            let mut token_pairs_idx = 0usize;
            let mut arg_places: HashMap<usize, usize> = HashMap::new();

            loop {
                let token = guarded_unwrap!(guarded_unwrap!(lexer.next(), break), continue);
                let token_slice = lexer.slice();

                if matches!(token, Token::ArgIdentifier) {
                    if let Some(Ok(next_token)) = lexer.next() {
                        let next_token_slice = lexer.slice();

                        if matches!(next_token, Token::Text) {
                            if let Some(arg_idx) = args.get(next_token_slice) {
                                arg_places.insert(token_pairs_idx, *arg_idx);

                            } else {
                                token_pairs.push((Token::Nil, "nil".to_string()));
                                token_pairs_idx += 1;
                            }

                        } else {
                            token_pairs.push((token, token_slice.to_string()));
                            token_pairs.push((next_token, next_token_slice.to_string()));
                            token_pairs_idx += 2;
                        }
                    }

                } else {
                    token_pairs.push((token, token_slice.to_string()));
                    token_pairs_idx += 1;
                }
            }

            Self {
                token_pairs,
                arg_places: Some(arg_places)
            }
            
        } else {
            loop {
                let token = guarded_unwrap!(guarded_unwrap!(lexer.next(), break), continue);
                let slice = lexer.slice().to_string();
                token_pairs.push((token, slice));
            }

            Self {
                token_pairs,
                arg_places: None
            }
        }
    }

    pub fn iter(
        &'a self,
        args_tokens: Option<Vec<Vec<(Token, &'a str)>>>
    ) -> MacroTokenIterator<'a> {
        if let Some(args_tokens) = args_tokens {
            if let Some(arg_places) = &self.arg_places {
                let args = Some(MacroArgs {
                    args_tokens, arg_places
                });

                return MacroTokenIterator::new(&self.token_pairs, args)
            }
        }

        MacroTokenIterator::new(&self.token_pairs, None)
    }
}

#[derive(Debug, Clone)]
pub struct MacroGroup(HashMap<String, HashMap<usize, Macro>>);

impl<'a> MacroGroup {
    pub fn new() -> Self {
        MacroGroup(HashMap::new())
    }

    fn insert(&mut self, name: &str, body: String, args: Option<HashMap<&'a str, usize>>) {
        let macro_map = self.0.entry(name.into()).or_insert(HashMap::new());
        if let Some(some_args) = &args {
            macro_map.insert(
                some_args.len(),
                Macro::new(body, args)
            );
        } else {
            macro_map.insert(0, Macro::new(body, None));
        };
    }

    pub fn get(&self, macro_name: &str, args_len: usize) -> Option<&Macro> {
        let macro_data = guarded_unwrap!(self.0.get(macro_name), return None);

        macro_data.get(&args_len)
    }
}



