use quote::{quote, ToTokens};
use syn::{parenthesized, parse::ParseStream, punctuated::Punctuated, Ident, Token};

use super::{common::Attributes, document::DocumentItemAttributes};

pub struct SyscallDefinition {
    pub attributes: Attributes,
    pub name: Ident,
    pub arguments: Punctuated<SyscallArgument, Token![,]>,
    pub return_type: Option<syn::Type>,
}

pub struct SyscallArgument {
    pub name: syn::Ident,
    pub ty: syn::Type,
}

impl DocumentItemAttributes for SyscallDefinition {
    fn extend_attributes<I: IntoIterator<Item = syn::Attribute>>(&mut self, attrs: I) {
        self.attributes.extend(attrs);
    }
}

impl syn::parse::Parse for SyscallDefinition {
    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
        let arguments;

        let name = input.parse()?;
        parenthesized!(arguments in input);
        let arguments = arguments.parse_terminated(SyscallArgument::parse, Token![,])?;

        let return_type = if input.peek(Token![->]) {
            input.parse::<Token![->]>()?;
            let ty = input.parse()?;
            Some(ty)
        } else {
            None
        };

        input.parse::<Token![;]>()?;

        Ok(Self {
            attributes: Attributes::new(),
            name,
            arguments,
            return_type,
        })
    }
}

impl syn::parse::Parse for SyscallArgument {
    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
        let name = input.parse()?;
        input.parse::<Token![:]>()?;
        let ty = input.parse()?;

        Ok(Self { name, ty })
    }
}

impl ToTokens for SyscallArgument {
    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        let Self { name, ty } = self;
        tokens.extend(quote!(#name: #ty))
    }
}

impl ToTokens for SyscallDefinition {
    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        let Self {
            attributes,
            name,
            arguments,
            return_type,
        } = self;
        let return_type = return_type.as_ref().map(|ty| quote!(-> #ty));
        tokens.extend(quote! {
            #attributes
            syscall #name (#arguments) #return_type;
        });
    }
}
