
//////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture.cpp
// Author    : Jason Turner
// Copyright (c) 2008 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef ASURA_USE_VIDEO_TO_TEXTURE_SYSTEM

//
// Asura independent includes
//
#include "Core/Asura_String.h"
#include "Core/Asura_Timers.h"
#include "Cutscene/Asura_FMVSubtitles.h"
#include "Debugging/Asura_Logfile.h"
#include "FMV/Asura_VideoToTexture.h"
#include "FMV/Asura_VideoToTexture_System.h"
#ifdef ASURA_USE_PRIMITIVE_RENDER
#include "Rendering/Asura_Primitive_Render.h"
#endif
#include "Sound/Asura_Audio_System.h"
#include "Sound/Asura_Audio_Voice.h"

u_int Asura_VideoToTexture::s_uRefCount = 0;
u_int Asura_VideoToTexture::s_uTotalMemUsed = 0;
u_int Asura_VideoToTexture::s_uTotalVidMemUsed = 0;

////////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture::Asura_VideoToTexture
//
////////////////////////////////////////////////////////////////////////////////

Asura_VideoToTexture::Asura_VideoToTexture()
{
	// if this is the first object
	if (s_uRefCount == 0)
	{
		Asura_VideoToTexture_System::Initialise();
	}

	m_iTextureRTIndex = ASURA_TEXTURE_NONE;
	m_pPlatformData = NULL;
	m_eFlags = ASURA_FMV_FLAG_DEFAULT;
	m_ePlayerState = PLAYER_STATE_UNINITIALISED;

	// hard-coded for now
	m_pfnRenderSubtitleCB = Asura_FMVSubtitles::RenderSubtitleByTime;

	Platform_Initialise();

	// add video to storage
	Asura_VideoToTexture_System::AddVideo(this);

	s_uTotalMemUsed = 0;
	s_uTotalVidMemUsed = 0;

	m_iSourceVoiceHandle = ASURA_INVALID_HANDLE;
	m_xSpeakerPropertyList.Clear();
	m_xSubmixVoices.Clear();
	m_bSubmixVoicesCreated = false;
	m_bUseGameTimeScale = true;
	m_fPlayTimeScale = 1.0f;
	m_bAutoPlay = false;

	m_xVideoInfo.m_uWidth = 0;
	m_xVideoInfo.m_uHeight = 0;
	m_xVideoInfo.m_fDuration = 0.0f;

	s_uRefCount++;
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture::~Asura_VideoToTexture
//
////////////////////////////////////////////////////////////////////////////////

Asura_VideoToTexture::~Asura_VideoToTexture()
{
	// remove video from storage
	Asura_VideoToTexture_System::RemoveVideo(this);

	// Destroy source voice
	if (m_iSourceVoiceHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Audio_System::DestroyVoice(m_iSourceVoiceHandle); 
		m_iSourceVoiceHandle = ASURA_INVALID_HANDLE;
	}

	// Destroy all submix voices

	for (Asura_LinkList_ConstIt< Asura_Handle > xIt( m_xSubmixVoices ); !xIt.Done(); xIt.Next())
	{
		Asura_Handle iSubHan = *xIt.GetCurrent();
		if (iSubHan == ASURA_INVALID_HANDLE)
		{
			Asura_Assert(iSubHan != ASURA_INVALID_HANDLE, "Invalid submix voice");
			continue;
		}

		Asura_Audio_System::DestroyVoice(iSubHan); 
	}

	m_xSubmixVoices.Clear();
	m_xSpeakerPropertyList.Clear();

	// Destroy voices above, *before* freeing voice buffers
	Platform_Shutdown();

	// is this our last object?
	if (s_uRefCount == 1)
	{
		Asura_VideoToTexture_System::Shutdown();
	}

	s_uRefCount--;
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture::Open
//
////////////////////////////////////////////////////////////////////////////////

bool Asura_VideoToTexture::Open(const char* szFilename, const int iTextureRTIndex, u_int uSoundTrackIndex)
{
	Asura_Assert(iTextureRTIndex != ASURA_TEXTURE_INVALID, "Asura_VideoToTexture: Invalid render target");
	if (iTextureRTIndex == ASURA_TEXTURE_INVALID)
	{
		return false;
	}

	m_iTextureRTIndex = iTextureRTIndex;

	return Platform_Open(szFilename, iTextureRTIndex, uSoundTrackIndex);
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture::Play
//
////////////////////////////////////////////////////////////////////////////////

bool Asura_VideoToTexture::Play()
{
	bool bRet = true;

	// can we play now?
	switch (GetPlayerState())
	{
		case PLAYER_STATE_UNINITIALISED:
		case PLAYER_STATE_INITIALISED:
		{
			Asura_Assert(false, "Play called before Open()!");
			bRet = false;
			break;
		}
		case PLAYER_STATE_FILE_OPENING:
		{
			m_bAutoPlay = true;
			bRet = true;
			break;
		}
		case PLAYER_STATE_FILE_OPENED:
		{
			m_bAutoPlay = false;
			bRet = Platform_Play();
			break;
		}
		case PLAYER_STATE_PAUSED:
		{
			bRet = Platform_Play();
			break;
		}
		default:
		{
			break;
		}
	}

	return bRet;
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture::Pause
//
////////////////////////////////////////////////////////////////////////////////

bool Asura_VideoToTexture::Pause()
{
	// Note: Any source voices are AUTOMATICALLY paused when the GAME TIME is set to zero.
	// If you're calling this externally then we need to pause the source voice manually.

	m_fPlayTimeScale = 0.0f;
	bool bPaused = Platform_Pause();
	return bPaused;
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture::SetPlaySpeed
//
////////////////////////////////////////////////////////////////////////////////

bool Asura_VideoToTexture::SetPlaySpeed(float fSpeed)
{
	bool bRet = Platform_SetPlaySpeed(fSpeed);

	// did we correctly set the play speed?
	if (bRet)
	{
		m_fPlayTimeScale = fSpeed;
	}

	return bRet;
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture::Update
//
////////////////////////////////////////////////////////////////////////////////

void Asura_VideoToTexture::Update()
{
	// we need to start playing
	if (m_bAutoPlay)
	{
		if (GetPlayerState() == PLAYER_STATE_FILE_OPENED)
		{
			m_bAutoPlay = false;
			Platform_Play();
		}
	}

	// have we got a source voice yet?
	if (!m_bSubmixVoicesCreated && (m_iSourceVoiceHandle != ASURA_INVALID_HANDLE))
	{
		CreateSubmixVoices();
		m_bSubmixVoicesCreated = true;
	}

	float fPlayTimeScale = m_fPlayTimeScale;

	// override the play speed with the game time
	if (m_bUseGameTimeScale)
	{
		fPlayTimeScale = Asura_Timers::GetGameTimeScale();
	}

	if (fPlayTimeScale != m_fPlayTimeScale)
	{
		// pause if we're not already
		if ((fPlayTimeScale == 0.0f) && (GetPlayerState() != PLAYER_STATE_PAUSED))
		{
			Pause();
		}
		// we should be playing now
		else if ((fPlayTimeScale != 0.0f) && (GetPlayerState() == PLAYER_STATE_PAUSED))
		{
			Play();
		}
		else
		{
			SetPlaySpeed(fPlayTimeScale);
		}

		m_fPlayTimeScale = fPlayTimeScale;
	}

	Platform_Update();
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture::Render
//
////////////////////////////////////////////////////////////////////////////////

void Asura_VideoToTexture::Render()
{
	Platform_Render();
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture::CreateSpeaker
//
////////////////////////////////////////////////////////////////////////////////

void Asura_VideoToTexture::CreateSpeaker(SpeakerProperty& xSpeakerProperty)
{
	m_xSpeakerPropertyList.Push_Back(xSpeakerProperty);
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture::CreateSubmixVoices
//
////////////////////////////////////////////////////////////////////////////////

bool Asura_VideoToTexture::CreateSubmixVoices(void)
{
	// we don't need to create any submixers
	if (m_xSpeakerPropertyList.Size() == 0)
	{
		return true;
	}

	if (m_iSourceVoiceHandle == ASURA_INVALID_HANDLE)
	{
		Asura_Assert(m_iSourceVoiceHandle != ASURA_INVALID_HANDLE, "Invalid source voice");
		return false;
	}

	Asura_Handle* const piVoiceHandles = new Asura_Handle [ m_xSpeakerPropertyList.Size() ];
	if( !piVoiceHandles ) return false;

	Asura_LinkList_ConstIt< SpeakerProperty > xIt( m_xSpeakerPropertyList );
	for (u_int uSubVoice = 0; !xIt.Done(); xIt.Next(), uSubVoice++ )
	{
		SpeakerProperty xProp = *xIt.GetCurrent();
		Asura_Handle iThisSubmix = Asura_Audio_System::CreateSubmixVoice( true, false);
		Asura_Assert(iThisSubmix != ASURA_INVALID_HANDLE, "Can't create submix voice");

		Asura_Audio_Voice* const pxVoice = Asura_Audio_System::GetVoice(iThisSubmix);
		Asura_Assert(pxVoice, "Can't find submix voice");
		pxVoice->SetSpeakerMappingType( ASURA_ASMT_SPACIAL );
		pxVoice->SetHasPosition( true );
		pxVoice->SetPos(xProp.m_xPosition);
		pxVoice->SetRadii(xProp.m_fInnerRange, xProp.m_fOuterRange);

		m_xSubmixVoices.Push_Back(iThisSubmix);
		piVoiceHandles[uSubVoice] = iThisSubmix;
	}

	// route our source voice into all the submix voices
	Asura_Audio_Voice* const pxVoice = Asura_Audio_System::GetVoice(m_iSourceVoiceHandle);
	pxVoice->SetSubmixOutputs(piVoiceHandles, m_xSubmixVoices.Size());

	delete[] piVoiceHandles;

	return true;
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture::GetVideoInfo
//
////////////////////////////////////////////////////////////////////////////////

const Asura_VideoToTexture::VideoInfo* Asura_VideoToTexture::GetVideoInfo()
{
	switch (GetPlayerState())
	{
		case PLAYER_STATE_FILE_OPENED:
		case PLAYER_STATE_PLAYING:
		case PLAYER_STATE_PAUSED:
		case PLAYER_STATE_COMPLETE:
		{
			return &m_xVideoInfo;
			break;
		}
		default:
		{
			return NULL;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_VideoToTexture::RenderDebugInfo
//
////////////////////////////////////////////////////////////////////////////////

void Asura_VideoToTexture::RenderDebugInfo()
{
	#ifdef ASURA_USE_PRIMITIVE_RENDER
	switch (GetPlayerState())
	{
		case PLAYER_STATE_FILE_OPENED:
		case PLAYER_STATE_PLAYING:
		case PLAYER_STATE_PAUSED:
		case PLAYER_STATE_COMPLETE:
		{
			Asura_Primitive_Render::DrawConsoleText("-----------------------------------------------");
			Asura_Primitive_Render::DrawConsoleText("Filename     : %s", m_xFileName.GetString());
			const u_int uOneMeg = 1024 * 1024;

			if (s_uTotalMemUsed >= uOneMeg)
			{
				const float fMeg = static_cast<float>(s_uTotalMemUsed) / static_cast<float> (uOneMeg);
				Asura_Primitive_Render::DrawConsoleText("Sys Mem Used : %.2f MB", fMeg);
			}
			else
			{
				const float fKilo = static_cast<float>(s_uTotalMemUsed) / 1024.0f;
				Asura_Primitive_Render::DrawConsoleText("Sys Mem Used : %.2f KB", fKilo);
			}

			const VideoInfo* pxVidInfo = GetVideoInfo();
			if (pxVidInfo)
			{
				if (pxVidInfo->m_fDuration < 60.0f)
				{
					Asura_Primitive_Render::DrawConsoleText("VIDEO        : %dx%d, %.2f secs", pxVidInfo->m_uWidth, pxVidInfo->m_uHeight, pxVidInfo->m_fDuration);
				}
				else
				{
					const u_int uMins = static_cast<u_int>( pxVidInfo->m_fDuration / 60.0f);
					const u_int uSecs = static_cast<u_int>( pxVidInfo->m_fDuration ) % 60;
					Asura_Primitive_Render::DrawConsoleText("VIDEO        : %dx%d, %dm %ds", pxVidInfo->m_uWidth, pxVidInfo->m_uHeight, uMins, uSecs);
				}
			}

			break;
		}
		default:
		{
			break;
		}
	}
	#endif
}


#endif // ASURA_USE_VIDEO_TO_TEXTURE_SYSTEM
