use proc_macro::TokenStream;
use syn::{ItemFn, parse::Parse, Token, LitInt};
use quote::quote;

struct NativeFuncParams {
    arity: LitInt,
    variadic: Option<Token![..]>,
}

impl Parse for NativeFuncParams {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let arity = input.parse()?;
        let variadic = input.parse()?;
        Ok(Self { arity , variadic })
    }
}

#[proc_macro_attribute]
pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream {
    let itemfn: ItemFn = syn::parse(annotated_item).unwrap();

    let input: NativeFuncParams = syn::parse(input).unwrap();
    let arity = input.arity;
    let variadic = input.variadic.is_some();

    let visibility = itemfn.vis;
    let block = itemfn.block;
    let name = itemfn.sig.ident;
    let name_str = name.to_string();
    let inputs = itemfn.sig.inputs;
    let output = itemfn.sig.output;

    assert!(itemfn.sig.constness.is_none(), "item must not be const");
    assert!(itemfn.sig.asyncness.is_none(), "item must not be async");
    assert!(itemfn.sig.unsafety.is_none(), "item must not be unsafe");
    assert!(itemfn.sig.abi.is_none(), "item must not contain an ABI specifier");
    assert!(itemfn.sig.variadic.is_none(), "item must not be variadic");

    let expanded = quote! {
        #visibility fn #name() -> ::std::rc::Rc< ::matrix_lang::prelude::Function> {
            ::std::rc::Rc::new( ::matrix_lang::prelude::Function {
                name: ::std::rc::Rc::from( #name_str ),
                arity: #arity,
                variadic: #variadic,
                fun: ::matrix_lang::prelude::InnerFunction::Native(
                    ::std::rc::Rc::new(
                        |#inputs| #output #block
                    )
                )
            })
        }
    };

    TokenStream::from(expanded)
}