// mover lib for lua

#include "g_lua.h"

#ifdef G_LUA
// mover.Halt(entity ent)
// Stops translational movement on ent immediately.
static int Mover_Halt(lua_State *L) {
	lent_t		*lent;
	gentity_t	*ent = NULL;
	int			id = 0;
	
	if(lua_isnumber(L, 1)) {
		id =  luaL_checkint(L, 1);
		if(id < 0 || id > MAX_GENTITIES - 1) return 1;
		ent = &g_entities[id];
		if(!ent) return 1;
	} else {
		lent = Lua_GetEntity(L, 1);
		if(!lent || !lent->e) return 1;
		ent = lent->e;
	}

	LUA_DEBUG("Mover_Halt - start: end=%d", ent->s.number);
	BG_EvaluateTrajectory(&ent->s.pos, level.time, ent->r.currentOrigin);
	VectorCopy(ent->r.currentOrigin, ent->s.pos.trBase);
	ent->s.pos.trType = TR_STATIONARY;
	ent->s.pos.trTime = level.time;
	ent->nextthink = 0;
	ent->think = NULL;
	ent->nextTrain = NULL;
	trap_LinkEntity(ent);
	LUA_DEBUG("Mover_Halt - return: halted ent");
	return 0;
}

// mover.HaltAngles(entity ent)
// Stops rotational movement on ent immediately.
static int Mover_HaltAngles(lua_State * L)
{
	lent_t			*lent;
	gentity_t       *ent = NULL;
	int				id = 0;

	if(lua_isnumber(L, 1)) {
		id = luaL_checkint(L, 1);
		if(id < 0 || id > MAX_GENTITIES - 1) return 1;
		ent = &g_entities[id];
		if(!ent) return 1;
	} else {
		lent = Lua_GetEntity(L, 1);
		if(!lent || !lent->e) return 1;
		ent = lent->e;
	}

	LUA_DEBUG("Mover_HaltAngles - start: ent=%d", ent->s.number);
	if(ent)
	{
		BG_EvaluateTrajectory(&ent->s.apos, level.time, ent->s.apos.trBase);
		ent->s.apos.trType = TR_STATIONARY;
		ent->s.apos.trTime = level.time;
		trap_LinkEntity(ent);
		LUA_DEBUG("Mover_HaltAngles - return: halted ent");
	}
	return 0;
}

extern void Reached_Train(gentity_t *ent);
extern void Think_SetupTrainTargets(gentity_t *ent);
extern void SetMoverState(gentity_t *ent, moverState_t moverState, int time);

// mover.AsTrain(entity mover, entity target, float speed)
// Moves an entity like a func_train entity. Targets have to be path_corner entities.
// * ent the entity to move
// * target the first path_corner to move to
// * speed speed to move to first path_corner with
static int Mover_AsTrain(lua_State * L)
{
	lent_t			*lent, *tlent;
	gentity_t      *ent = NULL;
	gentity_t      *targ = NULL;
	vec3_t          move;
	float           length;
	int				id = 0, tid = 0;

	float           speed = (float)luaL_checknumber(L, 3);

	if(lua_isnumber(L, 1)) {
		id = luaL_checkint(L, 1);
		if(id < 0 || id > MAX_GENTITIES - 1) return 1;
		ent = &g_entities[id];
		if(!ent) return 1;
	} else {
		lent = Lua_GetEntity(L, 1);
		if(!lent || !lent->e) return 1;
		ent = lent->e;
	}
	if(luaL_checkint(L, 2)) {
		tid = luaL_checkint(L, 2);
		if(tid < 0 || tid > MAX_GENTITIES - 1) return 1;
		targ = &g_entities[tid];
		if(!targ) return 1;
	} else {
		tlent = Lua_GetEntity(L, 2);
		if(!tlent || tlent->e) return 1;
		targ = tlent->e;
	}

	LUA_DEBUG("Mover_AsTrain - start: ent=%d target=%d speed=%f", ent->s.number, targ->s.number, speed);

	if(!ent || !targ)
	{
		LUA_DEBUG("Mover_AsTrain - return: ent or/and target missing");
		return 0;
	}
	if(speed < 1)
	{
		LUA_DEBUG("Mover_AsTrain - moving: speed less than 1 fixed");
		speed = 1;
	}

	if(ent->nextTrain)
	{
		LUA_DEBUG("Mover_AsTrain - pathing: NextTrain=%d ", ent->nextTrain->s.number);
	}

	ent->speed = speed;
	ent->nextTrain = targ;
	ent->reached = Reached_Train;
	ent->target = G_NewString(targ->targetname);

	Think_SetupTrainTargets(ent);

	BG_EvaluateTrajectory(&ent->s.pos, level.time, ent->r.currentOrigin);
	VectorCopy(ent->r.currentOrigin, ent->s.origin);

	VectorCopy(ent->s.origin, ent->pos1);
	VectorCopy(ent->nextTrain->s.origin, ent->pos2);

	VectorSubtract(ent->pos2, ent->pos1, move);
	length = VectorLength(move);

	if(length <= 0.05)
	{
		G_SetOrigin(ent, ent->pos2);
		LUA_DEBUG("Mover_AsTrain - return: snapped to target, length too small length=%f", length);
		return 0;
	}

	ent->s.pos.trDuration = length * 1000 / speed;

	ent->s.loopSound = ent->nextTrain->soundLoop;

	SetMoverState(ent, MOVER_1TO2, level.time);

	LUA_DEBUG("Mover_AsTrain - return: moving to target, length=%f duration=%d", length, ent->s.pos.trDuration);
	return 0;
}

// mover.SetAngles(entity ent, vector angles) or 
// mover.SetAngles(entity ent, float y, float z, float x)
// Sets the angles of ent to the specified value(s). 
// Values are sorted Pitch (around Y-Axis), Yaw (around Z-Axis) and 
// Roll (around X-Axis). These can also be stowed in a vector angles.
static int Mover_SetAngles(lua_State * L)
{
	vec3_t          newAngles;
	lent_t			*lent;
	gentity_t      *ent = NULL;
	vec_t          *target;
	int				id = 0;

	if(lua_isnumber(L, 1)) {
		id = luaL_checkint(L, 1);
		if(id < 0 || id > MAX_GENTITIES - 1) return 1;
		ent = &g_entities[id];
		if(!ent) return 1;
	} else {
		lent = Lua_GetEntity(L, 1);
		if(!lent || !lent->e) return 1;
		ent = lent->e;
	}

	if(Lua_IsVector(L, 2))
	{
		target = Lua_GetVector(L, 2);
		VectorCopy(target, newAngles);
	}
	else
	{
		newAngles[0] = luaL_checkint(L, 2);
		newAngles[1] = luaL_checkint(L, 3);
		newAngles[2] = luaL_checkint(L, 4);
	}
	LUA_DEBUG("Mover_SetAngles - start: ent=%d angles=%s", ent->s.number, vtos(newAngles));
	if(ent)
	{
		VectorCopy(newAngles, ent->s.apos.trBase);
		VectorCopy(newAngles, ent->s.angles);
		trap_LinkEntity(ent);
		LUA_DEBUG("Mover_SetAngles - return: moved");
	}
	return 0;
}

// mover.SetPosition(entity ent, vector pos) or 
// mover.SetPosition(entity ent, float x, float y, float z)
// Set the position of ent to the specified value(s). Can also be stowed in a vector pos.
static int Mover_SetPosition(lua_State * L)
{
	vec3_t          newOrigin;
	lent_t			*lent;
	gentity_t      *ent = NULL;
	vec_t          *target;
	int				id = 0;

	if(lua_isnumber(L, 1)) {
		id = luaL_checkint(L, 1);
		if(id < 0 || id > MAX_GENTITIES - 1) return 1;
		ent = &g_entities[id];
		if(!ent) return 1;
	} else {
		lent = Lua_GetEntity(L, 1);
		if(!lent || !lent->e) return 1;
		ent = lent->e;
	}

	if(Lua_IsVector(L, 2))
	{
		target = Lua_GetVector(L, 2);
		VectorCopy(target, newOrigin);
	}
	else
	{
		newOrigin[0] = luaL_checkint(L, 2);
		newOrigin[1] = luaL_checkint(L, 3);
		newOrigin[2] = luaL_checkint(L, 4);
	}
	LUA_DEBUG("Mover_SetPosition - start: ent=%d pos=%s", ent->s.number, vtos(newOrigin));
	if(ent)
	{
		G_SetOrigin(ent, newOrigin);
		trap_LinkEntity(ent);
		LUA_DEBUG("Mover_SetPosition - return: moved");
	}
	return 0;
}

static void SetTrajectoryLinear(trajectory_t * tr, const float speed, const vec3_t endPosition)
{
	vec3_t          delta;
	float           length;

	VectorSubtract(endPosition, tr->trBase, delta);
	length = VectorLength(delta);
	VectorNormalize(delta);
	tr->trDuration = length * 1000 / speed;
	tr->trTime = level.time;
	VectorScale(delta, speed, tr->trDelta);
	tr->trType = TR_LINEAR_STOP;
}

// mover.ToAngles(entity ent, float speed, vector angles) or 
// mover.ToAngles(entity ent, float speed, float y, float z, float x)
// Rotates ent with speed speed (in degrees per second) to the specified value(s). 
// Values are sorted Pitch (around Y-Axis), Yaw (around Z-Axis) and 
// Roll (around X-Axis). These can also be stowed in a vector angles.
static int Mover_ToAngles(lua_State * L)
{
	vec3_t          newAngles;
	lent_t			*lent;
	gentity_t      *ent = NULL;
	float           speed;
	vec_t          *target;
	int				id = 0;

	if(lua_isnumber(L, 1)) {
		id = luaL_checkint(L, 1);
		if(id < 0 || id > MAX_GENTITIES - 1) return 1;
		ent = &g_entities[id];
		if(!ent) return 1;
	} else {
		lent = Lua_GetEntity(L, 1);
		if(!lent || !lent->e) return 1;
		ent = lent->e;
	}

	speed = (float)luaL_checknumber(L, 2);

	if(Lua_IsVector(L, 3))
	{
		target = Lua_GetVector(L, 3);
		VectorCopy(target, newAngles);
	}
	else
	{
		newAngles[0] = luaL_checkint(L, 3);
		newAngles[1] = luaL_checkint(L, 4);
		newAngles[2] = luaL_checkint(L, 5);
	}

	LUA_DEBUG("Mover_ToAngles - start: ent=%d angles=%s speed=%f", ent->s.number, vtos(newAngles), speed);
	if(ent)
	{
		BG_EvaluateTrajectory(&ent->s.apos, level.time, ent->s.apos.trBase);
		SetTrajectoryLinear(&ent->s.apos, speed, newAngles);
		ent->moverState = MOVER_LUA;
		trap_LinkEntity(ent);
		LUA_DEBUG("Mover_ToAngles - return: moving");
	}
	return 0;
}

// mover.ToPosition(entity ent, vector pos) or 
// mover.ToPosition(entity ent, float x, float y, float z)
// Moves ent with speed speed to the specified value(s). Can also be stowed in a vector pos.
static int Mover_ToPosition(lua_State * L)
{
	vec3_t          newOrigin;
	lent_t			*lent;
	gentity_t      *ent = NULL;
	float           speed;
	vec_t          *target;
	int				id = 0;

	if(lua_isnumber(L, 1)) {
		id = luaL_checkint(L, 1);
		if(id < 0 ||id > MAX_GENTITIES - 1) return 1;
		ent = &g_entities[id];
		if(!ent) return 1;
	} else {
		lent = Lua_GetEntity(L,1);
		if(!lent || !lent->e) return 1;
		ent = lent->e;
	}

	speed = (float)luaL_checknumber(L, 2);

	if(Lua_IsVector(L, 3))
	{
		target = Lua_GetVector(L, 3);
		VectorCopy(target, newOrigin);
	}
	else
	{
		newOrigin[0] = luaL_checkint(L, 3);
		newOrigin[1] = luaL_checkint(L, 4);
		newOrigin[2] = luaL_checkint(L, 5);
	}
	

	LUA_DEBUG("Mover_ToPosition - start: ent=%d pos=%s speed=%f", ent->s.number, vtos(newOrigin), speed);
	if(ent)
	{
		BG_EvaluateTrajectory(&ent->s.pos, level.time, ent->s.pos.trBase);
		SetTrajectoryLinear(&ent->s.pos, speed, newOrigin);
		ent->moverState = MOVER_LUA;
		trap_LinkEntity(ent);
		LUA_DEBUG("Mover_ToPosition - return: moving");
	}
	return 0;
}

static const luaL_Reg lib_mover[] = {
	{"Halt", Mover_Halt},
	{"HaltAngles", Mover_HaltAngles},
	{"AsTrain", Mover_AsTrain},
	{"SetPosition", Mover_SetPosition},
	{"SetOrigin", Mover_SetPosition},
	{"ToPosition", Mover_ToPosition},
	{"SetAngles", Mover_SetAngles},
	{"ToAngles", Mover_ToAngles},
	{NULL, NULL}
};

int Luaopen_Mover(lua_State *L) {
	luaL_register(L, "mover", lib_mover);
	return 1;
}
#endif
