#include "inputmanager.h"
#include "game_object.h"
#include <SDL3/SDL.h>

// Lua wrappers
void Entity_LoadModel(LuaPlus::LuaState* state)
{
	LuaPlus::LuaStack stack(state);

	Entity* entity = (Entity*)stack[1].GetByName("__object").GetLightUserdata();
	entity->LoadModel(stack[2].GetString());
}

REGISTER_ENTITY(Entity);

Entity::Entity() : 
	m_model(nullptr)
{
}

Entity::~Entity()
{
	if (m_onShutdownFunction.IsFunction())
	{
		LuaPlus::LuaFunctionVoid function = m_onShutdownFunction;
		function(m_luaObject);
	}

	if (m_luaObject.IsTable())
	{
		LuaPlus::LuaObject name = m_luaObject["m_name"];
		if (name.IsString())
		{
			LuaPlus::LuaObject factory = GetLuaState().GetGlobal("g_factory");
			factory.SetNil(m_luaObject);
		}

		m_luaObject.AssignNil();
	}
}

void Entity::Update(float dt)
{
	if ( m_onUpdateFunction.IsFunction())
	{
		LuaPlus::LuaFunctionVoid function = m_onUpdateFunction;
		function(m_luaObject, dt);
	}
}

void Entity::Render()
{
	if (m_model)
		m_model->Draw(GetWorldTransform());
}

void Entity::LoadModel(const char* filename)
{
	m_model = g_modelSystem->LoadModel(filename);
}

void Entity::SetVisible(bool visible)
{
}

bool Entity::GetVisible()
{
	return true;
}

void Entity::InitFromTable(LuaPlus::LuaObject& _object)
{
	SDL_assert_always(!_object.IsNil() && "Invalid object passed");

	// catch table instance
	m_luaObject = _object;

	// find functions
	m_onInitFunction = m_luaObject.GetByName("on_init");
	m_onShutdownFunction = m_luaObject.GetByName("on_shutdown");
	m_onUpdateFunction = m_luaObject.GetByName("on_update");

	// check
	SDL_assert_always(m_onInitFunction.IsFunction() || m_onShutdownFunction.IsFunction() || m_onUpdateFunction.IsFunction());

	// register entity functions
	m_luaObject.RegisterDirect("load_model", &Entity_LoadModel);
	//m_luaObject.RegisterDirect("set_visible", &Entity::SetVisible);
	//m_luaObject.RegisterDirect("get_visible", &Entity::GetVisible);

	// assign C++ object
	m_luaObject.SetLightUserdata("__object", this);

	// call init
	LuaPlus::LuaFunctionVoid onInitFunction = m_onInitFunction;
	onInitFunction(m_luaObject);
}

REGISTER_ENTITY(TempPlayer);

TempPlayer::TempPlayer()
{
}

TempPlayer::~TempPlayer()
{
}

void TempPlayer::Update(float dt)
{
	g_cameraManager.SetActiveCamera(&m_camera);

	UpdateCameraLook();

	UpdateCameraMovement(dt);
}

void TempPlayer::UpdateCameraMovement(float dt)
{
	// calculate player movement
	float speed = 19.0f * dt;

	uint32_t movementDir = GenMovementDir();

	if (movementDir & EMovementDir_Forward)
		m_position += speed * m_camera.GetFront();
	if (movementDir & EMovementDir_Backward)
		m_position -= speed * m_camera.GetFront();
	if (movementDir & EMovementDir_Left)
		m_position -= glm::normalize(glm::cross(m_camera.GetFront(), m_camera.GetUp())) * speed;
	if (movementDir & EMovementDir_Right)
		m_position += glm::normalize(glm::cross(m_camera.GetFront(), m_camera.GetUp())) * speed;

	// set position back to camera for calculation view matrix
	m_camera.SetPosition(m_position);
}

void TempPlayer::UpdateCameraLook()
{
	g_inputManager.SetRelativeMouseMode(true);

	glm::ivec2 mousePos = g_inputManager.GetMousePos();

	// calculate yaw and pitch
	static float yaw = 0.0f, pitch = 0.0f;

	int deltaX = mousePos.x;
	int deltaY = mousePos.y;

	float sensitivity = 0.15f;

	yaw += deltaX * sensitivity;
	pitch -= deltaY * sensitivity;

	if (pitch > 89.0f) pitch = 89.0f;
	if (pitch < -89.0f) pitch = -89.0f;

	m_camera.SetYawPitch(yaw, pitch);
}

uint32_t TempPlayer::GenMovementDir()
{
	uint32_t movementDir = EMovementDir_None;

	if (g_inputManager.GetKeyboard().IsKeyDown(SDLK_W)) {
		movementDir |= EMovementDir_Forward;
	}

	if (g_inputManager.GetKeyboard().IsKeyDown(SDLK_S)) {
		movementDir |= EMovementDir_Backward;
	}

	if (g_inputManager.GetKeyboard().IsKeyDown(SDLK_A)) {
		movementDir |= EMovementDir_Left;
	}

	if (g_inputManager.GetKeyboard().IsKeyDown(SDLK_D)) {
		movementDir |= EMovementDir_Right;
	}

	return movementDir;
}

REGISTER_ENTITY(Weapon);

Weapon::Weapon()
{
}

Weapon::~Weapon()
{
}

void Weapon::Update(float dt)
{
}
