#ifndef ACMD_WRAPPER_H
#define ACMD_WRAPPER_H

#include <switch.h>

#include "imports/app/sv_animcmd.hpp"
#include "imports/app/sv_system.hpp"
#include "imports/app/sv_math.hpp"
#include "imports/app/lua_bind.hpp"
#include "imports/lib/l2c.hpp"

#include <initializer_list>

using namespace lib;

u64 load_module(u64 module_accessor, u64 module_offset) {
	return LOAD64(module_accessor + module_offset);
}

void* load_module_impl(u64 module, u64 function_offset) {
	u64 function_impl = LOAD64(module) + function_offset;
	return (void*) LOAD64(function_impl);
}

bool is_before_frame(u64 lua_state, float f) {
	u64 acmd_frame_obj = LOAD64(LOAD64(lua_state - 8) + 432LL);
	return *(float*)((*((u32 *)acmd_frame_obj + 64) + 15) & 0xFFFFFFF0) < f; 
}

struct ACMD {
	L2CAgent* l2c_agent;
	u64 module_accessor;

	ACMD(L2CAgent* agent) {
		l2c_agent = agent;
		module_accessor = app::sv_system::battle_object_module_accessor(l2c_agent->lua_state_agent);
	}

	void frame(float f) {
		l2c_agent->clear_lua_stack();
		L2CValue frame_val(f);
		l2c_agent->push_lua_stack(&frame_val);
		app::sv_animcmd::frame(l2c_agent->lua_state_agent, f);
		l2c_agent->clear_lua_stack();
	}

	// attempted reimplementation of sv_animcmd::frame
	bool _frame(float f) {
        u64 acmd_obj = LOAD64(LOAD64(l2c_agent->lua_state_agent - 8) + 432);
        if ( !is_before_frame(l2c_agent->lua_state_agent, f) )
            return true;
        
        *(u8*)(acmd_obj + 47) = 3;
        LOAD64(acmd_obj + 48) = (u64)&is_before_frame;
        *(float*)(acmd_obj + 56) = f;
        
        u64 acmd_obj_other = LOAD64(acmd_obj);
        if (*(u8*)(acmd_obj_other + 664)) {
            void (*some_func)(u64) = (void (*)(u64)) LOAD64(LOAD64(LOAD64(acmd_obj_other + 656)) + 16);
            some_func(LOAD64(acmd_obj_other + 656));
            return true;
        }

        bool doThing = false;

        if ( !*(u16*)(l2c_agent->lua_state_agent + 196)) {
            u64 v4 = LOAD64(l2c_agent->lua_state_agent + 32);
            *(u8*)(l2c_agent->lua_state_agent + 12) = 1;
            LOAD64(v4 + 56) = LOAD64(v4) - LOAD64(l2c_agent->lua_state_agent + 56);
            if ( *(u8*)(v4 + 66) & 2)
                return true;
            doThing = true;
        }


        if ( doThing || LOAD64(LOAD64(l2c_agent->lua_state_agent + 24) + 200) == l2c_agent->lua_state_agent) {
            // throw 
            u64 v4 = LOAD64(l2c_agent->lua_state_agent + 32);
            LOAD64(v4 + 32) = 0;
            LOAD64(v4) = LOAD64(l2c_agent->lua_state_agent + 16) - 16;
            
        }

        return false;

        // throw
    }

	void wait(float f) {
		l2c_agent->clear_lua_stack();
		L2CValue frame_val(f);
		l2c_agent->push_lua_stack(&frame_val);
		app::sv_animcmd::wait(l2c_agent->lua_state_agent, f);
		l2c_agent->clear_lua_stack();
	}

	bool is_excute() {
		l2c_agent->clear_lua_stack();
		app::sv_animcmd::is_excute(l2c_agent->lua_state_agent);
		L2CValue is_excute;
		l2c_agent->get_lua_stack(1, &is_excute);
		bool excute = (bool)(is_excute);
		l2c_agent->clear_lua_stack();
		return excute;
	}

	void wrap(u64 (*acmd_func)(u64), std::initializer_list<L2CValue> list) {
		l2c_agent->clear_lua_stack(); 
		for (L2CValue elem : list)
			l2c_agent->push_lua_stack(&elem);

		acmd_func(l2c_agent->lua_state_agent);
		l2c_agent->clear_lua_stack();    
	}

	void ATTACK(
		u64 i1,  // ID
		u64 i2,  // Part
		u64 h1,  // Bone
		float f1,  // Damage
		u64 i3,  // Angle
		u64 i4,  // KBG
		u64 i5,  // FKB
		u64 i6,  // BKB
		float f2,  // Size
		float f3,  // X
		float f4,  // Y
		float f5,  // Z
		// X2
		// Y2
		// Z2
		float f6,  // Hitlag
		float f7,  // SDI
		u64 i7,  // Clang/Rebound
		u64 i8,  // Facing Restriction
		u64 i9,  // Fixed Weight
		u64 i10, // Shield Damage
		float f8,  // Trip Chance
		u64 i11, // Rehite Rate
		u64 i12, // Reflectable
		u64 i13, // Absorbable
		u64 i14, // Flinchless
		u64 i15, // Disable Hitlag
		u64 i16, // Direct
		u64 i17, // Ground/Air
		u64 i18, // Hit Bits
		u64 i19, // Collision Bits
		u64 i20, // Friendly Fire
		u64 h2,  // Effect
		u64 i21, // SFX Level
		u64 i22, // SFX Type
		u64 i23) {  // Move Type
		wrap(app::sv_animcmd::ATTACK, {
			L2CValue(i1), L2CValue(i2), L2CValue(h1), L2CValue(f1),
			L2CValue(i3), L2CValue(i4), L2CValue(i5), L2CValue(i6),
			L2CValue(f2), L2CValue(f3), L2CValue(f4), L2CValue(f5),
			L2CValue("void"), L2CValue("void"), L2CValue("void"), L2CValue(f6),
			L2CValue(f7), L2CValue(i7), L2CValue(i8), L2CValue(i9),
			L2CValue(i10), L2CValue(f8), L2CValue(i11), L2CValue(i12),
			L2CValue(i13), L2CValue(i14), L2CValue(i15), L2CValue(i16),
 			L2CValue(i17), L2CValue(i18), L2CValue(i19), L2CValue(i20),
			L2CValue(h2), L2CValue(i21), L2CValue(i22), L2CValue(i23)
		});
	}

	void ATTACK(
		u64 i1,  // ID
		u64 i2,  // Part
		u64 h1,  // Bone
		float f1,  // Damage
		u64 i3,  // Angle
		u64 i4,  // KBG
		u64 i5,  // FKB
		u64 i6,  // BKB
		float f2,  // Size
		float f3,  // X
		float f4,  // Y
		float f5,  // Z
		float fX2, // X2
		float fY2, // Y2
		float fZ2, // Z2
		float f6,  // Hitlag
		float f7,  // SDI
		u64 i7,  // Clang/Rebound
		u64 i8,  // Facing Restriction
		u64 i9,  // Fixed Weight
		u64 i10, // Shield Damage
		float f8,  // Trip Chance
		u64 i11, // Rehite Rate
		u64 i12, // Reflectable
		u64 i13, // Absorbable
		u64 i14, // Flinchless
		u64 i15, // Disable Hitlag
		u64 i16, // Direct
		u64 i17, // Ground/Air
		u64 i18, // Hit Bits
		u64 i19, // Collision Bits
		u64 i20, // Friendly Fire
		u64 h2,  // Effect
		u64 i21, // SFX Level
		u64 i22, // SFX Type
		u64 i23) {  // Move Type
		wrap(app::sv_animcmd::ATTACK, {
			L2CValue(i1), L2CValue(i2), L2CValue(h1), L2CValue(f1),
			L2CValue(i3), L2CValue(i4), L2CValue(i5), L2CValue(i6),
			L2CValue(f2), L2CValue(f3), L2CValue(f4), L2CValue(f5),
			L2CValue(fX2), L2CValue(fY2), L2CValue(fZ2), L2CValue(f6),
			L2CValue(f7), L2CValue(i7), L2CValue(i8), L2CValue(i9),
			L2CValue(i10), L2CValue(f8), L2CValue(i11), L2CValue(i12),
			L2CValue(i13), L2CValue(i14), L2CValue(i15), L2CValue(i16),
 			L2CValue(i17), L2CValue(i18), L2CValue(i19), L2CValue(i20),
			L2CValue(h2), L2CValue(i21), L2CValue(i22), L2CValue(i23)
		});
	}
};

#endif // ACMD_WRAPPER_H