#ifndef L2C_IMPORTS_H
#define L2C_IMPORTS_H
#include <switch.h>

#include "l2c.hpp"
#include "lua_bind_hash.hpp"

u64 is_training_mode(void) asm("_ZN3app9smashball16is_training_modeEv") LINKABLE;

namespace lib
{
    enum L2CVarType
      {
       L2C_void = 0,
       L2C_bool = 1,
       L2C_integer = 2,
       L2C_number = 3,
       L2C_pointer = 4,
       L2C_table = 5,
       L2C_inner_function = 6,
       L2C_hash = 7,
       L2C_string = 8,
      };

    struct L2CTable_meta
    {
      uint64_t a;
      uint64_t b;
      uint64_t c;
      uint64_t d;
    };
  
    struct L2CTable
    {
      uint32_t refcnt;
      uint32_t unk;
      
      uint64_t begin; // L2CValue*
      uint64_t end; // L2CValue*
      uint64_t also_end; // L2CValue*
      struct L2CTable_meta meta;
      uint64_t unk_ptr;
    };

    struct L2CInnerFunctionBase
    {
      uint64_t unk;
      uint32_t refcnt;
    } L2CInnerFunctionBase;

    struct L2CValue
    {
      uint32_t type;
      uint32_t unk;
      union
      {
        uint64_t raw;
        float raw_float;
        void* raw_pointer;
        struct L2CTable* raw_table;
        struct L2CInnerFunctionBase* raw_innerfunc;
        //std::string* raw_string;
      };
    };

    struct L2CAgent
    {
      uint64_t vtable;
      uint64_t lua_state_agent;
      uint64_t unk10;
      uint64_t unk18;
      uint64_t unk20;
      uint64_t unk28;
      uint64_t unk30;
      uint64_t unk38;
      uint64_t lua_state_agentbase;

      L2CAgent* L2CAgent_constr(u64 lua_state) asm("_ZN3lib8L2CAgentC2EP9lua_State") LINKABLE;
      u64 push_lua_stack(L2CValue* l2c_value) asm("_ZN3lib8L2CAgent14push_lua_stackERKNS_8L2CValueE") LINKABLE;
      // pop_lua_stack
      // Notes: 
      // Actually takes three arguments, but the third is given through X8 due to how 
      // AArch64 treats struct pointers that are used as pass by reference to get the value.
      // Thus, my current solution is to use inline ASM before using this to pass the 
      // last arg. This is done using asm("mov x8, %x0" : : "r"(&popped) : "x8" );, where
      // popped is an L2CValue that will be populated by the function.
      // FURTHERMORE, this function does NOT actually pop the stack, it only returns the value at the
      // position indicated by the second argument.
      // This index is either positive, meaning absolute position in the stack, or negative,
      // which is more traditional, i.e. -1 is the top of the stack.
      //__int64_t (*lib_L2CAgent_pop_lua_stack)(__int64_t, int);
      u64 pop_lua_stack(int index) asm("_ZN3lib8L2CAgent13pop_lua_stackEi") LINKABLE;

      void get_lua_stack(int index, lib::L2CValue* l2c_val) {
        asm("mov x8, %x0" : : "r"(l2c_val) : "x8" );
        pop_lua_stack(index);
      }

      u64 sv_set_function_hash(void* func, u64 hash) asm("_ZN3lib8L2CAgent20sv_set_function_hashEPvN3phx6Hash40E") LINKABLE;
      u64 clear_lua_stack() asm("_ZN3lib8L2CAgent15clear_lua_stackEv") LINKABLE;
    };

    bool lua_bind_get_value(u64, int*) asm("_ZN3lib18lua_bind_get_valueIiEEbmRT_") LINKABLE;

    int lua_const(const char* str) {
        int val;
        if (lua_bind_get_value(lua_bind_hash_str(str), &val))
            return val;
        else
            return -1;
    }
}

#endif // L2C_IMPORTS_H