// Asura_Xbox360_XMV.cpp
// TY 17/10/07
// Copyright (C) 2001 Rebellion, All Rights Reserved.

//main asura header
#include "Core/Asura.h"

//my header
#include "FMV/Asura_Xbox360_XMV.h"

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

#ifndef ASURA_SUPPORT_DYNAMIC_MUSIC_SYSTEM
#include "Sound/Asura_Music.h"
#endif

#if ASURA_USE_XMV2
#include "Sound/Asura_XAudio2.h"
#endif

//Asura Xbox360 specific
#include "Core/Memory/Asura_Mem.h"
#include "Core/Asura_XBox360_Notifications.h"
#include "Input/Asura_PlatformInput.h"
#include "Rendering/Asura_Xbox360_Graphics.h"

// External
#if ASURA_USE_XMV2
#include <Xmedia2.h>
#else
#include <Xmedia.h>
#endif

#include <Xmp.h>
#include <xtl.h>

const u_int uASURA_XBOX360_FMV_SIZE_APPROXIMATION_LEEWAY = 5;

#if ASURA_USE_XMV2
IXMedia2XmvPlayer*	Asura_Xbox360_XMV::s_pxXMVPlayer	= 0;
#else
IXMediaXmvPlayer*	Asura_Xbox360_XMV::s_pxXMVPlayer	= 0;
#endif

bool				Asura_Xbox360_XMV::s_bAllowSkip		= true;
bool				Asura_Xbox360_XMV::s_bSkipping		= false;

Asura_Xbox360_XMV::Asura_Xbox360_XMV()
{
	
}

Asura_Xbox360_XMV::~Asura_Xbox360_XMV()
{
	if(s_pxXMVPlayer)
	{
		s_pxXMVPlayer->Release();
	}
}

void Asura_Xbox360_XMV::SetRectangle(const int iTop, const int iLeft, const int iBottom, const int iRight)
{
	if(s_pxXMVPlayer)
	{
		RECT xRect;
		xRect.top		= iTop;
		xRect.left		= iLeft;
		xRect.bottom    = iBottom;
		xRect.right		= iRight;
		HRESULT hr = s_pxXMVPlayer->SetRectangle(&xRect);
		(void)hr;
		Asura_Assert(SUCCEEDED(hr),"Failed to set rectangle for XMV playback");
	}	
	else
	{
		Asura_Assert(false, "Attempting to set XMV playback rectangle but no file set");
	}
}

void Asura_Xbox360_XMV::AllowSkip (bool bAllow)
{
	s_bAllowSkip = bAllow;
}

void Asura_Xbox360_XMV::VidCallback (void* pVid)
{	
#if ASURA_USE_XMV2
	IXMedia2XmvPlayer* xPlayer = ( IXMedia2XmvPlayer* )pVid;
#else
	IXMediaXmvPlayer* xPlayer = ( IXMediaXmvPlayer* )pVid;
#endif
	if(s_bSkipping)
	{
		//We need to flush the input here so that whatever happens after the XMV doesn't get unwanted input
		Asura_UserInput::Update();
		if( !Asura_UserInput::HasAnyKeyBeenPressed() )
		{
			xPlayer->Stop( XMEDIA_STOP_IMMEDIATE );
		}
	}
	else if(s_bAllowSkip)
	{
		Asura_UserInput::Update();
		// Watch for user input.
		if(		Asura_UserInput::DebouncedKeyPressed( ASURA_XBOX360_KEYID_PAD1_START )	
			||	Asura_UserInput::DebouncedKeyPressed( ASURA_XBOX360_KEYID_PAD1_A		)
			||	Asura_UserInput::DebouncedKeyPressed( ASURA_XBOX360_KEYID_PAD2_START )	
			||	Asura_UserInput::DebouncedKeyPressed( ASURA_XBOX360_KEYID_PAD2_A		)
			||	Asura_UserInput::DebouncedKeyPressed( ASURA_XBOX360_KEYID_PAD3_START )	
			||	Asura_UserInput::DebouncedKeyPressed( ASURA_XBOX360_KEYID_PAD3_A		)
			||	Asura_UserInput::DebouncedKeyPressed( ASURA_XBOX360_KEYID_PAD4_START )	
			||	Asura_UserInput::DebouncedKeyPressed( ASURA_XBOX360_KEYID_PAD4_A		) )
		{
			s_bSkipping = true;
		}
	}
	#ifndef ASURA_SUPPORT_DYNAMIC_MUSIC_SYSTEM
	Asura_Music_System::Update(); // to ensure music gets stopped.
	#endif
	Asura_XBox360_Notifications::Update();	// notifications are important too
}

void Asura_Xbox360_XMV::Play(const char* szFilename, int iAudioStream /*=0*/, bool bLoop /*= false*/)
{
	if(s_pxXMVPlayer)
	{
		Asura_Assert(false, "Attempting to set a file for XMV playback, but we are already in playback");
		return;
	}

	s_pxXMVPlayer = 0;
	HRESULT hr;

	XVIDEO_MODE xMode;
	XGetVideoMode( &xMode );
	//We need to reset the D3D device because XMV expects an auto backbuffer
	D3DPRESENT_PARAMETERS xParams = 
	{
		xMode.dwDisplayWidth,			// BackBufferWidth;
		xMode.dwDisplayHeight,			// BackBufferHeight;
		D3DFMT_A8R8G8B8,				// BackBufferFormat;
		1,								// BackBufferCount;
		D3DMULTISAMPLE_NONE,			// MultiSampleType;
		0,								// MultiSampleQuality;
		D3DSWAPEFFECT_DISCARD,			// SwapEffect;
		NULL,							// hDeviceWindow;
		FALSE,							// Windowed;
		FALSE,							// EnableAutoDepthStencil;
		D3DFMT_UNKNOWN,					// AutoDepthStencilFormat;
		0,								// Flags;
		0,								// FullScreen_RefreshRateInHz;
		D3DPRESENT_INTERVAL_ONE,		// FullScreen_PresentationInterval;

		// 360 extensions
		FALSE,							// DisableAutoBackBuffer;
		FALSE,							// DisableAutoFrontBuffer;
		D3DFMT_LE_X8R8G8B8,				// FrontBufferFormat;
		D3DCOLORSPACE_RGB,				// FrontBufferColorSpace;
		{0},							// RingBufferParameters;
		{0},							// VideoScalerParameters;
	};

	Asura_Xbox360_Graphics::AcquireThreadOwnership();

	Direct3D_Device* pxDevice = Asura_Xbox360_Graphics::GetDevice();
	Asura_Assert(pxDevice,"Failed to get Direct3D device for XMV playback");
	bool bFailedToCreate = false;
	if(pxDevice)
	{
		hr = pxDevice->Reset(&xParams);
		if(SUCCEEDED(hr))
		{
			XMEDIA_XMV_CREATE_PARAMETERS xParams;
			ZeroMemory(&xParams, sizeof (xParams));

			xParams.dwFlags = bLoop ? XMEDIA_CREATE_FOR_LOOP : 0;
			xParams.createType = XMEDIA_CREATE_FROM_FILE;
			xParams.createFromFile.szFileName = szFilename;
			xParams.dwAudioStreamId = iAudioStream;

#if ASURA_USE_XMV2
			IXAudio2* pxAudioEngine = Asura_XAudio2::GetEngine();
			Asura_Assert(pxAudioEngine, "Asura_XAudio2::GetEngine() not inited");
			hr = XMedia2CreateXmvPlayer(pxDevice, pxAudioEngine, &xParams, &s_pxXMVPlayer );	
#else
			hr = XMediaCreateXmvPlayer(pxDevice, &xParams, &s_pxXMVPlayer );	
#endif
			bFailedToCreate = !SUCCEEDED(hr);
			if(SUCCEEDED(hr))
			{	
				XMEDIA_VIDEO_DESCRIPTOR xDescriptor;
				hr = s_pxXMVPlayer->GetVideoDescriptor(&xDescriptor);
				if(SUCCEEDED(hr))
				{	
					s_pxXMVPlayer->SetCallback(XMEDIA_NOTIFY_END_OF_FRAME,VidCallback,s_pxXMVPlayer);
					
					RECT xRect;
					//we expect some round numbers here
					// (NY - 06.02.2008) this assumes the source video is 16:9, which I'm informed it always will be for 360
					// if that ever changes, this code will need to be generalised
					if(!xMode.fIsWideScreen)
					{
						//rescale for 4:3
						u_int uNewHeight = xMode.dwDisplayWidth / 16 * 9;
						xRect.top = (xMode.dwDisplayHeight - uNewHeight) / 2;
						xRect.bottom = (xRect.top + uNewHeight);
						xRect.left = 0;
						xRect.right = xMode.dwDisplayWidth;
						s_pxXMVPlayer->SetRectangle(&xRect);
					}
					
					DWORD uFlags = bLoop ? XMEDIA_PLAY_LOOP : 0;

					s_bSkipping = false;
					XMPOverrideBackgroundMusic();
					s_pxXMVPlayer->Play(uFlags,0);
					XMPRestoreBackgroundMusic();
					s_pxXMVPlayer->Release();
					s_pxXMVPlayer = 0;
				}
			}
		}
	}

	ResetD3DDevice();
	pxDevice->SetRenderState(D3DRS_VIEWPORTENABLE, TRUE);

	Asura_Xbox360_Graphics::ReleaseThreadOwnership();

	// The front buffer created in the previous reset still seems to be assigned to the
	// device atm, even though the memory has been released (we were getting corruption on screen)
	// make sure we do a present to get the new frontbuffer active.
	Asura_Xbox360_Graphics::BeginFrame();

	// If we're going to Swap(), make sure EDRAM is set to valid values. It's not necessarily going
	// to be nice and blank after the FMV/device reset
	Asura_Xbox360_Graphics::GetDevice()->Clear( 0, NULL, D3DCLEAR_TARGET, 0, 0.0f, 0 );

	Asura_Xbox360_Graphics::EndFrame();

	// [6/11/2008 AS] Moved the Assert after the Reset as our asserts dont like the device
	// that the XMV player uses.
	Asura_Assertf(!bFailedToCreate,("Failed to create XMV player for file %s",szFilename));
}

void Asura_Xbox360_XMV::Resume  ()
{
	if(Paused())
	{
		s_pxXMVPlayer->Resume();
	}
	else
	{
		Asura_Assert(false,"Cannot resume XMV playback, we are not paused");
	}
}

void Asura_Xbox360_XMV::Pause  ()
{
	if(Playing())
	{
		s_pxXMVPlayer->Pause();		
	}
	else
	{
		Asura_Assert(false,"Cannot pause XMV playback, we are not playing");
	}
}

void Asura_Xbox360_XMV::Stop()
{
	if(Playing() || Paused())
	{
		s_pxXMVPlayer->Stop(XMEDIA_STOP_IMMEDIATE);
		s_pxXMVPlayer->Release();
		s_pxXMVPlayer = 0;
		ResetD3DDevice();
	}
	else
	{
		Asura_Assert(false,"Cannot stop XMV playback, we are not playing or paused");
	}
}

bool Asura_Xbox360_XMV::Playing	()
{
	if(s_pxXMVPlayer)
	{
		XMEDIA_PLAYBACK_STATUS xStatus;		
		if(SUCCEEDED(s_pxXMVPlayer->GetStatus(&xStatus)))
		{
			return (xStatus.Status == XMEDIA_PLAYER_PLAYING);
		}
	}
	return false;
}

bool Asura_Xbox360_XMV::Paused	()
{
	if(s_pxXMVPlayer)
	{
		XMEDIA_PLAYBACK_STATUS xStatus;		
		if(SUCCEEDED(s_pxXMVPlayer->GetStatus(&xStatus)))
		{
			return ((xStatus.Status == XMEDIA_PLAYER_PAUSING) || (xStatus.Status == XMEDIA_PLAYER_PAUSED));
		}
	}
	return false;
}

void Asura_Xbox360_XMV::ResetD3DDevice ()
{	
	Direct3D_Device* pxDevice = Asura_Xbox360_Graphics::GetDevice();
		
	D3DPRESENT_PARAMETERS xParams;
	Asura_Mem::Set( &xParams, 0, sizeof( xParams ) );
	Asura_Xbox360_Graphics::GetDefaultParams(&xParams);
	pxDevice->Reset(&xParams);
}


bool Asura_Xbox360_XMV::IsApproximately_16_9 (u_int uWidth, u_int uHeight)
{
	u_int uShouldBeHeight = static_cast<u_int>(static_cast<float>(uWidth) / 16.0f * 9.0f);
	return ( ( uShouldBeHeight > ( uHeight - uASURA_XBOX360_FMV_SIZE_APPROXIMATION_LEEWAY ) ) && 
			 ( uShouldBeHeight < ( uHeight + uASURA_XBOX360_FMV_SIZE_APPROXIMATION_LEEWAY ) ) );
}


bool Asura_Xbox360_XMV::IsApproximately_4_3	 (u_int uWidth, u_int uHeight)
{
	u_int uShouldBeHeight = static_cast<u_int>(static_cast<float>(uWidth) / 4.0f * 3.0f);
	return ( ( uShouldBeHeight > ( uHeight - uASURA_XBOX360_FMV_SIZE_APPROXIMATION_LEEWAY ) ) && 
			 ( uShouldBeHeight < ( uHeight + uASURA_XBOX360_FMV_SIZE_APPROXIMATION_LEEWAY ) ) );
}

//eof
