Documentation
#![allow(warnings)]
use proc_macro::TokenStream;
use quote::quote;
use std::fmt::format;
use syn::{
    parse::{Parse, ParseStream},
    parse_macro_input,
    punctuated::Punctuated,
    spanned::Spanned,
    AngleBracketedGenericArguments, DataEnum, DataStruct, DeriveInput, GenericArgument, Ident,
    PathArguments, Token, Type, TypePath,
};

#[proc_macro_derive(RTS)]
pub fn derive_rts(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);

    let struct_id = &ast.ident;
    let struct_name = struct_id.to_string();
    let vis = match ast.vis {
        syn::Visibility::Public(_) => "export ",
        syn::Visibility::Restricted(_) => "",
        syn::Visibility::Inherited => "",
    };
    if let syn::Data::Struct(DataStruct {
        fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
        ..
    }) = ast.data
    {
     

        // eprintln!("{:#?}", vis);
        let field_id: Vec<_> = named.iter().map(|v| &v.ident).collect();
        let field_ty: Vec<_> = named.iter().map(|v| &v.ty).collect();
        let builder_fields = {
            quote!(
                #(#field_id: ::std::option::Option<#field_ty>)*
            )
        };
        let builder_fields_none: Vec<_> = named
            .iter()
            .map(|v| {
                let id = &v.ident;
                quote!(
                    #id: ::std::option::Option::None,
                )
            })
            .collect();

        let setter = {
            let mut ts = proc_macro2::TokenStream::new();
            for (id, ty) in field_id.iter().zip(field_ty.iter()) {
                let s = quote!(
                    fn #id(&mut self, #id: #ty) -> &mut Self {
                        self.#id = ::std::option::Option::Some(#id);
                        self
                    }
                );
                ts.extend(s);
            }
            ts
        };

        let field_set = {
            quote!(
                #(s+= ("  ".to_string() + stringify!(#field_id) + ": " + self.#field_id.to_type_name().as_str() + ";\n").as_str(););*
            )
        };

        return quote!(
            impl RTS for #struct_id{
                fn to_type_string(&self) -> String {
                    let mut s = #vis.into();
                    s+= "type ".into();
                    s += stringify!(#struct_id);
                    s +=  " = {\n";

                    #field_set;

                    s += "}\n";
                    s
                }

                fn to_type_name(&self)->String{
                    stringify!(#struct_id).into()
                }
            }
        )
        .into();
    } else {
        if let syn::Data::Enum(DataEnum {
            variants,
            // enum_token,
            // brace_token,
            ..
        }) = ast.data
        {
            let vars: Vec<_> = variants.iter().map(|v| &v.ident).collect();
            let vars_id: Vec<_> = (0..vars.len()).map(|i| i.to_string()).collect();
            // eprintln!("{:#?}", variants);
            // eprintln!("{:#?}", vars);

            let field_set = {
                quote!(
                    #(s+= ("  ".to_string() + stringify!(#vars) + " = "+ #vars_id+ ",\n").as_str(););*
                )
            };
            return quote!(
                impl RTS for #struct_id{
                    fn to_type_string(&self) -> String {
                        let mut s = #vis.into();
                        s += "declare enum ".into();
                        s += stringify!(#struct_id);
                        s += " {\n";

                        #field_set;

                        s += "}\n";
                        s
                    }

                    fn to_type_name(&self)->String{
                        stringify!(#struct_id).into()
                    }
                }
            )
            .into();
        } else {
        }
    }
    "".parse().unwrap()
}

#[derive(Debug)]
struct Args {
    vars: Vec<Ident>,
}

impl Parse for Args {
    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
        let vars = Punctuated::<Ident, Token![,]>::parse_terminated(input)?;
        Ok(Args {
            vars: vars.into_iter().collect(),
        })
    }
}

#[proc_macro]
pub fn rts_to_string(input: TokenStream) -> TokenStream {
    let args: Args = syn::parse_macro_input!(input as Args);
    // eprintln!("{:#?}", args);

    let s: Vec<_> = args
        .vars
        .iter()
        .map(|i| {
            let id = i.to_string();

            let stmt = if id.chars().next().unwrap().is_uppercase() {
                quote!(
                    v.push(#i::default().to_type_string());
                )
            } else {
                quote!(
                    v.push(#i.to_type_string());
                )
            };
            stmt
        })
        .collect();

    quote!({
        let mut v: Vec<String> = Vec::new();
        #(#s)*;
        v.join("\n")
    })
    .into()
}