#include "Utility/ViewCameraHelper.h"

#include "Mix/HID/IMouse.h"
#include "Mix/HID/IKeyboard.h"

namespace Utility {

ViewCameraHelper::ViewCameraHelper( void ) :
m_pMouse( NULL ),
m_pKeyboard( NULL ),
m_AtCoiffe( 0.0f ),
m_RotCoiffe( 0.0f ),
m_CtrlDistCoiffe( 0.0f ),
m_WheelDistCoiffe( 0.0f ),
m_Rot( 0.0f, 0.0f, 0.0f, 1.0f ),
m_At( 0.0f, 0.0f, 0.0f ),
m_Dist( 0.0f )
{
}

ViewCameraHelper::~ViewCameraHelper( void )
{
	Dispose();
}

void ViewCameraHelper::Initialize(	Mix::HID::IMouse* pMouse, Mix::HID::IKeyboard* pKeyboard,
									const Mix::Vector3& at, const Mix::Vector2& rot, Float32 dist,
									Float32 atCoiffe, Float32 rotCoiffe, Float32 ctrlDistCoiffe, Float32 wheelDistCoiffe )
{
	MIX_ASSERT( pMouse != NULL );
	MIX_ASSERT( pKeyboard != NULL );

	MIX_ADD_REF( pMouse );
	m_pMouse = pMouse;

	MIX_ADD_REF( pKeyboard );
	m_pKeyboard = pKeyboard;

	m_AtCoiffe = atCoiffe;
	m_RotCoiffe = rotCoiffe;
	m_CtrlDistCoiffe = ctrlDistCoiffe;
	m_WheelDistCoiffe = wheelDistCoiffe;

	m_At = at;

	m_Rot.SetRotationAxis( Mix::Vector3( -1.0f, 0.0f, 0.0f ), rot.x );
	m_Rot.RotationAxis( Mix::Vector3( 0.0f, 1.0f, 0.0f ), rot.y );
	m_Rot.Normalize();

	m_Dist = dist;
}

void ViewCameraHelper::Update( void )
{
	MIX_ASSERT( m_pMouse != NULL );
	MIX_ASSERT( m_pKeyboard != NULL );

	Mix::Vector2 mouseVel = m_pMouse->GetVelocity();
	Int32 mouseWheel = m_pMouse->GetWheelDelta();
	
	if( mouseVel.GetLengthSqr() > 0 )
	{
		if( ( m_pMouse->GetButtonState( 2 ) & Mix::HID::DOWN ) == Mix::HID::DOWN )
		{
			if( ( m_pKeyboard->GetKeyState( Mix::HID::KEY_SHIFT ) & Mix::HID::DOWN ) == Mix::HID::DOWN )
			{
				Mix::Vector3 dirX( 1.0f, 0.0f, 0.0f );
				Mix::Vector3 dirY( 0.0f, 1.0f, 0.0f );

				Mix::Matrix4x4 mat = m_Rot;

				dirX = mat * dirX;
				dirY = mat * dirY;

				m_At += dirX * mouseVel.x * m_AtCoiffe;
				m_At += dirY * mouseVel.y * m_AtCoiffe;
			}
			else if( ( m_pKeyboard->GetKeyState( Mix::HID::KEY_CTRL ) & Mix::HID::DOWN ) == Mix::HID::DOWN )
			{
				m_Dist = max( 0.0f, m_Dist + mouseVel.y * m_CtrlDistCoiffe );
			}
			else
			{
				Mix::Quaternion rot;

				rot.SetRotationAxis( Mix::Vector3( 0.0f, 1.0f, 0.0f ), mouseVel.x * m_RotCoiffe );
				rot.RotationAxis( Mix::Vector3( 1.0f, 0.0f, 0.0f ), -mouseVel.y * m_RotCoiffe );

				m_Rot = rot * m_Rot;
				m_Rot.Normalize();
			}
		}
	}

	if( mouseWheel < 0 )
	{
		m_Dist += m_WheelDistCoiffe;
	}
	else if( mouseWheel > 0 )
	{
		m_Dist = max( 0.0f, m_Dist - m_WheelDistCoiffe );
	}
}

void ViewCameraHelper::Dispose( void )
{
	MIX_RELEASE( m_pKeyboard );
	MIX_RELEASE( m_pMouse );
}

const Mix::Quaternion& ViewCameraHelper::GetRotation( void ) const
{
	return m_Rot;
}

const Mix::Vector3& ViewCameraHelper::GetAt( void ) const
{
	return m_At;
}

Float32 ViewCameraHelper::GetDistance( void ) const
{
	return m_Dist;
}

}
