#include "game/engine/player_camera.hpp"

using namespace polygun::engine;

static float mx,my;
float pitch_cos;

Camera::Camera(glm::vec3 position, glm::vec3 up, float yaw, float pitch) :
	m_position(position),
	m_front(glm::vec3(0.0f, 0.0f, -1.0f)),
	m_movement_front(),
	m_up(),
	m_right(),
	m_worldup(up),
	m_yaw(yaw),
	m_pitch(pitch),
	m_movement_speed(SPEED),
	m_mouse_sensitivity(SENSITIVITY),
	m_fov(FOV),
	m_last_x(SCR_WIDTH / 2.0f),
	m_last_y(SCR_HEIGHT / 2.0f),
	m_first_mouse(true)
{
	update_camera_vectors();
}

Camera::Camera(float pos_x, float pos_y, float pos_z, float up_x, float up_y, float up_z, float yaw, float pitch) :
	m_position(pos_x, pos_y, pos_z),
	m_front(glm::vec3(0.0f, 0.0f, -1.0f)),
	m_movement_front(),
	m_up(),
	m_right(),
	m_worldup(up_x, up_y, up_z),
	m_yaw(yaw),
	m_pitch(pitch),
	m_movement_speed(SPEED),
	m_mouse_sensitivity(SENSITIVITY),
	m_fov(FOV),
	m_last_x(SCR_WIDTH / 2.0f),
	m_last_y(SCR_HEIGHT / 2.0f),
	m_first_mouse(true)
{
	update_camera_vectors();
}

void Camera::update(bool reset) {
	if (m_first_mouse || reset) {
		m_last_x = mx;
		m_last_y = my;
		m_first_mouse = false;
	}

	float x_offset = mx - m_last_x;
	float y_offset = m_last_y - my; 

	m_last_x = mx;
	m_last_y = my;

	// only if mouse moved
	if (x_offset != 0.0f || y_offset != 0.0f)
		process_mouse_movement(x_offset, y_offset);

}

void Camera::process_movement(camera_movement direction, float delta_time) {
	float velocity = m_movement_speed * delta_time;
	if (direction == FORWARD)
		m_position += glm::vec3(m_movement_front.x, 0.0f, m_movement_front.y) * velocity;
	if (direction == BACKWARD)
		m_position -= glm::vec3(m_movement_front.x, 0.0f, m_movement_front.y) * velocity;
	if (direction == LEFT)
		m_position -= glm::vec3(m_right.x, 0.0f, m_right.z) * velocity;
	if (direction == RIGHT)
		m_position += glm::vec3(m_right.x, 0.0f, m_right.z) * velocity;
	if (direction == UP)
		m_position += glm::vec3(0.0f, 1.0f, 0.0f) * velocity;
	if (direction == DOWN)
		m_position += glm::vec3(0.0f, -1.0f, 0.0f) * velocity;
}

void Camera::process_mouse_movement(float x_offset, float y_offset, GLboolean constrain_pitch) {
	x_offset *= m_mouse_sensitivity;
	y_offset *= m_mouse_sensitivity;

	m_yaw   += x_offset;
	m_pitch += y_offset;

	if (constrain_pitch) {
		if (m_pitch > 89.9f)
			m_pitch = 89.9f;
		else if (m_pitch < -89.9f)
			m_pitch = -89.9f;
	}

	update_camera_vectors();
}

void Camera::process_mouse_scroll(float y_offset) {
	m_fov -= y_offset;
	if (m_fov < 1.0f)
		m_fov = 1.0f;
	if (m_fov > 45.0f)
		m_fov = 45.0f;
}

void Camera::mouse_callback(GLFWwindow* window, double x_pos_in, double y_pos_in) {
	mx = static_cast<float>(x_pos_in);
	my = static_cast<float>(y_pos_in);
}

void Camera::update_camera_vectors() {
	glm::vec3 front;
	pitch_cos = cos(glm::radians(m_pitch));

	m_movement_front.x = cos(glm::radians(m_yaw));
	m_movement_front.y = sin(glm::radians(m_yaw));

	front.x = m_movement_front.x * pitch_cos;
	front.y = sin(glm::radians(m_pitch));
	front.z = m_movement_front.y * pitch_cos;

	m_front = glm::normalize(front);
	m_right = glm::normalize(glm::cross(m_front, m_worldup));  
	m_up    = glm::normalize(glm::cross(m_right, m_front));
}
