use proc_macro_crate::{FoundCrate, crate_name};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::Token;
use syn::parse::{Parse, ParseStream};
use syn::token::Brace;
use syn::{
    Attribute, Field, FnArg, Ident, ImplItemFn, Pat, Type, Visibility, braced,
    parse_macro_input, parse_quote,
};

mod kw {
    syn::custom_keyword!(class);
}

#[derive(Debug, Clone)]
struct OverrideItem {
    _override_token: Token![override],
    override_class: Type,
    _brace_token: Brace,
    items: Vec<ImplItemFn>,
}

fn correct_priv_vis(vis: Visibility) -> Visibility {
    match vis {
        Visibility::Public(p) => Visibility::Public(p),
        Visibility::Restricted(mut restricted) => {
            if restricted.path.segments.get(0) != Some(&parse_quote!(crate)) {
                restricted.path.segments.insert(0, parse_quote!(super));
            }
            Visibility::Restricted(restricted)
        }
        Visibility::Inherited => {
            parse_quote!(pub(super))
        }
    }
}

impl Parse for OverrideItem {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let content;
        let override_token = input.parse()?;
        let override_class = input.parse()?;
        let brace_token = braced!(content in input);
        let mut items = Vec::new();
        while !content.is_empty() {
            let mut item: ImplItemFn = content.parse()?;
            item.vis = correct_priv_vis(item.vis);
            items.push(item);
        }

        Ok(OverrideItem {
            _override_token: override_token,
            override_class,
            _brace_token: brace_token,
            items,
        })
    }
}

#[derive(Debug, Clone)]
enum ClassItem {
    Field {
        field: Field,
        _semi_token: Token![;],
    },
    ImplItemFn(ImplItemFn),
    OverrideItem(OverrideItem),
}

impl Parse for ClassItem {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(Token![override]) {
            return Ok(ClassItem::OverrideItem(OverrideItem::parse(input)?));
        }

        let begin = input.fork();
        let _attrs = input.call(Attribute::parse_outer)?;
        let _vis: Visibility = begin.parse()?;
        let lookahead = begin.lookahead1();
        if lookahead.peek(Token![fn]) || lookahead.peek(Token![unsafe]) {
            return Ok(ClassItem::ImplItemFn(ImplItemFn::parse(input)?));
        }

        let mut field = Field::parse_named(input)?;
        field.vis = correct_priv_vis(field.vis);
        let semi_token = input.parse()?;
        Ok(ClassItem::Field {
            field,
            _semi_token: semi_token,
        })
    }
}

#[derive(Debug, Clone)]
struct SuperclassInput {
    _colon_token: Token![:],
    ty: Type,
}

impl Parse for SuperclassInput {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(SuperclassInput {
            _colon_token: input.parse()?,
            ty: input.parse()?,
        })
    }
}

#[derive(Debug, Clone)]
struct ClassInput {
    vis: Visibility,
    _class_token: kw::class,
    ident: Ident,
    superclass: Option<SuperclassInput>,
    _brace_token: Brace,
    items: Vec<ClassItem>,
}

impl Parse for ClassInput {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let content;
        let vis = input.parse()?;
        let class_token = input.parse()?;
        let ident = input.parse()?;

        let lookahead = input.lookahead1();
        let superclass = if lookahead.peek(Token![:]) {
            Some(input.parse()?)
        } else {
            None
        };

        let brace_token = braced!(content in input);
        let mut items = Vec::new();
        while !content.is_empty() {
            items.push(content.parse()?);
        }
        Ok(ClassInput {
            vis,
            _class_token: class_token,
            ident,
            superclass,
            _brace_token: brace_token,
            items,
        })
    }
}

#[derive(Debug, Clone)]
struct CrateAlias {
    _crate_token: Token![crate],
    _as_token: Token![as],
    ident: Ident,
    _semi_token: Token![;],
}

impl Parse for CrateAlias {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(CrateAlias {
            _crate_token: input.parse()?,
            _as_token: input.parse()?,
            ident: input.parse()?,
            _semi_token: input.parse()?,
        })
    }
}

struct ClassInputs {
    crate_alias: Option<CrateAlias>,
    inputs: Vec<ClassInput>,
}

impl Parse for ClassInputs {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let lookahead = input.lookahead1();
        let crate_alias = if lookahead.peek(Token![crate]) {
            Some(input.parse()?)
        } else {
            None
        };

        let mut inputs = Vec::new();
        while !input.is_empty() {
            inputs.push(input.parse()?);
        }

        Ok(ClassInputs {
            crate_alias,
            inputs,
        })
    }
}

fn plusplus() -> proc_macro2::TokenStream {
    let found_crate = crate_name("plusplus").expect("plusplus is present in `Cargo.toml`");
    match found_crate {
        FoundCrate::Itself => quote!(crate),
        FoundCrate::Name(name) => {
            let ident = Ident::new(&name, Span::call_site());
            quote!( #ident )
        }
    }
}

fn cast_class_ptr(
    plusplus: &proc_macro2::TokenStream,
    from: &Type,
    to: &Type,
    expr: impl Into<proc_macro2::TokenStream>,
) -> proc_macro2::TokenStream {
    let expr = expr.into();
    quote! {{
        let t: &#from = #expr;
        let self_size = std::mem::size_of_val(t);
        let target_size = std::mem::size_of::<#to<#plusplus::InConstruction>>();
        assert!(self_size >= target_size);
        let array_size = self_size - target_size;
        let target_ptr = std::ptr::slice_from_raw_parts(t as *const #from as *const u8, array_size);
        let target_ref = &*(target_ptr as *const #to);
        assert_eq!(self_size, std::mem::size_of_val(target_ref));
        target_ref
    }}
}

fn cast_class_ptr_mut(
    plusplus: &TokenStream,
    from: &Type,
    to: &Type,
    expr: impl Into<TokenStream>,
) -> TokenStream {
    let expr = expr.into();
    quote! {{
        let t: &mut #from = #expr;
        let self_size = std::mem::size_of_val(t);
        let target_size = std::mem::size_of::<#to<#plusplus::InConstruction>>();
        assert!(self_size >= target_size);
        let array_size = self_size - target_size;
        let target_ptr = std::ptr::slice_from_raw_parts_mut(t as *mut #from as *mut u8, array_size);
        let target_ref = &mut *(target_ptr as *mut #to);
        assert_eq!(self_size, std::mem::size_of_val(target_ref));
        target_ref
    }}
}

#[derive(Debug, Clone)]
struct FuncInfo {
    func: ImplItemFn,
    name: Ident,
    vtbl_name: Ident,
    sig: TokenStream,
    args: Vec<Box<Pat>>,
    mut_self: bool,
}

fn get_func_sig(class_name: &Type, f: &ImplItemFn) -> FuncInfo {
    let func_name = &f.sig.ident;

    let mut inputs = f.sig.inputs.clone();
    let mut mut_self = false;
    if let Some(FnArg::Receiver(rx)) = &f.sig.inputs.get(0) {
        let receiver = &mut inputs[0];
        if rx.mutability.is_some() {
            *receiver = FnArg::Typed(parse_quote!(this: &mut #class_name));
        } else {
            *receiver = FnArg::Typed(parse_quote!(this: &#class_name));
        }
        mut_self = rx.mutability.is_some();
    };

    let output = &f.sig.output;
    let func_sig = quote! {
        (#inputs) #output
    };
    let vtbl_name = format_ident!("fn_{func_name}");

    let func_args = inputs
        .into_iter()
        .skip(1)
        .map(|arg| match arg {
            FnArg::Receiver(_) => unreachable!(),
            FnArg::Typed(arg) => arg.pat,
        })
        .collect::<Vec<_>>();

    FuncInfo {
        func: f.clone(),
        name: func_name.clone(),
        vtbl_name,
        sig: func_sig,
        args: func_args,
        mut_self,
    }
}

struct ClassData {
    plusplus: TokenStream,
    class_name: Ident,
    class_type: Type,
    class_mod_name: Ident,
    vtbl_ident: Ident,
    class_vis: Visibility,
    fields: Vec<Field>,
    constructors: Vec<FuncInfo>,
    member_funcs: Vec<FuncInfo>,
    overrides: Vec<OverrideItem>,
    override_funcs: Vec<FuncInfo>,
    superclass_type: Option<Type>,
    mod_superclass_type: Option<Type>,
}

impl ClassData {
    fn from_input(input: ClassInput, crate_alias: Option<&CrateAlias>) -> ClassData {
        let ClassInput {
            vis: class_vis,
            _class_token: _,
            ident: class_name,
            superclass,
            _brace_token: _,
            items: class_items,
        } = input;

        let mut fields = Vec::new();
        let mut constructors = Vec::new();
        let mut member_funcs = Vec::new();
        let mut overrides = Vec::new();
        let mut override_funcs = Vec::new();

        let class_type: Type = parse_quote!(#class_name);

        for item in class_items {
            match item {
                ClassItem::Field { field, .. } => fields.push(field),
                ClassItem::ImplItemFn(func) => {
                    if let Some(FnArg::Receiver(_)) = func.sig.inputs.get(0) {
                        member_funcs.push(get_func_sig(&class_type, &func))
                    } else {
                        constructors.push(get_func_sig(&class_type, &func));
                    }
                }
                ClassItem::OverrideItem(override_item) => {
                    override_funcs.extend(override_item.items.iter().map(|f| get_func_sig(&class_type, &f)));
                    overrides.push(override_item);
                }
            }
        }

        let class_mod_name = format_ident!("plusplus__class_{}", class_name.to_string().to_lowercase());

        let plusplus = if let Some(alias) = crate_alias {
            let alias = &alias.ident;
            quote!(#alias)
        } else {
            plusplus()
        };

        let superclass_type = superclass.map(|sc| sc.ty);
        let mod_superclass_type = match superclass_type.clone() {
            Some(Type::Path(mut type_path)) => {
                if type_path.path.segments.get(0) != Some(&parse_quote!(crate)) {
                    type_path.path.segments.insert(0, parse_quote!(super));
                }
                Some(Type::Path(type_path))
            }
            ty => ty
        };

        ClassData {
            plusplus,
            vtbl_ident: format_ident!("{}Vtbl", class_name),
            class_vis,
            class_name,
            class_type,
            class_mod_name,
            fields,
            constructors,
            member_funcs,
            overrides,
            override_funcs,
            superclass_type,
            mod_superclass_type,
        }
    }

    fn has_superclass(&self) -> bool {
        self.superclass_type.is_some()
    }

    fn gen_mod_vtbl_struct(&self) -> TokenStream {
        // create vtbl
        let vtbl_ident = &self.vtbl_ident;
        let class_name = &self.class_name;

        let mut vtbl_func_names = Vec::new();
        let mut func_sigs = Vec::new();
        let mut my_func_names = Vec::new();

        for f in self.member_funcs.iter() {
            let FuncInfo {
                func: ImplItemFn{ .. },
                name: func_name,
                vtbl_name,
                sig: func_sig,
                args: _,
                mut_self: _,
            } = f;
            vtbl_func_names.push(vtbl_name);
            func_sigs.push(func_sig);
            let my_func_name = format_ident!("my_{func_name}");
            my_func_names.push(my_func_name);
        }

        let vtbl_drop_field: Option<_>;
        let vtbl_drop_func: Option<_>;
        let vtbl_drop_set: Option<_>;
        if !self.has_superclass() {
            vtbl_drop_field = Some(quote! {
                pub manually_drop: unsafe fn(*mut #class_name),
            });
            vtbl_drop_func = Some(quote! {
                unsafe fn manually_drop(this: *mut #class_name) {
                    unsafe{ std::ptr::drop_in_place(this) }
                }
            });
            vtbl_drop_set = Some(quote! {
                manually_drop,
            });
        } else {
            vtbl_drop_field = None;
            vtbl_drop_func = None;
            vtbl_drop_set = None;
        };

        quote! {
            #[doc(hidden)]
            pub struct #vtbl_ident {
                #vtbl_drop_field
                #(pub #vtbl_func_names: fn #func_sigs,)*
            }

            impl #vtbl_ident {
                const BASE: Self = {
                    #vtbl_drop_func

                    Self {
                        #vtbl_drop_set
                        #(#vtbl_func_names: #class_name::#my_func_names,)*
                    }
                };
            }
        }
    }

    fn gen_fn_set_vtbls(&self) -> TokenStream {
        let plusplus = &self.plusplus;
        let class_name = &self.class_name;

        let set_vtbls = self.overrides.iter().map(|ovr| {
            let ovr_class = &ovr.override_class;
            let ovr_class = match ovr_class.clone() {
                Type::Path(mut type_path) => {
                    type_path.path.segments.insert(0, parse_quote!(super));
                    Type::Path(type_path)
                }
                ty => ty
            };

            let mut ol_func_names = Vec::new();
            let mut ol_func_sigs = Vec::new();
            let mut ol_func_self_call_impls = Vec::new();
            for f in &ovr.items {
                let FuncInfo {
                    func: _,
                    name: func_name,
                    vtbl_name,
                    sig: func_sig,
                    args: func_args,
                    mut_self,
                } = get_func_sig(&ovr_class, f);
                ol_func_names.push(vtbl_name);
                ol_func_sigs.push(func_sig);

                let func_name = format_ident!("my_{}", func_name);
                let self_call = if mut_self {
                    let cast_mut = cast_class_ptr_mut(
                        &plusplus,
                        &ovr_class,
                        &self.class_type,
                        quote! {this},
                    );
                    quote! {
                        let this: &mut #class_name = unsafe{ #cast_mut };
                        this.#func_name(#(#func_args,)*)
                    }
                } else {
                    let cast = cast_class_ptr(&plusplus, &ovr_class, &self.class_type, quote! {this});
                    quote! {
                        let this: &#class_name = unsafe{ #cast };
                        this.#func_name(#(#func_args,)*)
                    }
                };
                ol_func_self_call_impls.push(self_call);
            }

            quote! {{
                let this: &mut #ovr_class = &mut *(unsafe{ self.to_constructed() });
                #(
                    fn #ol_func_names #ol_func_sigs {
                        #ol_func_self_call_impls
                    }
                    unsafe{ this.plusplus__vtbl_mut().#ol_func_names = #ol_func_names };
                )*
            }}
        });

        let root_type: Type = parse_quote!(<#class_name as #plusplus::Class>::RootClass);
        let cast_root_to_self =
            cast_class_ptr_mut(&plusplus, &root_type, &self.class_type, quote!(ref_mut));
        let root_type = &root_type;

        let set_subclass = self.has_superclass().then(|| quote!{
            unsafe{ self.superclass.plusplus__set_subclass(<#class_name as #plusplus::Class>::TYPE_ID) };

            {
                unsafe fn manually_drop(this: *mut #root_type) {
                    let ref_mut = unsafe{ &mut *this };
                    let this = unsafe{ #cast_root_to_self };
                    unsafe{ std::ptr::drop_in_place(this) };
                }
                let root_vtbl = unsafe{ <#class_name as #plusplus::Class>::root_class_mut(self.to_constructed()).plusplus__vtbl_mut() };
                root_vtbl.manually_drop = manually_drop;
            }
        });

        quote! {
            fn plusplus__set_vtbls(&mut self) {
                #set_subclass
                #(#set_vtbls)*
            }
        }
    }

    fn gen_mod_class_struct(&self) -> TokenStream {
        let plusplus = &self.plusplus;

        let superclass_field = self.mod_superclass_type.as_ref().map(|sc_type| {
            quote! {
                superclass: #sc_type<#plusplus::InConstruction>,
            }
        });
        let superclass_field = superclass_field.as_ref();
        let superclass_bound = self.mod_superclass_type.as_ref().map(|sc_ident| quote!{
            where #sc_ident: #plusplus::Class
        });

        let class_struct_vis = correct_priv_vis(self.class_vis.clone());
        let vtbl_ident = &self.vtbl_ident;
        let class_name = &self.class_name;
        let fields = &self.fields;
        let init_superclass_field = superclass_field.map(|f| quote!(pub #f));
        let init_fields = self.fields.iter().cloned().map(|f| Field {
            vis: Visibility::Public(parse_quote!(pub)),
            ..f
        });

        quote! {
            #[repr(C)]
            #class_struct_vis struct #class_name<C: ?Sized + #plusplus::ClassMemory = #plusplus::Constructed>
                #superclass_bound
            {
                #superclass_field
                vtbl: #vtbl_ident,
                subclass_id: Option<std::any::TypeId>,
                #(#fields,)*
                memory: C,
            }

            pub struct PlusPlus__InitClass {
                #init_superclass_field
                #(#init_fields,)*
            }
        }
    }

    fn gen_superclass_casters(&self) -> Option<TokenStream> {
        let Some(sc_type) = self.superclass_type.as_ref() else {
            return None;
        };
        let class_name = &self.class_name;

        let deref_upcast = cast_class_ptr(&self.plusplus, &self.class_type, sc_type, quote! {self});
        let deref_upcast_mut = cast_class_ptr_mut(
            &self.plusplus,
            &self.class_type,
            sc_type,
            quote! {self},
        );
        let ref_downcast = cast_class_ptr(&self.plusplus, sc_type, &self.class_type, quote! {self});
        let ref_downcast_mut = cast_class_ptr_mut(
            &self.plusplus,
            sc_type,
            &self.class_type,
            quote! {self},
        );

        let plusplus = &self.plusplus;
        Some(quote! {
            impl std::ops::Deref for #class_name {
                type Target = #sc_type;

                fn deref(&self) -> &Self::Target {
                    unsafe { #deref_upcast }
                }
            }

            impl std::ops::DerefMut for #class_name {
                fn deref_mut(&mut self) -> &mut Self::Target {
                    unsafe { #deref_upcast_mut }
                }
            }

            impl<'a> #plusplus::Downcast<#class_name> for &'a #sc_type {
                type Wrapped = &'a #class_name;
                fn downcast(self) -> Result<&'a #class_name, Self> {
                    use #plusplus::Class;
                    let subclass_type_id = <#class_name as #plusplus::Class>::TYPE_ID;
                    if self.subclass_id() == Some(subclass_type_id) {
                        Ok(unsafe{ #ref_downcast })
                    } else {
                        Err(self)
                    }
                }
            }

            impl<'a> #plusplus::Downcast<#class_name> for &'a mut #sc_type {
                type Wrapped = &'a mut #class_name;
                fn downcast(self) -> Result<&'a mut #class_name, Self> {
                    use #plusplus::Class;
                    let subclass_type_id = <#class_name as #plusplus::Class>::TYPE_ID;
                    if self.subclass_id() == Some(subclass_type_id) {
                        Ok(unsafe{ #ref_downcast_mut })
                    } else {
                        Err(self)
                    }
                }
            }
        })
    }

    fn gen_mod_impl_class_trait(&self) -> TokenStream {
        let plusplus = &self.plusplus;
        let class_name = &self.class_name;
        let root_class = self.mod_superclass_type
            .as_ref()
            .map(|sc_ident| quote! { <#sc_ident as #plusplus::Class>::RootClass })
            .unwrap_or_else(|| quote! { #class_name });

        quote! {
            unsafe impl #plusplus::Class for #class_name {
                const TYPE_ID: std::any::TypeId = std::any::TypeId::of::<#class_name>();

                type RootClass = #root_class;

                fn subclass_id(&self) -> Option<std::any::TypeId> {
                    self.subclass_id
                }

                fn root_class(&self) -> &Self::RootClass {
                    self
                }

                fn root_class_mut(&mut self) -> &mut Self::RootClass {
                    self
                }

                unsafe fn manually_drop(slot: &mut std::mem::ManuallyDrop<Self>) {
                    let as_root_class = slot.root_class_mut();
                    let manual_drop_fn = unsafe{ as_root_class.plusplus__vtbl_mut().manually_drop };
                    unsafe{ manual_drop_fn(as_root_class); }
                }
            }
        }
    }

    fn gen_mod_class_impl(&self) -> TokenStream {
        let class_name = &self.class_name;
        let vtbl_ident = &self.vtbl_ident;

        let mut call_vtbl_impls = Vec::new();
        for f in self.member_funcs.iter() {
            let FuncInfo {
                func: ImplItemFn{ vis, sig, ..},
                name: _,
                vtbl_name,
                sig: _,
                args: func_args,
                mut_self: _,
            } = f;

            let vis = correct_priv_vis(vis.clone());
            let call_vtbl = quote! {
                #vis #sig {
                    (self.vtbl.#vtbl_name)(self, #(#func_args,)*)
                }
            };
            call_vtbl_impls.push(call_vtbl);
        }

        let mut my_func_impls = Vec::new();
        let mut super_func_impls = Vec::new();

        for (f, is_override) in self.member_funcs
            .iter().cloned()
            .map(|f| (f, false))
            .chain(self.override_funcs.iter().cloned().map(|f| (f, true)))
        {
            let FuncInfo {
                func: ImplItemFn {
                    attrs: _,
                    vis,
                    defaultness: _,
                    sig,
                    block,
                },
                name: func_name,
                vtbl_name: _,
                sig: _,
                args: func_args,
                mut_self,
            } = f;

            let my_impl_name = format_ident!("my_{}", func_name);
            let mut my_impl_sig = sig.clone();
            my_impl_sig.ident = my_impl_name.clone();
            my_func_impls.push(quote! {
                #vis #my_impl_sig {
                    #block
                }
            });

            if is_override {
                let super_impl_name = format_ident!("super_{}", func_name);
                let mut super_impl_sig = sig.clone();
                super_impl_sig.ident = super_impl_name.clone();
                let super_impl_block = if mut_self {
                    quote! {
                        self.plusplus__super_mut().#my_impl_name(#(#func_args)*)
                    }
                } else {
                    quote! {
                        self.plusplus__super_ref().#my_impl_name(#(#func_args)*)
                    }
                };
                super_func_impls.push(quote! {
                    #vis #super_impl_sig {
                        #super_impl_block
                    }
                });
            }
        }

        let superclass_getters = self.mod_superclass_type.as_ref().map(|sc_ident| {
            quote! {
                fn plusplus__super_ref(&self) -> &#sc_ident {
                    self
                }

                fn plusplus__super_mut(&mut self) -> &mut #sc_ident {
                    self
                }
            }
        });

        quote!{
            impl #class_name {
                #(#call_vtbl_impls)*
                #(#super_func_impls)*

                #superclass_getters

                #[doc(hidden)]
                pub unsafe fn plusplus__vtbl_mut(&mut self) -> &mut #vtbl_ident {
                    &mut self.vtbl
                }
            }
        }
    }

    fn gen_mod_in_construction_class_impl(&self) -> TokenStream {
        let plusplus = &self.plusplus;
        let class_name = &self.class_name;
        let vtbl_ident = &self.vtbl_ident;

        let set_vtbl_func = self.gen_fn_set_vtbls();
        let class_vis = correct_priv_vis(self.class_vis.clone());

        let superclass_field = self.has_superclass().then(|| quote!{superclass: init.superclass,});
        let fields = self.fields.iter().map(|f| &f.ident).collect::<Vec<_>>();

        quote!{
            impl #class_name<#plusplus::InConstruction> {
                #set_vtbl_func

                pub(super) fn plusplus__new_from_init(init: PlusPlus__InitClass) -> Self {
                    let mut this = Self {
                        vtbl: #vtbl_ident::BASE,
                        memory: #plusplus::InConstruction::default(),
                        subclass_id: None,
                        #superclass_field
                        #(#fields: init.#fields,)*
                    };

                    this.plusplus__set_vtbls();

                    this
                }

                #[doc(hidden)]
                pub unsafe fn plusplus__set_subclass(&mut self, subclass_id: std::any::TypeId) {
                    self.subclass_id = Some(subclass_id);
                }

                /// Unsafe because caller must guarantee that vtbl doesn't contain any
                /// subclass methods
                pub unsafe fn to_constructed(&mut self) -> &mut #class_name {
                    unsafe{ &mut *(std::ptr::slice_from_raw_parts_mut::<u8>(self as *mut _ as *mut u8, 0) as *mut #class_name) }
                }

                /// Finish constructing this by moving it to the heap placing it in a `ClassBox`.
                ///
                /// Downcasting, upcasting, and deref coersions will work properly after calling this!
                #class_vis fn finish(self: #class_name<#plusplus::InConstruction>) -> #plusplus::ClassBox<#class_name> {
                    let boxed = Box::new(self);
                    let leaked = Box::leak(boxed);
                    let constructed = unsafe{ leaked.to_constructed() };
                    unsafe{ #plusplus::ClassBox::from_raw(constructed) }
                }
            }
        }
    }

    fn gen_init_class_macro(&self) -> TokenStream {
        let plusplus = &self.plusplus;
        let class_name = &self.class_name;
        let class_mod_name = &self.class_mod_name;

        quote! {
            macro_rules! init_class {
                ($($tt:tt)*) => {{
                    #class_name::<#plusplus::InConstruction>::plusplus__new_from_init(#class_mod_name::PlusPlus__InitClass {
                        $($tt)*
                    })
                }}
            }
        }
    }

    fn gen_class_impl(&self) -> TokenStream {
        let class_name = &self.class_name;
        let mut my_func_impls = Vec::new();
        for f in self.member_funcs
            .iter().cloned()
            .chain(self.override_funcs.iter().cloned())
        {
            let FuncInfo {
                func: ImplItemFn {
                    attrs: _,
                    vis,
                    defaultness: _,
                    sig,
                    block,
                },
                name: func_name,
                vtbl_name: _,
                sig: _,
                args: _,
                mut_self: _,
            } = f;

            let my_impl_name = format_ident!("my_{}", func_name);
            let mut my_impl_sig = sig.clone();
            my_impl_sig.ident = my_impl_name.clone();
            my_func_impls.push(quote! {
                #vis #my_impl_sig {
                    #block
                }
            });
        }

        let init_class_macro = self.gen_init_class_macro();
        let mut constructor_impls = Vec::new();
        for c in self.constructors.iter() {
            let ImplItemFn {
                attrs,
                vis,
                defaultness,
                sig,
                block,
            } = &c.func;

            let vis = correct_priv_vis(vis.clone());
            constructor_impls.push(quote! {
                #(#attrs)* #vis #defaultness #sig {
                    #init_class_macro
                    #block
                }
            });
        }

        quote!{
            impl #class_name {
                #(#constructor_impls)*
                #(#my_func_impls)*
            }
        }
    }

    fn gen_class(&self) -> TokenStream {
        let class_vis = &self.class_vis;
        let class_name = &self.class_name;
        let class_mod_name = &self.class_mod_name;
        let mod_vtbl_struct = self.gen_mod_vtbl_struct();
        let mod_class_struct = self.gen_mod_class_struct();
        let mod_impl_class_trait = self.gen_mod_impl_class_trait();
        let superclass_cast = self.gen_superclass_casters();
        let mod_class_impl = self.gen_mod_class_impl();
        let mod_in_construction_class_impl = self.gen_mod_in_construction_class_impl();
        let class_impl = self.gen_class_impl();

        quote! {
            #class_vis use #class_mod_name::#class_name;
            mod #class_mod_name {
                use super::*;
                #mod_vtbl_struct
                #mod_class_struct

                #mod_class_impl

                #mod_impl_class_trait

                #mod_in_construction_class_impl
            }

            #class_impl

            #superclass_cast
        }
    }
}

/// The whole point.
#[proc_macro]
pub fn class(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let inputs = parse_macro_input!(tokens as ClassInputs);
    let class_data = inputs.inputs.into_iter().map(|input| ClassData::from_input(input, inputs.crate_alias.as_ref()).gen_class());

    let output = quote!{
        #(#class_data)*
    };
    output.into()
}
