use proc_macro::TokenStream;
use quote::{ToTokens, quote};
use syn::{DeriveInput, Meta, MetaList, parse_macro_input};

mod commands;
mod decodeable;
mod encodeable;
mod handler_module_attribute;

#[proc_macro_derive(Encodeable)]
pub fn derive_encodeable(item: TokenStream) -> TokenStream {
    encodeable::impl_encodeable(item)
}

#[proc_macro_derive(Decodeable)]
pub fn derive_decodeable(item: TokenStream) -> TokenStream {
    decodeable::impl_decodeable(item)
}

#[proc_macro_derive(GMInput)]
pub fn derive_commands(item: TokenStream) -> TokenStream {
    commands::impl_gm_input(item)
}

#[proc_macro_attribute]
pub fn handlers(_attr: TokenStream, input: TokenStream) -> TokenStream {
    handler_module_attribute::imp(input)
}

#[proc_macro_derive(ClientCmdID, attributes(id))]
pub fn derive_client_cmd_id(item: TokenStream) -> TokenStream {
    let input = parse_macro_input!(item as DeriveInput);
    let struct_name = input.ident;

    let id = match input.attrs.iter().find(|attr| attr.path().is_ident("id")) {
        Some(attr) => match attr.meta {
            Meta::List(MetaList { ref tokens, .. }) => tokens.into_token_stream(),
            _ => panic!("Invalid cmdid attribute value"),
        },
        _ => 0u16.into_token_stream(),
    };

    TokenStream::from(quote! {
        impl crate::ClientCmdID for #struct_name {
            const CMD_ID: u16 = #id;
        }
    })
}
