extern crate proc_macro;
use proc_macro::{TokenStream,TokenTree,Ident,Delimiter};

use lookahead::LookAhead;

#[proc_macro_derive(CrimtagMappable)]
pub fn derive_crimtag_mappable(s: TokenStream) -> TokenStream {
    let mut i = LookAhead::<2,_,_>::new(s.into_iter());
    while let Some(t) = i.next() {
        if let TokenTree::Ident(ident) = t &&
            ident.to_string() == "struct" {
            break;
        }
    }
    let name = if let Some(TokenTree::Ident(ident)) = i.next() {
        ident.to_string()
    } else {
        panic!("No name found in struct.");
    };

    // TODO: Expand this to include the generic parameters and the where? at
    // least the parameters.
    //println!("struct name: {:?}", name);

    let mut newfunc = format!(
r#"impl<'a> crimtag::MappedStructure<'a> for {} {{
    fn get_value(&'a self, id: &str) -> Option<MappedValue<'a>> {{
        match id {{
"#, name);

    // We're going to cheat. Every field name is followed directly by a colon,
    // so all we really need to do is hunt for colons and then take the
    // previous item.

    while let Some(n) = i.next() {
        if let TokenTree::Group(g) = &n &&
            g.delimiter() == Delimiter::Brace {
            // Operate on the struct members.
            let mut gi = LookAhead::<2,_,_>::new(g.stream().into_iter());
            loop {
                if let Some(TokenTree::Punct(ch)) = gi.peek(1) &&
                    ch.as_char() == ':' &&
                    let Some(TokenTree::Ident(id)) = gi.peek(0) {
                    //println!("!!!!!!!!!! -> {}", id);
                    newfunc.push_str(&format!(
r#"            "{}" => Some((&self.{}).into()),
"#, id, id));
                }
                //println!(" -> {:?} ", gi.peek(0));
                //println!("    {:?}", gi.peek(1) );
                // Update next.
                if gi.next().is_none() {
                    break;
                }
            }
        }
    }
    
    newfunc.push_str(
r#"            _ => None,
        }
    }
}"#);
/*
    println!();
    println!();
    println!("{}", newfunc);
    println!();
    println!();
*/
    newfunc.parse().unwrap()
}

