////////////////////////////////////////////////////////////////////////////////
// Filename: cameraclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "camera_class.h"

camera_class::camera_class()
{
	LOG_INIT("Initializing camera class");
	position_x_ = 0.0f;
	position_y_ = 0.0f;
	position_z_ = 0.0f;

	rotation_x_ = 0.0f;
	rotation_y_ = 0.0f;
	rotation_z_ = 0.0f;

	camera_speed_ = 4.0f;

	view_matrix_ = XMMatrixIdentity();
	reflection_view_matrix_ = XMMatrixIdentity();
	
	LOG_INIT("Camera class initialized");
}


camera_class::camera_class(const camera_class& other)
{
}


camera_class::~camera_class()
{
	LOG_SHUTDOWN("Camera class destructor called");
}

void camera_class::set_position(float x, float y, float z)
{
	position_x_ = x;
	position_y_ = y;
	position_z_ = z;
	return;
}


void camera_class::set_rotation(float x, float y, float z)
{
	rotation_x_ = x;
	rotation_y_ = y;
	rotation_z_ = z;
	return;
}

XMFLOAT3 camera_class::get_position()
{
	return XMFLOAT3(position_x_, position_y_, position_z_);
}


XMFLOAT3 camera_class::get_rotation()
{
	return XMFLOAT3(rotation_x_, rotation_y_, rotation_z_);
}

void camera_class::render()
{
	XMFLOAT3 up, position, lookAt;
	XMVECTOR upVector, positionVector, lookAtVector;
	float yaw, pitch, roll;
	XMMATRIX rotationMatrix;


	// Setup the vector that points upwards.
	up.x = 0.0f;
	up.y = 1.0f;
	up.z = 0.0f;

	// Load it into a XMVECTOR structure.
	upVector = XMLoadFloat3(&up);

	// Setup the position of the camera in the world.
	position.x = position_x_;
	position.y = position_y_;
	position.z = position_z_;

	// Load it into a XMVECTOR structure.
	positionVector = XMLoadFloat3(&position);

	// Setup where the camera is looking by default.
	lookAt.x = 0.0f;
	lookAt.y = 0.0f;
	lookAt.z = 1.0f;

	// Load it into a XMVECTOR structure.
	lookAtVector = XMLoadFloat3(&lookAt);

	// Set the yaw (Y axis), pitch (X axis), and roll (Z axis) rotations in radians.
	pitch = rotation_x_ * 0.0174532925f;
	yaw = rotation_y_ * 0.0174532925f;
	roll = rotation_z_ * 0.0174532925f;

	// Create the rotation matrix from the yaw, pitch, and roll values.
	rotationMatrix = XMMatrixRotationRollPitchYaw(pitch, yaw, roll);

	// Transform the lookAt and up vector by the rotation matrix so the view is correctly rotated at the origin.
	lookAtVector = XMVector3TransformCoord(lookAtVector, rotationMatrix);
	upVector = XMVector3TransformCoord(upVector, rotationMatrix);

	// Translate the rotated camera position to the location of the viewer.
	lookAtVector = XMVectorAdd(positionVector, lookAtVector);

	// Finally create the view matrix from the three updated vectors.
	view_matrix_ = XMMatrixLookAtLH(positionVector, lookAtVector, upVector);

	return;
}



XMMATRIX camera_class::get_view_matrix(XMMATRIX& view_matrix) const
{
	view_matrix = view_matrix_;
	return view_matrix;
}

void camera_class::render_reflection(float height)
{
	XMFLOAT3 up, position, lookAt;
	XMVECTOR upVector, positionVector, lookAtVector;
	float yaw, pitch, roll;
	XMMATRIX rotationMatrix;


	// Setup the vector that points upwards.
	up.x = 0.0f;
	up.y = 1.0f;
	up.z = 0.0f;

	// Load it into a XMVECTOR structure.
	upVector = XMLoadFloat3(&up);

	// Setup the position of the camera in the world.
	position.x = position_x_;
	position.y = -position_y_ + (height * 2.0f);
	position.z = position_z_;

	// Load it into a XMVECTOR structure.
	positionVector = XMLoadFloat3(&position);

	// Setup where the camera is looking by default.
	lookAt.x = 0.0f;
	lookAt.y = 0.0f;
	lookAt.z = 1.0f;

	// Load it into a XMVECTOR structure.
	lookAtVector = XMLoadFloat3(&lookAt);

	// Set the yaw (Y axis), pitch (X axis), and roll (Z axis) rotations in radians.
	pitch = (-1.0f * rotation_x_) * 0.0174532925f;  // Invert for reflection
	yaw = rotation_y_ * 0.0174532925f;
	roll = rotation_z_ * 0.0174532925f;

	// Create the rotation matrix from the yaw, pitch, and roll values.
	rotationMatrix = XMMatrixRotationRollPitchYaw(pitch, yaw, roll);

	// Transform the lookAt and up vector by the rotation matrix so the view is correctly rotated at the origin.
	lookAtVector = XMVector3TransformCoord(lookAtVector, rotationMatrix);
	upVector = XMVector3TransformCoord(upVector, rotationMatrix);

	// Translate the rotated camera position to the location of the viewer.
	lookAtVector = XMVectorAdd(positionVector, lookAtVector);

	// Finally create the view matrix from the three updated vectors.
	reflection_view_matrix_ = XMMatrixLookAtLH(positionVector, lookAtVector, upVector);

	return;
}

void camera_class::get_reflection_view_matrix(XMMATRIX& reflectionViewMatrix) const
{
	reflectionViewMatrix = reflection_view_matrix_;
	return;
}

void camera_class::move(float deltatime, float delta_x, float delta_y)
{
	if (!inputs_) return;

	float speed;
	float radiansY = rotation_y_ * TO_RAD;
	float radiansX = rotation_x_ * TO_RAD;

	rotate(delta_x, delta_y);

	if (inputs_->scroll_up && inputs_->right_click)
	{
		camera_speed_ *= 1.1f;
	}
	if (inputs_->scroll_down && inputs_->right_click)
	{
		camera_speed_ *= 0.9f;
		if (camera_speed_ < 0.25f) // Minimum speed.
		{
			camera_speed_ = 0.25f;
		}
	}

	if (inputs_->scroll_up && !inputs_->right_click)
	{
		speed = camera_speed_ * 20 * deltatime;
		position_x_ += sinf(radiansY) * cosf(radiansX) * speed;
		position_z_ += cosf(radiansY) * cosf(radiansX) * speed;
		position_y_ -= sinf(radiansX) * speed;
	}

	if (inputs_->scroll_up && !inputs_->right_click) 
	{
		speed = camera_speed_ * 20 * deltatime;
		position_x_ -= sinf(radiansY) * cosf(radiansX) * speed;
		position_z_ -= cosf(radiansY) * cosf(radiansX) * speed;
		position_y_ += sinf(radiansX) * speed;
	}

	speed = camera_speed_ * deltatime;

	if (inputs_->move_forward)
	{
		position_x_ += sinf(radiansY) * cosf(radiansX) * speed;
		position_z_ += cosf(radiansY) * cosf(radiansX) * speed;
		position_y_ -= sinf(radiansX) * speed;
	}

	if (inputs_->move_backward)
	{
		position_x_ -= sinf(radiansY) * cosf(radiansX) * speed;
		position_z_ -= cosf(radiansY) * cosf(radiansX) * speed;
		position_y_ += sinf(radiansX) * speed;
	}

	if (inputs_->move_left)
	{
		position_x_ -= cosf(radiansY) * speed;
		position_z_ += sinf(radiansY) * speed;
	}

	if (inputs_->move_right)
	{
		position_x_ += cosf(radiansY) * speed;
		position_z_ -= sinf(radiansY) * speed;
	}

	if (inputs_->move_up)
	{
		position_y_ += speed;
	}

	if (inputs_->move_down)
	{
		position_y_ -= speed;
	}

	return;
}

void camera_class::rotate(float delta_x, float delta_y)
{

	if (inputs_ == nullptr) return;
	
	float delta_yaw = delta_x * camera_sensitivity_;
	float delta_pitch = delta_y * camera_sensitivity_;
	if (inputs_->right_click)
	{
		rotation_y_ += delta_yaw;
		if (rotation_y_ < 0.0f)
		{
			rotation_y_ += 360.0f;
		}
		else if (rotation_y_ > 360.0f)
		{
			rotation_y_ -= 360.0f;
		}

		rotation_x_ += delta_pitch;
		if (rotation_x_ < -90.0f)
		{
			rotation_x_ = -90.0f;
		}
		else if (rotation_x_ > 90.0f)
		{
			rotation_x_ = 90.0f;
		}
		
	}
	return;
}

void camera_class::update_camera_inputs_states(input_class* inputs)
{
	inputs_->move_forward = inputs->is_key_pressed(DIK_W);
	inputs_->move_backward = inputs->is_key_pressed(DIK_S);
	inputs_->move_left = inputs->is_key_pressed(DIK_A);
	inputs_->move_right = inputs->is_key_pressed(DIK_D);
	inputs_->move_up = inputs->is_key_pressed(DIK_E);
	inputs_->move_down = inputs->is_key_pressed(DIK_Q);
	inputs_->scroll_up = inputs->IsScrollUp();
	inputs_->scroll_down = inputs->IsScrollDown();
	inputs_->right_click = inputs->IsRightMousePressed();
}