use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Ident};

#[proc_macro]
pub fn hotreload(input: TokenStream) -> TokenStream {
    let game_struct = parse_macro_input!(input as Ident);

    let expanded = quote! {

        #[allow(clippy::not_unsafe_ptr_arg_deref)]
        #[unsafe(no_mangle)]
        pub extern "C" fn game_create() -> *mut std::ffi::c_void {
            let game = Box::new(#game_struct::default());
            Box::into_raw(game) as *mut std::ffi::c_void
        }

        #[allow(clippy::not_unsafe_ptr_arg_deref)]
        #[unsafe(no_mangle)]
        pub extern "C" fn game_destroy(game: *mut std::ffi::c_void) {
            if !game.is_null() {
                unsafe {
                    let _ = Box::from_raw(game as *mut #game_struct);
                }
            }
        }

        #[allow(clippy::not_unsafe_ptr_arg_deref)]
        #[unsafe(no_mangle)]
        pub extern "C" fn game_init(game: *mut std::ffi::c_void, engine: *mut ::roast2d::prelude::Engine) {
            if !game.is_null() && !engine.is_null() {
                unsafe {
                    let game = &mut *(game as *mut #game_struct);
                    let engine = &mut *engine;
                    game.init(engine);
                }
            }
        }

        #[allow(clippy::not_unsafe_ptr_arg_deref)]
        #[unsafe(no_mangle)]
        pub extern "C" fn game_update(game: *mut std::ffi::c_void, engine: *mut ::roast2d::prelude::Engine) {
            if !game.is_null() && !engine.is_null() {
                unsafe {
                    let game = &mut *(game as *mut #game_struct);
                    let engine = &mut *engine;
                    game.update(engine);
                }
            }
        }

        #[allow(clippy::not_unsafe_ptr_arg_deref)]
        #[unsafe(no_mangle)]
        pub extern "C" fn game_draw(game: *mut std::ffi::c_void, engine: *mut ::roast2d::prelude::Engine) {
            if !game.is_null() && !engine.is_null() {
                unsafe {
                    let game = &mut *(game as *mut #game_struct);
                    let engine = &mut *engine;
                    game.draw(engine);
                }
            }
        }

        #[allow(clippy::not_unsafe_ptr_arg_deref)]
        #[unsafe(no_mangle)]
        pub extern "C" fn game_cleanup(game: *mut std::ffi::c_void, engine: *mut ::roast2d::prelude::Engine) {
            if !game.is_null() && !engine.is_null() {
                unsafe {
                    let game = &mut *(game as *mut #game_struct);
                    let engine = &mut *engine;
                    game.cleanup(engine);
                }
            }
        }
    };

    TokenStream::from(expanded)
}
