#include "Camera.h"
#include "OpenGLUtils.h"
#include "OpenGLMatrix.h"
#include "Color.h"

#include <Utility/Log.h>

//////////////////////////////////////////////////////////////
namespace Dubious
{
namespace Renderer
{

//////////////////////////////////////////////////////////////
Camera::Camera( const Math::RectangleYUp& Viewport, float FOVY )
	: m_Viewport( Viewport )
	, m_FOVY( FOVY )
	, m_MaxDepth( 500.0f )
{
}

//////////////////////////////////////////////////////////////
Camera::~Camera()
{
}

//////////////////////////////////////////////////////////////
void Camera::ReadyForPhoto() const
{
	OpenGLUtils::ClearColor( Color::WHITE );
	OpenGLUtils::Viewport( m_Viewport );

	SetupMatrices();
}

//////////////////////////////////////////////////////////////
void Camera::SetupMatrices() const
{
	// projection
	OpenGLMatrix::MatrixMode( GL_PROJECTION );
	OpenGLMatrix::LoadIdentity();
	float AspectRatio = static_cast<float>(m_Viewport.Dimensions().X()) / static_cast<float>(m_Viewport.Dimensions().Y());
	OpenGLUtils::Perspective( m_FOVY, AspectRatio, 1.0f, m_MaxDepth );

	// modelview
	OpenGLMatrix::MatrixMode( GL_MODELVIEW );
	OpenGLMatrix::LoadIdentity();
	Math::Vector X, Y, Z;
	m_CoordinateSpace.Axes( X, Y, Z );
	Math::Vector Eye = m_CoordinateSpace.Position();
	// If the m_ZAxisOffset is not equal to zero then the coordinate space of this
	// camera is situated in front of the camera (third person mode).  In this 
	// case we want to move the camera along the negative Z axis for the amount
	// specified by the Z Axis Offset
	if (!Math::Equals( m_ZAxisOffset, 0.0f )) {
		Math::Vector Offset = Z;
		Offset.SetLength( m_ZAxisOffset );
		Eye += Offset;
	}
//	LOG_INFO( "LookAt (" << Eye << ", " << Eye-Z << ", " << Y << ")" );
	OpenGLUtils::LookAt( Eye, Eye-Z, Y );
}

//////////////////////////////////////////////////////////////
Math::Vector Camera::Unproject( const Math::Point& P ) const
{
	LOG_INFO( "Calling SetupMatrices" );
	SetupMatrices();
	LOG_INFO( "Done" );

	Math::Vector V;
	float ModelMatrix[16];
	float ProjectionMatrix[16];
	int Viewport[4];
	glGetFloatv( GL_MODELVIEW_MATRIX, ModelMatrix );
	glGetFloatv( GL_PROJECTION_MATRIX, ProjectionMatrix );
	glGetIntegerv( GL_VIEWPORT, Viewport ); 

	OpenGLUtils::Unproject( P, ModelMatrix, ProjectionMatrix, Viewport, V );
	
	return V;
}

}
}
