//////////////////////////////////////////////////////////////////////////////
//
// Asura_EffectVolume_Camera_EntityData.cpp
//
// Copyright (c) 2008 Rebellion. All rights reserved.
//
// Created by Emil Popovic - 19/12/2008
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

// Core
#include "Core/Asura.h"

// This file's header.
#include "SpecialFX/EffectVolume/Asura_EffectVolume_Camera_EntityData.h"

// Engine independent.
#include "Camera/Asura_Camera.h"
#include "Core/Asura_Timers.h"
#include "Rendering/Asura_Fog.h"
#include "Entity/Triggers/EffectVolume/Asura_ClientEntity_EffectVolume.h"
#include "Entity/Triggers/EffectVolume/Asura_ServerEntity_EffectVolume.h"

// Tools
#ifdef ASURA_TOOLS
#include "Tools_System.h"
#include "Entity/EffectVolume/Tools_Entity_EffectVolume.h"
#endif



////////////////////////////////////////////////////////////////////////////////
// Version	Author	Comment
// ~~~~~~~	~~~~~~	~~~~~~~
// 1		EP		Created.
////////////////////////////////////////////////////////////////////////////////
const u_int	Asura_EffectVolume_Camera_EntityData::s_uChunkVersion	= 1;



// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////



Asura_EffectVolume_Camera_EntityData::Asura_EffectVolume_Camera_EntityData()
: m_uGuid(ASURA_HASH_ID_UNSET)
, m_uPriority(5)
, m_fCurrentStrength(0.0f)
, m_bKeepAlive(true)
{
}

Asura_EffectVolume_Camera_EntityData::~Asura_EffectVolume_Camera_EntityData()
{
}


// when this function returns false, it means that we have to destroy this EntityData. It is because Client doesn't exist anymore, or we are too long outside volume.
void Asura_EffectVolume_Camera_EntityData::Update( const Asura_Vector_3& xPosition )
{
#ifdef ASURA_TOOLS
	Asura_ServerEntity_EffectVolume* pxEntity = Asura_ServerEntity_EffectVolume::FindEntity( m_uGuid );
#else
	Asura_ClientEntity_EffectVolume* pxEntity = Asura_ClientEntity_EffectVolume::FindEntity( m_uGuid );
#endif


	// Object no longer exists. That means, someone has destroyed it. Time to destroy also this EntityData object too.
	if( !pxEntity )
	{
		m_fCurrentStrength = 0.0f;
		m_bKeepAlive = false;
		return;
	}

	Asura_EffectVolume_EntityParameters* xData = &pxEntity->GetData();

	bool bIsWithinVolume			= false; 
	bool bIsWithinTransitionVolume  = false; 

	// We can consider the position within volume only when the object is active. If it is not, lets pretend that we are outside the volume.
	if( pxEntity->IsEntityActive() )
	{
		bIsWithinVolume				= xData->IsPositionWithinVolume( xPosition );
		bIsWithinTransitionVolume	= xData->IsPositionWithinTransitionVolume( xPosition );

#ifdef ASURA_TOOLS
		// Inside tools, we can preview this object only in specific situations.
		if( !Tools_Entity_EffectVolume::CanPreviewThisObjectInTools(pxEntity) )
		{
			bIsWithinVolume = false;
			bIsWithinTransitionVolume = false;
		}
#endif

	}

	// now actualize m_fCurrentStrength value.
	UpdateCurrentStrength( bIsWithinVolume, bIsWithinTransitionVolume, xData, xPosition );

	if( m_fCurrentStrength==0.0f && bIsWithinVolume==false && bIsWithinTransitionVolume==false )
	{
		m_bKeepAlive = false;
	}
}


void Asura_EffectVolume_Camera_EntityData::UpdateCurrentStrength( bool bIsWithinVolume, bool bIsWithinTransitionVolume, const Asura_EffectVolume_EntityParameters* xData, const Asura_Vector_3& xPosition )
{
	// This is tricky part here. The way how we update m_fCurrentStrength is that
	// we will create "destination or aimed" strength first. After that CurrentStrength will 
	// "move" towards that value. And speed of movement will be determined by FadeIn or 
	// FadeOut time values.


	float fAimedStrength;	// Ok, here is the Aimed value to which CurrentValue would like to match.

	// We must make sure that we are not inside some TransitionVolume, Because if we are, 
	// then we have to force this value over any other time value.
	if( bIsWithinTransitionVolume )
	{
		fAimedStrength = xData->CalculateStrengthFromTransitionVolume( xPosition );
	}
	else
	{
		fAimedStrength = (bIsWithinVolume) ? 1.0f : 0.0f;
	}

	// Now try to match m_fCurrentStrength with AimedStrength.
	// If fAimedStrength is smaller that current m_fCurrentStrength, we have to lower the m_fCurrentStrength
	// to the level of fAimedStrength. For doing this, we will use Time Parameter FadeOut.
	// if fAimedStrength is bigger that current m_fCurrentStrength, we have to increase m_fCurrentStrength to 
	// match fAimedStrength. We will use FadeIn time parameter.

	if( m_fCurrentStrength<fAimedStrength )
	{
		if( xData->GetFadeInDuration()==0.0f )
		{
			m_fCurrentStrength = fAimedStrength;
		}
		else
		{
			m_fCurrentStrength += ( 1.0f / xData->GetFadeInDuration() ) * Asura_Timers::GetGameTimeSlice();

			if( m_fCurrentStrength>fAimedStrength )	// Clamp value. Don't go further that aimed strength;
			{
				m_fCurrentStrength=fAimedStrength;
			}
		}
	}
	else if( m_fCurrentStrength>fAimedStrength )
	{
		if( xData->GetFadeOutDuration()==0.0f )
		{
			m_fCurrentStrength = fAimedStrength;
		}
		else
		{
			m_fCurrentStrength -= ( 1.0f / xData->GetFadeOutDuration() ) * Asura_Timers::GetGameTimeSlice();

			if( m_fCurrentStrength<fAimedStrength )	// Clamp value. Don't go further that aimed strength;
			{
				m_fCurrentStrength=fAimedStrength;
			}
		}
	}
}


bool Asura_EffectVolume_Camera_EntityData::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	u_int uVersion;

	xStream >> uVersion;
	if( uVersion>s_uChunkVersion )
	{
		Asura_Assert( false, "Unexpected Asura_EffectVolume_Camera_EntityData chunk version");
		return false;
	}

	xStream >> m_uGuid;
	xStream >> m_uPriority;
	xStream >> m_fCurrentStrength;

	return true;
}


void Asura_EffectVolume_Camera_EntityData::WriteToChunkStream (Asura_Chunk_Stream& xStream) const
{
	xStream << s_uChunkVersion;

	xStream << m_uGuid;
	xStream << m_uPriority;
	xStream << m_fCurrentStrength;		// This is only important value to save!
}