#pragma once

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

#include "nvim/api/private/defs.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/garray_defs.h"  // IWYU pragma: keep
#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), NULL, 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)
  kRetMulti,    ///< like kRetObject but return multiple return values as an Array
} LuaRetMode;

/// Maximum number of errors in vim.ui_attach() and decor provider callbacks.
enum { CB_MAX_ERROR = 3, };

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

#include "lua/executor.h.generated.h"

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

/// Tracks the active Lua thread
extern lua_State *active_lstate;

#define ENTER_LUA_ACTIVE_STATE(new_state) \
  lua_State *const save_active_lstate = active_lstate; \
  active_lstate = (new_state);

#define LEAVE_LUA_ACTIVE_STATE() \
  active_lstate = save_active_lstate;
