/*
	Lan Wars.
	Copyright (c) 2009, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//=====================================================================================================================
/// @brief		Represents a 2D camera.
//=====================================================================================================================

#include "Precompiled.h"
#include "Graphics\Camera2D.h"
#include "Windows\Window.h"

using namespace Game;

//=====================================================================================================================
/// @brief		Creates a 2D camera
/// @param		window - window the camera's view will be in
/// @param		position - initial position of the camera
/// @param		reference_size - size of the viewing area the camera will try and show whilst maintaining aspect correctness
/// @param		z_near - near plane z value
/// @param		z_far - far plane z value
/// @pre		Given window cannot be null
/// @pre		Given area 'reference_size' must be valid
//=====================================================================================================================

Camera2D::Camera2D( Window * window , const Vec2f & position , const Vec2f & reference_size , float z_near , float z_far )
{
	// Make sure ok:

	ENSURE( window					, "Given window cannot be null"	);
	ENSURE( reference_size.x > 0	, "Given area 'reference_size' must be valid"	);
	ENSURE( reference_size.y > 0	, "Given area 'reference_size' must be valid"	);

	// Save all params:

	m_position			= position;
	m_reference_size	= reference_size;
	m_window			= window;
	m_z_near			= z_near;
	m_z_far				= z_far;
}

//=====================================================================================================================
/// @brief		Annouces the type name for this object.
/// @returns	Type name of this object
//=====================================================================================================================

const char * Camera2D::GetTypeName() const
{
	return "Camera2D";
}

//=====================================================================================================================
/// @brief	Interpolates the camera towards the given position
/// @param	position - position to interpolate to
/// @param	amount - amount of interpolation to do from 0-1
//=====================================================================================================================

void Camera2D::LerpTo( const Vec2f & position , float amount )
{
	// Do the lerp:
	
	m_position *= 1.0f - amount; 
	m_position += position * amount;
}

//=====================================================================================================================
/// @brief		Gets the view matrix used by the camera
/// @returns	View matrix used by the camera
//=====================================================================================================================

Matrix4f Camera2D::GetViewMatrix() const
{
	// Return an inverse translation of the camera's position:

	return Matrix4f::Translation(-m_position);
}

//=====================================================================================================================
/// @brief		Gets the dimension of the area of the world currently shown by the camera. This may differ from the 
///				reference view area depending on the current aspect ratio.
/// @returns	Dimension of the view area
/// @remarks	This is the full viewing area, not half extents
/// @pre		The window size must be greater than 0
/// @remarks	Since this function relies on retrieving the window size, if for some reason the size retrieval fails
///				(as happened occasionally on a test Vista x64 system when doing window flipping) then the function 
///				will return the reference view size given to the camera originally. This is to prevent disaster 
///				elswhere.
//=====================================================================================================================

Vec2f Camera2D::GetViewArea() const
{
    // Get the current screen resolution:
    
    Vec2u win_size = m_window->GetClientSize();   
    
    // If this is zero then return the reference size to cope with windows acting funny:
    
    if ( win_size.x == 0 || win_size.y == 0 )
    {
		return m_reference_size;
    }

    // Calculate the aspect ratio:

    float aspect = (float)(win_size.x) / (float)(win_size.y);      

    // Calculate the reference aspect ratio:

    float reference_aspect = m_reference_size.x / m_reference_size.y;

    // Calculate the area we will show when widescreen adjustments are taken into account
	
	Vec2f screen_area
	(
		( m_reference_size.x / reference_aspect ) * aspect , m_reference_size.y
	);

    // If we have a vertical widescreen display (highly unlikely!!!) then normalise the width to reference width and resize the height:

    if ( screen_area.x < m_reference_size.x )
    {
        // Rescale height for vertical widescreen mode:

        screen_area /= screen_area.x;

        screen_area.x *= m_reference_size.x;
        screen_area.x *= m_reference_size.y;
    }

    // Return the x area of the world that will be shown:
    
    return screen_area;
}

//=====================================================================================================================
/// @brief		Gets the projection matrix used by the camera
/// @returns	Projection matrix used by the camera
//=====================================================================================================================

Matrix4f Camera2D::GetProjectionMatrix() const
{
	// Get the view area shown by the camera:
	
	Vec2f view_area = GetViewArea();
	
	// Return the projection matrix:

	return Matrix4f::Orthographic( view_area.x , view_area.y , m_z_near , m_z_far );
}

//=====================================================================================================================
/// @brief		Gets the world matrix used by the camera
/// @returns	World matrix used by the camera
//=====================================================================================================================

Matrix4f Camera2D::GetWorldMatrix() const
{
	// Return a translation of the camera's position:

	return Matrix4f::Translation(m_position);
}

//=====================================================================================================================
/// @brief		Returns a combined world and projection matrix for the camera
/// @returns	Combined world and view matrix for the camera
//=====================================================================================================================

Matrix4f Camera2D::GetViewProjectionMatrix() const
{
	// Get the view and projection matrices:
	
	Matrix4f v = GetViewMatrix();
	Matrix4f p = GetProjectionMatrix();

	// Concatenate and return answer
	
	return v * p;;
}
