//	Darkstar Engine  (c) UnderWorld Interactive Studio (all coding by Svarog    2007)
//
//      Данный файл является частью Darkstar Engine. Любое использование кода файла
//      без согласия компании, является нарушением закона об авторском праве (в слу-
//      чае если не оговорено обратное).
//
//      Имя файла: fpcamera.cpp
//      Краткое описание: First person camera
//

#include <Darkstar/Graphic System/Camera/camera.hpp>
#include <Darkstar/Graphic System/Direct3D/graphicDriver.hpp>

using gs::GraphicDriver;
using gs::Camera;

Camera::Camera()
{
	position = math::vect3_zero;
	up = math::vect3_sing_y;
	target = math::vect3_zero;
	fov = znear = zfar = aspect = 0;
	is_correct_view = false;
	is_correct_proj = false;
	projection = math::matr4_sing;
	view = math::matr4_sing;
};

Camera::Camera(const float fov, const float aspect, const float znear, const float zfar)
{
	create(fov, aspect, znear, zfar);
	position = math::vect3_zero;
	up = math::vect3_sing_y;
	target = math::vect3_zero;
};

Camera::~Camera()
{

};

void Camera::create(const float fov, const float aspect, const float znear, const float zfar)
{
	this->fov = fov;
	this->aspect = aspect;
	this->znear = znear;
	this->zfar = zfar;
	is_correct_proj = false;
};

void Camera::setLookAt(const Vect3& new_position, const Vect3& new_target, const Vect3& new_up)
{
	position = new_position;
	target = new_target;
	up = new_up;
	is_correct_view = false;
};

void Camera::setAspect(const float new_aspect)
{
	aspect = new_aspect;
	is_correct_proj = false;
};

void Camera::setFOV(const float new_fov)
{
	fov = new_fov;
	is_correct_proj = false;
};

void Camera::setZfar(const float new_zfar)
{
	zfar = new_zfar;
	is_correct_proj = false;
};

void Camera::setZnear(const float new_znear)
{
	znear = new_znear;
	is_correct_proj = false;
};

void Camera::setZnearFar(const float z_near, const float z_far)
{
	znear = z_near;
	zfar = z_far;
	is_correct_proj = false;
};

void Camera::setPosition(const Vect3& new_position, bool without_correct_view)
{
	position = new_position;
	is_correct_view = without_correct_view;
};

void Camera::setTarget(const Vect3& new_target)
{
	target = new_target;
	is_correct_view = false;
};

void Camera::setUp(const Vect3& new_up)
{
	up = new_up;
	is_correct_view = false;
};

const Vect3& Camera::getPosition() const
{
	return position;
};

const Vect3& Camera::getTarget() const
{
	return target;
};

const Vect3& Camera::getUp() const
{
	return up;
};

float Camera::getAspect() const
{
	return aspect;
};

float Camera::getFOV() const
{
	return fov;
};

float Camera::getZfar() const
{
	return zfar;
};

float Camera::getZnear() const
{
	return znear;
};

void Camera::_recalcMatrix()
{
	if(!is_correct_view)
	{
		Vect3 zaxis = (target-position).getNormalize();
		Vect3 xaxis = math::cross(up, zaxis).getNormalize();
		Vect3 yaxis = math::cross(zaxis, xaxis);
		view = math::Matr4(xaxis.x, xaxis.y, xaxis.z, -math::dot(xaxis, position),
						yaxis.x, yaxis.y, yaxis.z, -math::dot(yaxis, position),
						zaxis.x, zaxis.y, zaxis.z, -math::dot(zaxis, position),
						0, 0, 0, 1);
		is_correct_view = true;
	};
	if(!is_correct_proj)
	{
		float yscale = 1/tanf(fov*0.5f);
		float xscale = yscale/aspect;
		float znf = zfar - znear;
		float znff = zfar/znf;
		projection = math::Matr4(xscale, 0, 0, 0,
								0, yscale, 0, 0,
								0, 0, znff, -znear*znff, 
								0, 0, 1, 0);
		is_correct_proj = true;
	};
};

math::Matr4 Camera::getViewProjection()
{
	_recalcMatrix();
	return projection*view;
};

const math::Matr4& Camera::getViewMatrix()
{
	_recalcMatrix();
	return view;
};

const math::Matr4& Camera::getProjectionMatrix()
{
	_recalcMatrix();
	return projection;
};

void Camera::recalcMatrix()
{
	_recalcMatrix();
	//GraphicDriver::ptr()->getDevice()->SetTransform(D3DTS_VIEW, reinterpret_cast<D3DXMATRIX*>(&(view.getTranspose())));
	//GraphicDriver::ptr()->getDevice()->SetTransform(D3DTS_PROJECTION, reinterpret_cast<D3DXMATRIX*>(&(projection.getTranspose())));
};
