// BLANKART
//-----------------------------------------------------------------------------
// Copyright (C) 2026 by "yama".
// Copyright (C) 2026 by BlanKart Team.
// Copyright (C) 2014-2025 by Sonic Team Junior.
// Copyright (C) 2016 by John "JTE" Muniz.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file  lua_voicelib.c
/// \brief voice structure library for Lua scripting

#include "doomdef.h"
#include "r_skins.h"
#include "sounds.h"
#include "deh_tables.h"

#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
#include "lua_hook.h" // hook_cmd_running errors

enum voicevars
{
	voicevars_id = 0,
	voicevars_name,
	voicevars_win,
	voicevars_lose,
	voicevars_pain,
	voicevars_attack,
	voicevars_boost,
	voicevars_overtake,
	voicevars_hitem,
	voicevars_power
};

static const char *const voicevars_opt[] = {
	"id",
	"name",
	"win",
	"lose",
	"pain",
	"attack",
	"boost",
	"overtake",
	"hitem",
	"power",
	NULL
};

#define RNOFIELD luaL_error(L, LUA_QL("kartvoice_t") " has no field named " LUA_QS, field)
#define RNOGET luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " cannot be get.", field)

static int voice_get(lua_State* L)
{
	kartvoice_t* voice = *((kartvoice_t**)luaL_checkudata(L, 1, META_VOICE));
	enum voicevars field = luaL_checkoption(L, 2, NULL, voicevars_opt);

	// voices are always valid, only added, never removed
	I_Assert(voice != NULL);

	switch (field)
	{
	case voicevars_id:
		return RNOGET;
	case voicevars_name:
		lua_pushstring(L, voice->name);
		break;
	case voicevars_win:
		LUA_PushUserdata(L, &voice->win, META_VOICE_ARRAY1);
		break;
	case voicevars_lose:
		LUA_PushUserdata(L, &voice->lose, META_VOICE_ARRAY1);
		break;
	case voicevars_pain:
		LUA_PushUserdata(L, voice->pain, META_VOICE_ARRAY);
		break;
	case voicevars_attack:
		LUA_PushUserdata(L, voice->attack, META_VOICE_ARRAY);
		break;
	case voicevars_boost:
		LUA_PushUserdata(L, voice->boost, META_VOICE_ARRAY);
		break;
	case voicevars_overtake:
		LUA_PushUserdata(L, &voice->overtake, META_VOICE_ARRAY1);
		break;
	case voicevars_hitem:
		LUA_PushUserdata(L, &voice->hitem, META_VOICE_ARRAY1);
		break;
	case voicevars_power:
		LUA_PushUserdata(L, &voice->power, META_VOICE_ARRAY1);
		break;
	default:
		return RNOFIELD;
	}

	return 1;
}

static int voice_set(lua_State *L)
{
	return luaL_error(L, LUA_QL("kartvoice_t") " struct cannot be edited by Lua.");
}

static int voice_num(lua_State* L)
{
	kartvoice_t* voice = *((kartvoice_t**)luaL_checkudata(L, 1, META_VOICE));

	// voices are always valid, only added, never removed
	I_Assert(voice != NULL);

	lua_pushinteger(L, voice->id);
	return 1;
}

// voice_array, i -> voice.array[i]
static int voice_array_get(lua_State *L)
{
	sfxenum_t *voice_array = *((sfxenum_t **)luaL_checkudata(L, 1, META_VOICE_ARRAY));
	INT32 i = luaL_checkinteger(L, 2);

	if (i < 0 || i >= VOICEARRAYSIZE)
		return luaL_error(L, "index %d out of range (0 - %d)", i, VOICEARRAYSIZE-1);

	lua_pushinteger(L, voice_array[i]);
	return 1;
}

static int voice_array_set(lua_State *L)
{
	sfxenum_t *voice_array = *((sfxenum_t **)luaL_checkudata(L, 1, META_VOICE_ARRAY));
	INT32 i = luaL_checkinteger(L, 2);

	if (hud_running)
		return luaL_error(L, "Do not alter kartvoice_t.array[] in HUD rendering code!");
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter kartvoice_t.array[] in CMD building code!");

	if (i < 0 || i >= VOICEARRAYSIZE)
		return luaL_error(L, "index %d out of range (0 - %d)", i, VOICEARRAYSIZE-1);

	voice_array[i] = luaL_checkinteger(L, 3);
	return 0;
}

// #voice.array -> VOICEARRAYSIZE
static int voice_array_num(lua_State *L)
{
	lua_pushinteger(L, VOICEARRAYSIZE);
	return 1;
}

// i would've liked to shove the length of the voice array into the userdata...
// but this codebase is not ready for that, sooooo... have yet ANOTHER userdata type!

// voice_array, i -> voice.array[i]
static int voice_array1_get(lua_State *L)
{
	sfxenum_t *voice_array = *((sfxenum_t **)luaL_checkudata(L, 1, META_VOICE_ARRAY1));
	INT32 i = luaL_checkinteger(L, 2);

	if (i < 0 || i >= 1)
		return luaL_error(L, "index %d out of range (0 - %d)", i, 1-1);

	lua_pushinteger(L, voice_array[i]);
	return 1;
}

static int voice_array1_set(lua_State *L)
{
	sfxenum_t *voice_array = *((sfxenum_t **)luaL_checkudata(L, 1, META_VOICE_ARRAY1));
	INT32 i = luaL_checkinteger(L, 2);

	if (hud_running)
		return luaL_error(L, "Do not alter kartvoice_t.array[1] in HUD rendering code!");
	if (hook_cmd_running)
		return luaL_error(L, "Do not alter kartvoice_t.array[1] in CMD building code!");

	if (i < 0 || i >= 1)
		return luaL_error(L, "index %d out of range (0 - %d)", i, 1-1);

	voice_array[i] = luaL_checkinteger(L, 3);
	return 0;
}

// #voice.array -> 1
static int voice_array1_num(lua_State *L)
{
	lua_pushinteger(L, 1);
	return 1;
}

int LUA_VoiceLib(lua_State* L)
{
	luaL_newmetatable(L, META_VOICE);
	lua_pushcfunction(L, voice_get);
	lua_setfield(L, -2, "__index");

	lua_pushcfunction(L, voice_set);
	lua_setfield(L, -2, "__newindex");

	lua_pushcfunction(L, voice_num);
	lua_setfield(L, -2, "__len");
	lua_pop(L, 1);

	luaL_newmetatable(L, META_VOICE_ARRAY);
	lua_pushcfunction(L, voice_array_get);
	lua_setfield(L, -2, "__index");

	lua_pushcfunction(L, voice_array_set);
	lua_setfield(L, -2, "__newindex");

	lua_pushcfunction(L, voice_array_num);
	lua_setfield(L, -2, "__len");
	lua_pop(L,1);

	luaL_newmetatable(L, META_VOICE_ARRAY1);
	lua_pushcfunction(L, voice_array1_get);
	lua_setfield(L, -2, "__index");

	lua_pushcfunction(L, voice_array1_set);
	lua_setfield(L, -2, "__newindex");

	lua_pushcfunction(L, voice_array1_num);
	lua_setfield(L, -2, "__len");
	lua_pop(L,1);

	return 0;
}
