Documentation
use quote::{quote, quote_spanned,format_ident};
use proc_macro::{TokenStream};
use syn::{Data,parse_macro_input, parse_quote, DeriveInput, NestedMeta, Lit, Field};
use proc_macro_roids::{DeriveInputStructExt, FieldExt, DeriveInputExt};
use syn::spanned::Spanned;




#[proc_macro_derive(build,attributes(cmd))]
pub fn make(input :TokenStream)->TokenStream{
    let derive_input = parse_macro_input!(input as DeriveInput);
    impl_default(&derive_input)
}

fn impl_default(derive_input: &syn::DeriveInput) ->TokenStream{
    let name = &derive_input.ident;
    let typeid={
        match derive_input.tag_parameter(&parse_quote!(cmd), &parse_quote!(typeid)){
            Some(value)=> {
                match value {
                    NestedMeta::Lit(value) => {
                        match value {
                            Lit::Int(v) => {
                                Some(v.to_string().parse::<u16>().expect("typeid type error not u16"))
                            },
                            _ => panic!("typeid type error not u16")
                        }
                    },
                    _=>panic!("typeid type error")
                }
            },
            _=>None
        }
    };


    let is_compatible= match derive_input.tag_parameter(&parse_quote!(cmd), &parse_quote!(compatible)){
        Some(NestedMeta::Lit(Lit::Bool(v)))=>{
          v.value
        },
        None=>false,
        _=>false
    };

    let write= derive_input.fields().iter().filter(|f|{!f.is_phantom_data()}).map(|f|{
        let name=&f.ident;
        quote_spanned! {
            f.span()=>  om.write_(data,&self.#name)?;
        }

    });


    let defs=  derive_input.fields().iter().filter(|f|{!f.is_phantom_data()}).map(|f|{
        let name=&f.ident;
        if let Some(x)= f.tag_parameter(&parse_quote!(cmd),&parse_quote!(default))
        {
            let default=get_fmt_default(f, x);
            quote_spanned! {
                   f.span()=> #name: #default,
                }
        }
        else {
            quote_spanned! {
                   f.span()=>  #name: ::core::default::Default::default(),
            }
        }
    });


    return if !is_compatible {
        let read = derive_input.fields().iter().filter(|f| { !f.is_phantom_data() }).map(|f| {
            let name = &f.ident;
            quote_spanned! {
                   f.span()=>  om.read_(data, &mut self.#name)?;
            }
        });

        if let Some(typeid)=typeid {
            let const_type_id_name=format_ident!("{}_ID",name.to_string().to_uppercase());
            let expanded = quote! {
                pub const #const_type_id_name:u16 = #typeid;
                impl xxlib::ISerdeTypeId for #name{
                    #[inline(always)]
                    fn type_id() -> u16 where Self: Sized {
                        #typeid
                    }
                    #[inline]
                    fn get_type_id(&self) -> u16 {
                       Self::type_id()
                    }
                }
                impl xxlib::ISerde for #name {
                    #[inline]
                    fn write_to(&self,om: &xxlib::ObjectManager,data: &mut xxlib::Data)->anyhow::Result<()> {
                        #( #write)*
                        Ok(())
                    }
                    #[inline]
                    fn read_from(&mut self,om: &xxlib::ObjectManager, data:&mut xxlib::DataReader) -> anyhow::Result<()> {
                        #( #read)*
                        Ok(())
                    }
                }
                #[automatically_derived]
                #[allow(unused_qualifications)]
                impl ::core::default::Default for #name {
                    #[inline]
                    fn default() -> #name {
                        #name {
                              #( #defs)*
                        }
                    }
                }
                impl ToString for #name{
                    #[inline]
                    fn to_string(&self) -> String {
                         ::std::format!("{:?}",self)
                    }
                }
            };

            TokenStream::from(expanded)
        }else {
            let expanded = quote! {
                impl xxlib::IStruct for #name {
                    #[inline]
                    fn write_to(&self,om: &xxlib::ObjectManager,data: &mut xxlib::Data)->anyhow::Result<()> {
                        #( #write)*
                        Ok(())
                    }
                    #[inline]
                    fn read_from(&mut self,om: &xxlib::ObjectManager, data:&mut xxlib::DataReader) -> anyhow::Result<()> {
                        #( #read)*
                        Ok(())
                    }
                }
                impl xxlib::IWriteInner for #name{
                    #[inline]
                    fn write_(&self, om: &ObjectManager, data: &mut Data) -> anyhow::Result<()> {
                        self.write_to(om,data)
                    }
                }
                impl xxlib::IReadInner for #name{
                    #[inline]
                    fn read_(&mut self, om: &ObjectManager, data: &mut DataReader) -> anyhow::Result<()> {
                        self.read_from(om,data)
                    }
                }
                #[automatically_derived]
                #[allow(unused_qualifications)]
                impl ::core::default::Default for #name {
                    #[inline]
                    fn default() -> #name {
                        #name {
                              #( #defs)*
                        }
                    }
                }

                impl ToString for #name{
                    #[inline]
                    fn to_string(&self) -> String {
                         ::std::format!("{:?}",self)
                    }
                }
            };
            TokenStream::from(expanded)
        }
    }else{
        let read = derive_input.fields().iter().filter(|f| { !f.is_phantom_data() }).map(|f| {
            let name = &f.ident;
            let default= if let Some(x)= f.tag_parameter(&parse_quote!(cmd),&parse_quote!(default)){
                let set_default=get_fmt_default(f, x);
                quote_spanned! {
                   f.span()=>   self.#name= #set_default;
                }
            }else{
                quote_spanned! {
                   f.span()=>   self.#name=  ::core::default::Default::default();
                }
            };
            quote! {
                    if read.len()>0 {
                        om.read_(&mut read, &mut self.#name)?;
                    }else{
                        #default;
                    }
                }
        });
        if let Some(typeid)=typeid {
            let expanded = quote! {
                impl xxlib::ISerdeTypeId for #name{
                    #[inline(always)]
                    fn type_id() -> u16 where Self: Sized {
                        #typeid
                    }
                    #[inline]
                    fn get_type_id(&self) -> u16 {
                       Self::type_id()
                    }
                }
                impl xxlib::ISerde for #name {
                    #[inline]
                    fn write_to(&self,om: &xxlib::ObjectManager,data: &mut xxlib::Data)->anyhow::Result<()> {
                        let bak=data.len();
                        data.write_fixed(&0u32);
                        #( #write)*
                        data.write_fixed_at(bak,(data.len()-bak) as u32)?;
                        Ok(())
                    }
                    #[inline]
                    fn read_from(&mut self,om: &xxlib::ObjectManager, data:&mut xxlib::DataReader) -> anyhow::Result<()> {
                        let end_offset = data.read_fixed::<u32>()? as usize - 4usize;
                        anyhow::ensure!(end_offset<=data.len(),"struct:'{}' read_from offset error end_offset:{} > have len:{}", core::any::type_name::<Self>(),end_offset,data.len());
                        let mut read = xxlib::DataReader::from(&data[..end_offset]);
                        #( #read)*
                        data.advance(end_offset)?;
                        Ok(())
                    }
                }
                #[automatically_derived]
                #[allow(unused_qualifications)]
                impl ::core::default::Default for #name {
                    #[inline]
                    fn default() -> #name {
                        #name {
                              #( #defs)*
                        }
                    }
                }
                impl ToString for #name{
                    #[inline]
                    fn to_string(&self) -> String {
                         ::std::format!("{:?}",self)
                    }
                }
            };
            TokenStream::from(expanded)
        }else {
            let expanded = quote! {
                impl xxlib::IStruct for #name {
                    #[inline]
                    fn write_to(&self,om: &xxlib::ObjectManager,data: &mut xxlib::Data)->anyhow::Result<()> {
                        let bak=data.len();
                        data.write_fixed(&0u32);
                        #( #write)*
                        data.write_fixed_at(bak,(data.len()-bak) as u32)?;
                        Ok(())
                    }
                    #[inline]
                    fn read_from(&mut self,om: &xxlib::ObjectManager, data:&mut xxlib::DataReader) -> anyhow::Result<()> {
                        let end_offset = data.read_fixed::<u32>()? as usize - 4usize;
                        anyhow::ensure!(end_offset<=data.len(),"struct:'{}' read_from offset error end_offset:{} > have len:{}", core::any::type_name::<Self>(),end_offset,data.len());
                        let mut read = xxlib::DataReader::from(&data[..end_offset]);
                        #( #read)*
                        data.advance(end_offset)?;
                        Ok(())
                    }
                }
                impl xxlib::IWriteInner for #name{
                    #[inline]
                    fn write_(&self, om: &ObjectManager, data: &mut Data) -> anyhow::Result<()> {
                        self.write_to(om,data)
                    }
                }
                impl xxlib::IReadInner for #name{
                    #[inline]
                    fn read_(&mut self, om: &ObjectManager, data: &mut DataReader) -> anyhow::Result<()> {
                        self.read_from(om,data)
                    }
                }
                #[automatically_derived]
                #[allow(unused_qualifications)]
                impl ::core::default::Default for #name {
                    #[inline]
                    fn default() -> #name {
                        #name {
                              #( #defs)*
                        }
                    }
                }
                impl ToString for #name{
                    #[inline]
                    fn to_string(&self) -> String {
                         ::std::format!("{:?}",self)
                    }
                }
            };
            TokenStream::from(expanded)
        }
    }
}

fn get_fmt_default(f: &Field,  x: NestedMeta) -> proc_macro2::TokenStream {
    match x {
        NestedMeta::Lit(Lit::Int(v)) => {
            quote_spanned! {
                           f.span()=> #v
                        }
        }
        NestedMeta::Lit(Lit::Float(v)) => {
            quote_spanned! {
                           f.span()=> #v
                        }
        }
        NestedMeta::Lit(Lit::Bool(v)) => {
            quote_spanned! {
                           f.span()=> #v
                        }
        }
        NestedMeta::Lit(Lit::Char(v)) => {
            quote_spanned! {
                           f.span()=> #v
                        }
        }
        NestedMeta::Lit(Lit::Str(v)) => {
            quote_spanned! {
                           f.span()=> #v.to_string()
                        }
        },
        NestedMeta::Meta(value) => {
            quote_spanned! {
                   f.span()=> #value
                }
        }
        _ => {
            quote_spanned! {
                           f.span()=> ::core::default::Default::default()
                        }
        }
    }
}

#[proc_macro_attribute]
pub fn build_enum(args:TokenStream, input: TokenStream) -> TokenStream {
    let number_type =  format_ident!("{}",args.to_string().trim());
    let ast:syn::DeriveInput = syn::parse(input).unwrap();
    let name=&ast.ident;
    let expanded = if let Data::Enum(_) = ast.data {
        quote! {
             #[repr(#number_type)]
             #[derive(Copy, Clone,Debug,Eq, PartialEq)]
             #ast
             impl xxlib::manager::IWriteInner for #name{
                #[inline]
                fn write_(&self, om: &ObjectManager, data: &mut Data) -> anyhow::Result<()> {
                    let v:#number_type=unsafe{
                         std::mem::transmute(*self)
                    };
                    om.write_(data,&v)?;
                    Ok(())
                }
            }
            impl xxlib::manager::IReadInner for #name{
                #[inline]
                fn read_(&mut self, om: &ObjectManager, data: &mut DataReader) -> anyhow::Result<()> {
                    let mut v:#number_type = 0;
                    om.read_(data,&mut v)?;
                    unsafe{
                        *self=std::mem::transmute(v)
                    }
                    Ok(())
                }
            }
            impl Default for #name{
                #[inline]
                fn default() -> Self {
                    unsafe{
                        std::mem::transmute::<#number_type,#name>(0)
                    }
                }
            }
        }
    } else {
        quote! {
            #ast
        }
    };

    TokenStream::from(expanded)
}