#pragma once

#include <lauxlib.h>
#include <lua.h>
#include <stdbool.h>

#include "nvim/api/private/helpers.h"
#include "nvim/cmdexpand_defs.h"  // IWYU pragma: keep
#include "nvim/ex_cmds_defs.h"  // IWYU pragma: keep
#include "nvim/func_attr.h"
#include "nvim/macros_defs.h"
#include "nvim/types_defs.h"
#include "nvim/usercmd.h"  // IWYU pragma: keep

// Generated by generators/gen_api_dispatch.lua
void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL;

typedef struct {
  LuaRef nil_ref;
  LuaRef empty_dict_ref;
  int ref_count;
#if __has_feature(address_sanitizer)
  PMap(int) ref_markers;
#endif
} nlua_ref_state_t;

#define NLUA_EXEC_STATIC(cstr, arg, mode, arena, err) \
  nlua_exec(STATIC_CSTR_AS_STRING(cstr), arg, mode, arena, err)

#define NLUA_CLEAR_REF(x) \
  do { \
    /* Take the address to avoid double evaluation. #1375 */ \
    if ((x) != LUA_NOREF) { \
      api_free_luaref(x); \
      (x) = LUA_NOREF; \
    } \
  } while (0)

typedef enum {
  kRetObject,  ///< any object, but doesn't preserve nested luarefs
  kRetNilBool,  ///< NIL preserved as such, other values return their booleanness
                ///< Should also be used when return value is ignored, as it is allocation-free
  kRetLuaref,  ///< return value becomes a single Luaref, regardless of type (except NIL)
} LuaRetMode;

/// To use with kRetNilBool for quick truthiness check
#define LUARET_TRUTHY(res) ((res).type == kObjectTypeBoolean && (res).data.boolean == true)

#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/executor.h.generated.h"
#endif

EXTERN nlua_ref_state_t *nlua_global_refs INIT( = NULL);
EXTERN bool nlua_disable_preload INIT( = false);
