//////////////////////////////////////////////////////////////////////////////
//
// Asura_ClientEntity_Projector.cpp
//
// Copyright (c) 2007 Rebellion (Derby). All rights reserved.
//
// Description:
//
// An entity to project a texture onto the environment
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

#ifdef ASURA_TARGET_SM3

// This file's header.
#include "Entity/Projector/Asura_ClientEntity_Projector.h"

// Engine independent.
#include "Core/Asura_Timers.h"
#include "Core/Asura_TextureManagement.h"
#include "Camera/Asura_Camera.h"
#include "Network/Asura_Message.h"
#include "Projectors/Asura_Projector_System.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/PhysicalObject/Asura_ClientEntity_PhysicalObject.h"
#include "Entity/PhysicalObject/Asura_ServerEntity_PhysicalObject.h"

#ifdef ASURA_TOOLS
#include "EntityTemplate/Tools_EntityTemplate_System.h"
#endif

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

#ifdef ASURA_TOOLS
Asura_Hashed_List<Asura_ClientEntity_Projector> Asura_ClientEntity_Projector::s_xList;
#endif

Asura_ClientEntity_Projector* Asura_ClientEntity_Projector::CreateFromChunkStream(Asura_Guid uGuid, Asura_Chunk_Stream &xStream)
{
	Asura_ClientEntity_Projector* pxNew = new Asura_ClientEntity_Projector(uGuid);

	pxNew->ReadFromServerProjectorChunkStream(xStream);

#ifdef ASURA_TOOLS
	Asura_Texture_Management::AddTexture(pxNew->m_xParams.m_xTextureName);	
	Asura_Texture_Management::AddTexture(pxNew->m_xParams.m_xDetailTextureName);
	Asura_Texture_Management::AddTexture(pxNew->m_xParams.m_xFalloffTextureName);
	Asura_Texture_Management::LoadTextures();
#endif

	pxNew->SetParams(pxNew->m_xParams);

	pxNew->Update();

	return pxNew;
}


#ifdef ASURA_TOOLS
Asura_ClientEntity_Projector* Asura_ClientEntity_Projector::CreateFromTools( Asura_Guid uGuid, const Asura_ServerEntity_Projector::ProjectorParams &xParams)
{
	Asura_ClientEntity_Projector* pxNew = new Asura_ClientEntity_Projector(uGuid);

	pxNew->SetEntityActiveFromLoad(pxNew->IsEntityActive());
	pxNew->SetEntityActive(pxNew->IsEntityActive());

	pxNew->SetParams(xParams);

	pxNew->Update();

	return pxNew;
}
#endif


Asura_ClientEntity_Projector::Asura_ClientEntity_Projector( Asura_Guid uGuid)
	: PARENT(uGuid)
{
#ifdef ASURA_TOOLS
	s_xList.AddToList(this);
#endif
	m_pxProjector = NULL;
	Classification = AsuraEntityClass_Projector;
}


Asura_ClientEntity_Projector::~Asura_ClientEntity_Projector( void )
{
#ifdef ASURA_TOOLS
	s_xList.RemoveFromList(this);
#endif

	if (m_pxProjector)
	{
		Asura_Projector_System::DestroyProjector(m_pxProjector);		
		m_pxProjector=NULL;
	}
}

void Asura_ClientEntity_Projector::ReadFromServerProjectorChunkStream(Asura_Chunk_Stream& xStream)
{
	u_int uVersion;
	
	xStream >> uVersion;
	bool bActive = true;

	if (uVersion >= 7)
	{
		Asura_ServerEntity_Projector::SkippedServerData xSkippedData;
		Asura_ServerEntity_Projector::SkipDataInChunkStream(xStream, &xSkippedData);
		bActive = xSkippedData.m_bActive && xSkippedData.m_bTemplateActive;
	}
	Asura_ServerEntity_Projector::ExtractParamsFromChunkStream(m_xParams, xStream, uVersion );
	SetEntityActiveFromLoad(bActive);
	SetEntityActive(bActive);
}


void Asura_ClientEntity_Projector::OnEntityActivate()
{
	AddToUpdateableClientList(this);
	PARENT::OnEntityActivate();

	SetParams(m_xParams);
}

void Asura_ClientEntity_Projector::OnEntityDeactivate()
{
	RemoveFromUpdateableClientList(this, false);
	PARENT::OnEntityDeactivate();

	SetParams(m_xParams);

}



void Asura_ClientEntity_Projector::Update( void )
{        
	// If texture isn't there, keep calling SetParams, as it may not have been loaded yet. Especially in tools

	if (m_pxProjector == NULL || m_pxProjector->m_iTextureIndex < 0)
	{
		SetParams(m_xParams);		
	}
	
	// Update the movement, if we're moving
	if (m_pxProjector!=NULL)
	{
		m_pxProjector->m_xPos	 = m_xParams.m_xPosition;

		if (m_xParams.m_bMoving)
		{
			Asura_Euler_3		xAngles;
			Asura_Matrix_3x3	xRot;
			
			float fTime = Asura_Timers::GetLocalScaledTime();
			
			float fPhase = m_xParams.m_fRotationOffset;

			xAngles = m_xParams.m_xRotSpeed * (fPhase + fTime);

			if (m_xParams.m_bSwingX)
			{				
				xAngles.x = m_xParams.m_xSwingDist.x * Asura_Maths::Sine(xAngles.x);
			}

			if (m_xParams.m_bSwingY)
			{				
				xAngles.y = m_xParams.m_xSwingDist.y * Asura_Maths::Sine(xAngles.y);
			}

			if (m_xParams.m_bSwingZ)
			{				
				xAngles.z = m_xParams.m_xSwingDist.z * Asura_Maths::Sine(xAngles.z);
			}
			
			xAngles.ToMatrix(xRot);

			m_pxProjector->m_xOrient = m_xParams.m_xOrientation;			
			m_pxProjector->m_xOrient = m_pxProjector->m_xOrient * xRot;
		}
		else
		{					
			m_pxProjector->m_xOrient = m_xParams.m_xOrientation;
		}

		// Handle attachment to a parent
		if(m_xParams.m_uMasterEntity != ASURA_GUID_UNREGISTERED)
		{
			// This is a a pain, because:
			// - Most client entities don't exist in tools
			// - GetPosition() on an advance light entity doesn't work (!!)

			Asura_ServerEntity_PhysicalObject * pxMasterSvr	   = Asura_ServerEntity_PhysicalObject::FindEntity(m_xParams.m_uMasterEntity);
			Asura_ClientEntity_PhysicalObject * pxMasterClient = Asura_ClientEntity_PhysicalObject::FindEntity(m_xParams.m_uMasterEntity);
			
			if(pxMasterSvr || pxMasterClient)
			{	
				Asura_Matrix_3x3 xMasterOrient;
				Asura_Vector_3   xMasterPos;

				// Take whatever position we can find... client is preferred, but isn't available in tools
				if (pxMasterClient)
				{				
					xMasterOrient = pxMasterClient->GetCurrentOrientation();
					xMasterPos	  = pxMasterClient->GetCurrentPosition();
				}
				else
				{				
					xMasterOrient = pxMasterSvr->GetOrientation();
					xMasterPos	  = pxMasterSvr->GetPosition();
				}

				const Asura_Vector_3&	xCurrentPosition	= m_xParams.m_xPosition;				
				Asura_Vector_3 xPosition = xCurrentPosition;				
				Asura_Matrix_3x3 xOrientation = m_xParams.m_xOrientation;

				if(m_xParams.m_uMasterMarker && pxMasterClient)
				{									
					pxMasterClient->GetMarkerLocationFromHash(m_xParams.m_uMasterMarker,&xPosition, &xOrientation);
				}
				else
				{				
					xPosition	 = m_xParams.m_xOffsetFromMaster;									
					xPosition	 = xPosition * xMasterOrient;
					xPosition	 = xMasterPos + xPosition;
					xOrientation = xMasterOrient; 
				}
				

				m_pxProjector->m_xOrient = xOrientation;
				m_pxProjector->m_xPos    = xPosition;				
			}
		}

		m_pxProjector->UpdateVertices();		
	}
}



void Asura_ClientEntity_Projector::SetParams( const Asura_ServerEntity_Projector::ProjectorParams& xNewParams )
{
	m_xParams = xNewParams;

	if (IsEntityActive())
	{

		if (m_pxProjector==NULL)
		{
			m_pxProjector = Asura_Projector_System::CreateProjector();		
		}

		if (m_pxProjector!=NULL)
		{
			m_pxProjector->m_iTextureIndex = Asura_Texture_Management::GetIndexFromHashID(m_xParams.m_uTextureHash);
	//		m_pxProjector->m_iFalloffTextureIndex = Asura_Texture_Management::GetIndexFromHashID(m_xParams.m_uFalloffTextureHash);
			m_pxProjector->m_iDetailTextureIndex = Asura_Texture_Management::GetIndexFromHashID(m_xParams.m_uDetailTextureHash);
			m_pxProjector->m_fDetailTextureScale = m_xParams.m_fDetailTextureScale;

			// Update the frustum params
			m_pxProjector->m_fFoV	   = m_xParams.m_fFoV;
			m_pxProjector->m_fMinRange = m_xParams.m_fMinRange;
			m_pxProjector->m_fMaxRange = m_xParams.m_fMaxRange;
			m_pxProjector->m_eType     = m_xParams.m_bParallel?ASURA_PROJECTOR_TYPE_PARALLEL:ASURA_PROJECTOR_TYPE_PERSPECTIVE;
			m_pxProjector->m_fUVAspect = m_xParams.m_fAspect;
			m_pxProjector->m_fScale    = m_xParams.m_fScale;
			
			// Update the Blending mode
			m_pxProjector->m_eBlendMode = static_cast<Asura_Projector_BlendMode>(m_xParams.m_iBlendMode);

			// Update the Movement params
			m_pxProjector->m_xPos	 = m_xParams.m_xPosition;
			m_pxProjector->m_xOrient = m_xParams.m_xOrientation;
			m_pxProjector->UpdateVertices();

			m_pxProjector->m_fDistFadeStart = m_xParams.m_fFadeoutStart;
			m_pxProjector->m_fDistFadeRange = m_xParams.m_fFadeoutDist;

			m_pxProjector->m_bFakeLight		= m_xParams.m_bFakeLight;
			m_pxProjector->m_bDynamicLight	= m_xParams.m_bDynamicLight;		
			m_pxProjector->m_fIntensity     = m_xParams.m_fIntensity;

			m_pxProjector->m_afRGB[0] = m_xParams.m_afRGB[0];
			m_pxProjector->m_afRGB[1] = m_xParams.m_afRGB[1];
			m_pxProjector->m_afRGB[2] = m_xParams.m_afRGB[2];
		}
	}
	else
	{
		if (m_pxProjector)
		{
			Asura_Projector_System::DestroyProjector(m_pxProjector);		
			m_pxProjector=NULL;
		}
	}
}



void Asura_ClientEntity_Projector::RenderFrustum(void)
{
	if (m_xParams.m_bShowFrustum && m_pxProjector)
	{
		m_pxProjector->RenderFrustum();
	}
}


#ifdef ASURA_TOOLS
void Asura_ClientEntity_Projector::ApplyChangesForTemplateInstance(class Tools_EntityTemplateInstance* pxInstance)
{
	m_xParams.m_xPosition    = pxInstance->TransformPosition(m_xParams.m_xPosition);
	m_xParams.m_xOrientation = pxInstance->TransformMatrix(m_xParams.m_xOrientation);
	m_xParams.m_uMasterEntity = pxInstance->ConvertGuid(m_xParams.m_uMasterEntity);
	m_xParams.m_xOffsetFromMaster = m_xParams.m_xOffsetFromMaster * pxInstance->GetRelativeMatrix();
}
#endif // ASURA_TOOLS




#endif //#ifdef ASURA_TARGET_SM3
