#include "Camera.h"
#include "OGL.h"
#include <glm/gtx/rotate_vector.hpp>
#include <glm/gtx/quaternion.hpp>

Camera::Camera() {
	_viewDistance = 4.0f;
	_eye = glm::vec3(0.0f, 0.0f, _viewDistance);
	_at = glm::vec3(0.0f, 0.0f, 0.0f);
	_up = glm::vec3(0.0f, 1.0f, 0.0f);
    _view = glm::lookAt(_eye, _at, _up);
    _projection = glm::perspective(45.0f, 1.0f, 1.0f, 7.0f);
}

void Camera::init() {
	ShaderProgram* sp = OGL::instance()->getCurShaderProgram();
	GLint view = glGetUniformLocation(*sp, "mView");
	glUniformMatrix4fv(view, 1, GL_FALSE, glm::value_ptr(_view));
	GLint projection = glGetUniformLocation(*sp, "mProjection");
	glUniformMatrix4fv(projection, 1, GL_FALSE, glm::value_ptr(_projection));
	GLint eye = glGetUniformLocation(*sp, "vEye");
	glUniform3fv(eye, 1, glm::value_ptr(_eye));
}

Camera *Camera::instance() {
    static Camera camera;
    return &camera;
}

void Camera::updateView() {
	_view = glm::lookAt(_eye, _at, _up);
	ShaderProgram* sp = OGL::instance()->getCurShaderProgram();
	GLint view = glGetUniformLocation(*sp, "mView");
	glUniformMatrix4fv(view, 1, GL_FALSE, glm::value_ptr(_view));
	GLint eye = glGetUniformLocation(*sp, "vEye");
	glUniform3fv(eye, 1, glm::value_ptr(_eye));
}

void Camera::turnLeft(float angle) {
	glm::quat q = glm::angleAxis(angle, _up);
	_eye = glm::rotate(q, _eye);
	updateView();
}

void Camera::turnRight(float angle) {
	glm::quat q = glm::angleAxis(-angle, _up);
	_eye = glm::rotate(q, _eye);
	updateView();
}

void Camera::turnUp(float angle) {
	glm::vec3 axis = glm::normalize(glm::cross(_up, _eye));
	glm::quat q = glm::angleAxis(angle, axis);
	_eye = glm::rotate(q, _eye);
	_up = glm::normalize(glm::cross(_eye, axis));
	updateView();
}

void Camera::turnDown(float angle) {
	glm::vec3 axis = glm::normalize(glm::cross(_up, _eye));
	glm::quat q = glm::angleAxis(-angle, axis);
	_eye = glm::rotate(q, _eye);
	_up = glm::normalize(glm::cross(_eye, axis));
	updateView();
}

void Camera::rotateCW(float angle) {
	glm::quat q = glm::angleAxis(-angle, glm::normalize(_eye));
	_up = glm::rotate(q, _up);
	updateView();
}

void Camera::rotateCCW(float angle) {
	glm::quat q = glm::angleAxis(angle, glm::normalize(_eye));
	_up = glm::rotate(q, _up);
	updateView();
}

void Camera::moveToCenter(float distance) {
	_eye -= distance*glm::normalize(_eye);
	updateView();
}

void Camera::moveFromCenter(float distance) {
	_eye += distance*glm::normalize(_eye);
	updateView();
}

void Camera::moveForward(float distance) {
}

void Camera::moveBackward(float distance) {

}

void Camera::mouseMoveAroundCenter(float dx, float dy) {
	glm::quat q = glm::angleAxis(-dx, _up);
	glm::vec3 axis = glm::normalize(glm::cross(_up, _eye));
	glm::quat q2 = glm::angleAxis(-dy, axis);
	_eye = q*q2*_eye;
	_up = glm::normalize(glm::cross(_eye, axis));
	updateView();
}
