#include "hud.h"

#include <math.h>
#include "Mix/Scene/IRenderer.h"
#include "Mix/Scene/ISkyDome.h"
#include "Mix/Scene/IAtmosphereScatter.h"

const Float32 HUD::SUN_RANGE = MIX_TO_RAD( 9.0f );
const Float32 HUD::NIGHT_START = 0.1f;

HUD::HUD( void ) :
m_pRenderer( NULL ),
m_pSkyDome( NULL ),
m_pAtmosphereScatter( NULL )
{
}

HUD::~HUD( void )
{
}

void HUD::Initialize(	Mix::Scene::IRenderer* pRenderer,
						Mix::Scene::ISkyDome* pSkyDome,
						Mix::Scene::IAtmosphereScatter* pAtmosphereScatter,
						const Mix::Point& screenSize )
{
	MIX_ASSERT( pRenderer != NULL );
	MIX_ASSERT( pSkyDome != NULL );
	MIX_ASSERT( pAtmosphereScatter != NULL );

	MIX_ADD_REF( pRenderer );
	m_pRenderer = pRenderer;

	MIX_ADD_REF( pSkyDome );
	m_pSkyDome = pSkyDome;

	MIX_ADD_REF( pAtmosphereScatter );
	m_pAtmosphereScatter = pAtmosphereScatter;

	SetHUDEnabled( True );
	SetPosition( Mix::Point( screenSize.x - HUD::DLG_WIDTH, screenSize.y - HUD::DLG_HEIGHT ) );
	SetSize( Mix::Point( HUD::DLG_WIDTH, HUD::DLG_HEIGHT ) );
}

void HUD::OnOpen( void )
{
	Int32 itemY = 16;

	const Mix::Scene::IRenderer::FOG_SETTINGS& fogSetting = m_pRenderer->GetFogSettings();
	const Mix::Scene::ISkyDome::HEMISPHERE_SETTINGS& hsSettings = m_pSkyDome->GetHemisphereSettings();
	const Mix::Scene::IAtmosphereScatter::DAY_SETTINGS& asDaySetting = m_pAtmosphereScatter->GetDaySettings();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Basis
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_BasisL.SetPosition( Mix::Point( HUD::LABEL_X, itemY ) );
	m_BasisL.SetSize( Mix::Point( HUD::LABEL_WIDTH, HUD::ITEM_HEIGHT ) );
	m_BasisL.SetText( L"Basis:" );
	m_BasisL.Initialize( this, HUD::CTRL_BASIS_L );

	m_BasisRadiusSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_BasisRadiusSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_BasisRadiusSF.SetText( L"Radius" );
	m_BasisRadiusSF.SetRange( 200.0f, 1000.0f );
	m_BasisRadiusSF.SetStep( 1.0f );
	m_BasisRadiusSF.SetValue( hsSettings.radius );
	m_BasisRadiusSF.Initialize( this, HUD::CTRL_BASIS_RADIUS_SF );
	itemY += HUD::ITEM_Y_STEP;

	m_BasisLayerSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_BasisLayerSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_BasisLayerSF.SetText( L"Height" );
	m_BasisLayerSF.SetRange( 1.0f, 200.0f );
	m_BasisLayerSF.SetStep( 1.0f );
	m_BasisLayerSF.SetValue( hsSettings.height );
	m_BasisLayerSF.Initialize( this, HUD::CTRL_BASIS_LAYER_SF );
	itemY += HUD::ITEM_YY_STEP;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Sun
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_SunL.SetPosition( Mix::Point( HUD::LABEL_X, itemY ) );
	m_SunL.SetSize( Mix::Point( HUD::LABEL_WIDTH, HUD::ITEM_HEIGHT ) );
	m_SunL.SetText( L"Sun:" );
	m_SunL.Initialize( this, HUD::CTRL_SUN_L );

	m_SunIntensitySF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_SunIntensitySF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_SunIntensitySF.SetText( L"Intensity" );
	m_SunIntensitySF.SetRange( 0.01f, 1.0f );
	m_SunIntensitySF.SetStep( 0.01f );
	m_SunIntensitySF.SetValue( asDaySetting.sunIntensity );
	m_SunIntensitySF.Initialize( this, HUD::CTRL_SUN_INTENSITY_SF );
	itemY += HUD::ITEM_Y_STEP;

	m_SunTurbiditySF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_SunTurbiditySF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_SunTurbiditySF.SetText( L"Turbidity" );
	m_SunTurbiditySF.SetRange( 0.0f, 4.0f );
	m_SunTurbiditySF.SetStep( 0.1f );
	m_SunTurbiditySF.SetValue( asDaySetting.sunTurbidity );
	m_SunTurbiditySF.Initialize( this, HUD::CTRL_SUN_TURBIDITY_SF );
	itemY += HUD::ITEM_Y_STEP;

	m_SunPositionSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_SunPositionSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_SunPositionSF.SetText( L"Position" );
	m_SunPositionSF.SetRange( -HUD::SUN_RANGE, +HUD::SUN_RANGE );
	m_SunPositionSF.SetStep( 0.001f );
	m_SunPositionSF.SetValue( 0.0f );
	m_SunPositionSF.Initialize( this, HUD::CTRL_SUN_POSITION_SF );
	itemY += HUD::ITEM_YY_STEP;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Fog
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_FogL.SetPosition( Mix::Point( HUD::LABEL_X, itemY ) );
	m_FogL.SetSize( Mix::Point( HUD::LABEL_WIDTH, HUD::ITEM_HEIGHT ) );
	m_FogL.SetText( L"Fog:" );
	m_FogL.Initialize( this, HUD::CTRL_FOG_L );

	m_FogStartSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_FogStartSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_FogStartSF.SetText( L"Start" );
	m_FogStartSF.SetRange( 0.0f, 1000.0f );
	m_FogStartSF.SetStep( 1.0f );
	m_FogStartSF.SetValue( fogSetting.start );
	itemY += HUD::ITEM_Y_STEP;

	m_FogDistanceSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_FogDistanceSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_FogDistanceSF.SetText( L"Distance" );
	m_FogDistanceSF.SetRange( 0.0f, 1000.0f );
	m_FogDistanceSF.SetStep( 1.0f );
	m_FogDistanceSF.SetValue( fogSetting.end - fogSetting.start );
	itemY += HUD::ITEM_YY_STEP;

	m_FogStartSF.Initialize( this, HUD::CTRL_FOG_START_SF );
	m_FogDistanceSF.Initialize( this, HUD::CTRL_FOG_DISTANCE_SF );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Detail
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_DetailL.SetPosition( Mix::Point( HUD::LABEL_X, itemY ) );
	m_DetailL.SetSize( Mix::Point( HUD::LABEL_WIDTH, HUD::ITEM_HEIGHT ) );
	m_DetailL.SetText( L"Detail:" );
	m_DetailL.Initialize( this, HUD::CTRL_DETAIL_L );

	m_DetailHGSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_DetailHGSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_DetailHGSF.SetText( L"HG" );
	m_DetailHGSF.SetRange( 0.0f, 1.0f );
	m_DetailHGSF.SetStep( 0.01f );
	m_DetailHGSF.SetValue( asDaySetting.henyeyGreenstein );
	m_DetailHGSF.Initialize( this, HUD::CTRL_DETAIL_HG_SF );
	itemY += HUD::ITEM_Y_STEP;

	m_DetailRayleighSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_DetailRayleighSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_DetailRayleighSF.SetText( L"Rayleigh" );
	m_DetailRayleighSF.SetRange( 0.0f, 100.0f );
	m_DetailRayleighSF.SetStep( 1.0f );
	m_DetailRayleighSF.SetValue( asDaySetting.rayleigh );
	m_DetailRayleighSF.Initialize( this, HUD::CTRL_DETAIL_RAYLEIGH_SF );
	itemY += HUD::ITEM_Y_STEP;

	m_DetailMieSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_DetailMieSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_DetailMieSF.SetText( L"Mie" );
	m_DetailMieSF.SetRange( 0.0f, 0.1f );
	m_DetailMieSF.SetStep( 0.0001f );
	m_DetailMieSF.SetValue( asDaySetting.mie );
	m_DetailMieSF.Initialize( this, HUD::CTRL_DETAIL_MIE_SF );
	itemY += HUD::ITEM_Y_STEP;

	m_DetailLinSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_DetailLinSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_DetailLinSF.SetText( L"Lin" );
	m_DetailLinSF.SetRange( 0.0f, 1.0f );
	m_DetailLinSF.SetStep( 0.01f );
	m_DetailLinSF.SetValue( asDaySetting.lin );
	m_DetailLinSF.Initialize( this, HUD::CTRL_DETAIL_LIN_SF );
	itemY += HUD::ITEM_Y_STEP;

	m_DetailFexRedSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_DetailFexRedSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_DetailFexRedSF.SetText( L"FexR" );
	m_DetailFexRedSF.SetRange( 0.0f, 1.0f );
	m_DetailFexRedSF.SetStep( 0.01f );
	m_DetailFexRedSF.SetValue( asDaySetting.fex.r );
	m_DetailFexRedSF.Initialize( this, HUD::CTRL_DETAIL_FEX_R_SF );
	itemY += HUD::ITEM_Y_STEP;

	m_DetailFexGreenSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_DetailFexGreenSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_DetailFexGreenSF.SetText( L"FexG" );
	m_DetailFexGreenSF.SetRange( 0.0f, 1.0f );
	m_DetailFexGreenSF.SetStep( 0.01f );
	m_DetailFexGreenSF.SetValue( asDaySetting.fex.g );
	m_DetailFexGreenSF.Initialize( this, HUD::CTRL_DETAIL_FEX_G_SF );
	itemY += HUD::ITEM_Y_STEP;

	m_DetailFexBlueSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_DetailFexBlueSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_DetailFexBlueSF.SetText( L"FexB" );
	m_DetailFexBlueSF.SetRange( 0.0f, 1.0f );
	m_DetailFexBlueSF.SetStep( 0.01f );
	m_DetailFexBlueSF.SetValue( asDaySetting.fex.b );
	m_DetailFexBlueSF.Initialize( this, HUD::CTRL_DETAIL_FEX_B_SF );
	itemY += HUD::ITEM_Y_STEP;

	m_DetailFexAlphaSF.SetPosition( Mix::Point( HUD::ITEM_X, itemY ) );
	m_DetailFexAlphaSF.SetSize( Mix::Point( HUD::ITEM_WIDTH, HUD::ITEM_HEIGHT ) );
	m_DetailFexAlphaSF.SetText( L"FexA" );
	m_DetailFexAlphaSF.SetRange( 0.0f, 1.0f );
	m_DetailFexAlphaSF.SetStep( 0.01f );
	m_DetailFexAlphaSF.SetValue( asDaySetting.fex.a );
	m_DetailFexAlphaSF.Initialize( this, HUD::CTRL_DETAIL_FEX_A_SF );
	itemY += HUD::ITEM_Y_STEP;
}

void HUD::OnDispose( void )
{
	MIX_RELEASE( m_pAtmosphereScatter );
	MIX_RELEASE( m_pSkyDome );
	MIX_RELEASE( m_pRenderer );
}

void HUD::OnSlider_ValueChanged( Utility::UI::SliderF* pSlider )
{
	static const Mix::Vector3 SUN_ROT_AXIS( 1.0f, 0.0f, 0.0f );
	static const Mix::Vector3 SUN_INIT_POS( 0.0f, 50.0f, 0.0f );

	Mix::Scene::IRenderer::FOG_SETTINGS fogSettings = m_pRenderer->GetFogSettings();
	Mix::Scene::ISkyDome::HEMISPHERE_SETTINGS hsSettings = m_pSkyDome->GetHemisphereSettings();
	Mix::Scene::ISkyDome::BASE_SETTINGS baseSettings = m_pSkyDome->GetBaseSettings();
	Mix::Scene::IAtmosphereScatter::DAY_SETTINGS asDaySetting = m_pAtmosphereScatter->GetDaySettings();
	Mix::Quaternion sunRot;
	Float32 sunHeight;

	switch( pSlider->GetID() )
	{
	case HUD::CTRL_BASIS_RADIUS_SF:
		hsSettings.radius = pSlider->GetValue();
		m_pSkyDome->SetHemisphereSettings( hsSettings );
		break;
	case HUD::CTRL_BASIS_LAYER_SF:
		hsSettings.height = pSlider->GetValue();
		m_pSkyDome->SetHemisphereSettings( hsSettings );
		break;

	case HUD::CTRL_SUN_INTENSITY_SF:
		asDaySetting.sunIntensity = pSlider->GetValue();
		m_pAtmosphereScatter->SetDaySettings( asDaySetting );
		break;
	case HUD::CTRL_SUN_TURBIDITY_SF:
		asDaySetting.sunTurbidity = pSlider->GetValue();
		m_pAtmosphereScatter->SetDaySettings( asDaySetting );
		break;
	case HUD::CTRL_SUN_POSITION_SF:
		sunRot.SetRotationAxis( SUN_ROT_AXIS, MIX_PI * pSlider->GetValue() );
		m_pSkyDome->SetSunPosition( Mix::Matrix4x4( sunRot ) * SUN_INIT_POS );

		sunHeight = m_pSkyDome->GetSunLight().height;

		baseSettings.texScale = 10.0f;

		if( sunHeight > HUD::NIGHT_START )
		{
			baseSettings.texOpacity = 0.0f;
		}
		else if( sunHeight > 0.0f )
		{
			baseSettings.texOpacity = MIX_FLOAT_SATURATE( 1.0f - ( sunHeight / HUD::NIGHT_START ) );
		}
		else
		{
			baseSettings.texOpacity = 1.0f;
		}

		m_pSkyDome->SetBaseSettings( baseSettings );
		break;

	case HUD::CTRL_FOG_START_SF:
		fogSettings.start = pSlider->GetValue();
		fogSettings.end = pSlider->GetValue() + m_FogDistanceSF.GetValue();
		m_pRenderer->SetFogSettings( fogSettings );
		break;
	case HUD::CTRL_FOG_DISTANCE_SF:
		fogSettings.end = fogSettings.start + pSlider->GetValue();
		m_pRenderer->SetFogSettings( fogSettings );
		break;

	case HUD::CTRL_DETAIL_HG_SF:
		asDaySetting.henyeyGreenstein = pSlider->GetValue();
		m_pAtmosphereScatter->SetDaySettings( asDaySetting );
		break;
	case HUD::CTRL_DETAIL_RAYLEIGH_SF:
		asDaySetting.rayleigh = pSlider->GetValue();
		m_pAtmosphereScatter->SetDaySettings( asDaySetting );
		break;
	case HUD::CTRL_DETAIL_MIE_SF:
		asDaySetting.mie = pSlider->GetValue();
		m_pAtmosphereScatter->SetDaySettings( asDaySetting );
		break;
	case HUD::CTRL_DETAIL_LIN_SF:
		asDaySetting.lin = pSlider->GetValue();
		m_pAtmosphereScatter->SetDaySettings( asDaySetting );
		break;
	case HUD::CTRL_DETAIL_FEX_R_SF:
		asDaySetting.fex.r = pSlider->GetValue();
		m_pAtmosphereScatter->SetDaySettings( asDaySetting );
		break;
	case HUD::CTRL_DETAIL_FEX_G_SF:
		asDaySetting.fex.g = pSlider->GetValue();
		m_pAtmosphereScatter->SetDaySettings( asDaySetting );
		break;
	case HUD::CTRL_DETAIL_FEX_B_SF:
		asDaySetting.fex.b = pSlider->GetValue();
		m_pAtmosphereScatter->SetDaySettings( asDaySetting );
		break;
	case HUD::CTRL_DETAIL_FEX_A_SF:
		asDaySetting.fex.a = pSlider->GetValue();
		m_pAtmosphereScatter->SetDaySettings( asDaySetting );
		break;
	}
}
