#include "propclass.h"
#include "model.h"
#include "vecmath.h"
#include "prop/prop.h"

namespace scripting::api {

//**********HANDLE: Propclass
ADE_OBJ(l_Propclass, int, "propclass", "Prop class handle");

ADE_FUNC(__tostring,
	l_Propclass,
	nullptr,
	"Prop class name",
	"string",
	"Prop class name, or an empty string if handle is invalid")
{
	int idx;
	const char* s = nullptr;
	if (!ade_get_args(L, "o|s", l_Propclass.Get(&idx), &s))
		return ade_set_error(L, "s", "");

	if (idx < 0 || idx >= prop_info_size())
		return ade_set_error(L, "s", "");

	return ade_set_args(L, "s", Prop_info[idx].name);
}

ADE_FUNC(__eq,
	l_Propclass,
	"propclass, propclass",
	"Checks if the two classes are equal",
	"boolean",
	"true if equal, false otherwise")
{
	int idx1, idx2;
	if (!ade_get_args(L, "oo", l_Propclass.Get(&idx1), l_Propclass.Get(&idx2)))
		return ade_set_error(L, "b", false);

	if (idx1 < 0 || idx1 >= prop_info_size())
		return ade_set_error(L, "b", false);

	if (idx2 < 0 || idx2 >= prop_info_size())
		return ade_set_error(L, "b", false);

	return ade_set_args(L, "b", idx1 == idx2);
}

ADE_VIRTVAR(Name,
	l_Propclass,
	"string",
	"Prop class name",
	"string",
	"Prop class name, or an empty string if handle is invalid")
{
	int idx;
	const char* s = nullptr;
	if (!ade_get_args(L, "o|s", l_Propclass.Get(&idx), &s))
		return ade_set_error(L, "s", "");

	if (idx < 0 || idx >= prop_info_size())
		return ade_set_error(L, "s", "");

	if (ADE_SETTING_VAR) {
		LuaError(L, "Setting prop class name is not supported");
	}

	return ade_set_args(L, "s", Prop_info[idx].name);
}

ADE_VIRTVAR(Model,
	l_Propclass,
	"model",
	"Model",
	"model",
	"Prop class model, or invalid model handle if propclass handle is invalid")
{
	int idx = -1;
	model_h* mdl = nullptr;
	if (!ade_get_args(L, "o|o", l_Propclass.Get(&idx), l_Model.GetPtr(&mdl)))
		return ade_set_error(L, "o", l_Model.Set(model_h(-1)));

	if (idx < 0 || idx >= prop_info_size())
		return ade_set_error(L, "o", l_Model.Set(model_h(-1)));

	prop_info* pip = &Prop_info[idx];

	if (ADE_SETTING_VAR) {
		LuaError(L, "Setting prop class model is not supported");
	}

	return ade_set_args(L, "o", l_Model.Set(model_h(pip->model_num)));
}

ADE_VIRTVAR(CustomData,
	l_Propclass,
	nullptr,
	"Gets the custom data table for this prop class",
	"table",
	"The prop class's custom data table")
{
	int idx;
	if (!ade_get_args(L, "o", l_Propclass.Get(&idx)))
		return ADE_RETURN_NIL;

	if (idx < 0 || idx >= prop_info_size())
		return ADE_RETURN_NIL;

	auto table = luacpp::LuaTable::create(L);

	prop_info* pip = &Prop_info[idx];

	for (const auto& pair : pip->custom_data) {
		table.addValue(pair.first, pair.second);
	}

	return ade_set_args(L, "t", &table);
}

ADE_FUNC(hasCustomData,
	l_Propclass,
	nullptr,
	"Detects whether the prop class has any custom data",
	"boolean",
	"true if the propclass's custom_data is not empty, false otherwise")
{
	int idx;
	if (!ade_get_args(L, "o", l_Propclass.Get(&idx)))
		return ADE_RETURN_NIL;

	if (idx < 0 || idx >= prop_info_size())
		return ADE_RETURN_NIL;

	prop_info* pip = &Prop_info[idx];

	bool result = !pip->custom_data.empty();
	return ade_set_args(L, "b", result);
}

ADE_VIRTVAR(CustomStrings,
	l_Propclass,
	nullptr,
	"Gets the indexed custom string table for this prop. Each item in the table is a table with the following values: "
	"Name - the name of the custom string, Value - the value associated with the custom string, String - the custom "
	"string itself.",
	"table",
	"The prop's custom data table")
{
	int idx;
	if (!ade_get_args(L, "o", l_Propclass.Get(&idx)))
		return ADE_RETURN_NIL;

	if (idx < 0 || idx >= prop_info_size())
		return ADE_RETURN_NIL;

	prop_info* pip = &Prop_info[idx];

	if (ADE_SETTING_VAR) {
		LuaError(L, "Setting Custom Data is not supported");
	}

	auto table = luacpp::LuaTable::create(L);

	int cnt = 0;

	for (const auto& cs : pip->custom_strings) {
		cnt++;
		auto item = luacpp::LuaTable::create(L);

		item.addValue("Name", luacpp::LuaValue::createValue(Script_system.GetLuaSession(), cs.name));
		item.addValue("Value", luacpp::LuaValue::createValue(Script_system.GetLuaSession(), cs.value));
		item.addValue("String", luacpp::LuaValue::createValue(Script_system.GetLuaSession(), cs.text));

		table.addValue(cnt, item);
	}

	return ade_set_args(L, "t", &table);
}

ADE_FUNC(hasCustomStrings,
	l_Propclass,
	nullptr,
	"Detects whether the prop has any custom strings",
	"boolean",
	"true if the prop's custom_strings is not empty, false otherwise")
{
	int idx;
	if (!ade_get_args(L, "o", l_Propclass.Get(&idx)))
		return ADE_RETURN_NIL;

	if (idx < 0 || idx >= prop_info_size())
		return ADE_RETURN_NIL;

	prop_info* pip = &Prop_info[idx];

	bool result = !pip->custom_strings.empty();
	return ade_set_args(L, "b", result);
}

ADE_FUNC(isValid,
	l_Propclass,
	nullptr,
	"Detects whether handle is valid",
	"boolean",
	"true if valid, false if handle is invalid, nil if a syntax/type error occurs")
{
	int idx;
	if (!ade_get_args(L, "o", l_Propclass.Get(&idx)))
		return ADE_RETURN_NIL;

	if (idx < 0 || idx >= prop_info_size())
		return ADE_RETURN_FALSE;

	return ADE_RETURN_TRUE;
}

ADE_FUNC(renderTechModel,
	l_Propclass,
	"number X1, number Y1, number X2, number Y2, [number RotationPercent =0, number PitchPercent =0, number "
	"BankPercent=40, number Zoom=1.3, boolean Lighting=true]",
	"Draws prop model as if in techroom. True for regular lighting, false for flat lighting.",
	"boolean",
	"Whether prop was rendered")
{
	int x1, y1, x2, y2;
	angles rot_angles = {0.0f, 0.0f, 40.0f};
	int idx;
	float zoom = 1.3f;
	bool lighting = true;
	if (!ade_get_args(L,
			"oiiii|ffffb",
			l_Propclass.Get(&idx),
			&x1,
			&y1,
			&x2,
			&y2,
			&rot_angles.h,
			&rot_angles.p,
			&rot_angles.b,
			&zoom,
			&lighting))
		return ade_set_error(L, "b", false);

	if (idx < 0 || idx >= prop_info_size())
		return ade_set_args(L, "b", false);

	if (x2 < x1 || y2 < y1)
		return ade_set_args(L, "b", false);

	CLAMP(rot_angles.p, 0.0f, 100.0f);
	CLAMP(rot_angles.b, 0.0f, 100.0f);
	CLAMP(rot_angles.h, 0.0f, 100.0f);

	// Handle angles
	matrix orient = vmd_identity_matrix;
	angles view_angles = {-0.6f, 0.0f, 0.0f};
	vm_angles_2_matrix(&orient, &view_angles);

	rot_angles.p = (rot_angles.p * 0.01f) * PI2;
	rot_angles.b = (rot_angles.b * 0.01f) * PI2;
	rot_angles.h = (rot_angles.h * 0.01f) * PI2;
	vm_rotate_matrix_by_angles(&orient, &rot_angles);

	return ade_set_args(L, "b", render_tech_model(TECH_PROP, x1, y1, x2, y2, zoom, lighting, idx, &orient));
}

// Nuke's alternate tech model rendering function
ADE_FUNC(renderTechModel2,
	l_Propclass,
	"number X1, number Y1, number X2, number Y2, [orientation Orientation=nil, number Zoom=1.3]",
	"Draws prop model as if in techroom",
	"boolean",
	"Whether prop was rendered")
{
	int x1, y1, x2, y2;
	int idx;
	float zoom = 1.3f;
	matrix_h* mh = nullptr;
	if (!ade_get_args(L, "oiiiio|f", l_Propclass.Get(&idx), &x1, &y1, &x2, &y2, l_Matrix.GetPtr(&mh), &zoom))
		return ade_set_error(L, "b", false);

	if (idx < 0 || idx >= prop_info_size())
		return ade_set_args(L, "b", false);

	if (x2 < x1 || y2 < y1)
		return ade_set_args(L, "b", false);

	// Handle angles
	matrix* orient = mh->GetMatrix();

	return ade_set_args(L, "b", render_tech_model(TECH_PROP, x1, y1, x2, y2, zoom, true, idx, orient));
}

ADE_FUNC(isModelLoaded,
	l_Propclass,
	"[boolean Load = false]",
	"Checks if the model used for this propclass is loaded or not and optionally loads the model, which might be a "
	"slow operation.",
	"boolean",
	"If the model is loaded or not")
{
	int idx;
	bool load_check = false;
	if (!ade_get_args(L, "o|b", l_Propclass.Get(&idx), &load_check))
		return ADE_RETURN_FALSE;

	prop_info* pip = &Prop_info[idx];

	if (pip == nullptr)
		return ADE_RETURN_FALSE;

	if (load_check) {
		pip->model_num = model_load(pip->pof_file.c_str());
	}

	if (pip->model_num > -1)
		return ADE_RETURN_TRUE;
	else
		return ADE_RETURN_FALSE;
}

ADE_FUNC(getPropClassIndex,
	l_Propclass,
	nullptr,
	"Gets the index value of the prop class",
	"number",
	"index value of the prop class")
{
	int idx;
	if (!ade_get_args(L, "o", l_Propclass.Get(&idx)))
		return ade_set_args(L, "i", -1);

	if (idx < 0 || idx >= prop_info_size())
		return ade_set_args(L, "i", -1);

	return ade_set_args(L, "i", idx + 1); // Lua is 1-based
}

} // namespace scripting::api
