//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================

#include "filesystem.h" 
#include "tier1/strtools.h"
#include "tier1/utllinkedlist.h"
#include "tier1/KeyValues.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/MaterialSystemUtil.h"
#include "materialsystem/itexture.h"
#include "vgui/ILocalize.h"
#include "vtf/vtf.h"
#include "pixelwriter.h"
#include "tier3/tier3.h"
#include "platform.h"

#include "videoservices.h"
#include "video_macros.h"

#include "tier0/memdbgon.h"

#if defined( WIN32 )
	#include <windows.h>
#elif defined( OSX )
	#include <Carbon/Carbon.h>
#endif

#if defined( USE_SDL )
	#include "SDL.h"
	#include "appframework/ilaunchermgr.h"
#endif

//-----------------------------------------------------------------------------
// Platform specific video system controls & definitions
//-----------------------------------------------------------------------------

enum EPlatform_t
{
	PLATFORM_NONE		= 0,
	PLATFORM_WIN32		= 0x01,
	PLATFORM_OSX		= 0x02,
	PLATFORM_XBOX_360	= 0x04,
	PLATFORM_PS3		= 0x08,
	PLATFORM_LINUX		= 0x10
};

DEFINE_ENUM_BITWISE_OPERATORS( EPlatform_t );

#if defined( IS_WINDOWS_PC )
	const EPlatform_t	thisPlatform = PLATFORM_WIN32;
#elif defined( OSX )
	const EPlatform_t	thisPlatform = PLATFORM_OSX;
#elif defined( _X360 )
	const EPlatform_t	thisPlatform = PLATFORM_XBOX_360;
#elif defined( _PS3 )
	const EPlatform_t	thisPlatform = PLATFORM_PS3;
#elif defined ( _LINUX ) || defined(PLATFORM_BSD)
	const EPlatform_t	thisPlatform = PLATFORM_LINUX;
#else
  #error "UNABLE TO DETERMINE PLATFORM"
#endif


#if defined( OSX ) || defined( LINUX ) || defined(PLATFORM_BSD)
ILauncherMgr *g_pLauncherMgr = NULL;
#endif


struct VideoSystemInfo_t
{
	VideoSystem_t	m_SystemID;
	EPlatform_t		m_Platforms;
	const char		*m_pModuleName;
	const char		*m_pInterfaceName;
};

static VideoSystemInfo_t s_VideoAppSystems[] = 
{
	{ VideoSystem::QUICKTIME,	PLATFORM_WIN32 | PLATFORM_OSX,							"video_quicktime",  VIDEO_SUBSYSTEM_INTERFACE_VERSION },
	{ VideoSystem::BINK,		PLATFORM_WIN32 | PLATFORM_OSX | PLATFORM_XBOX_360 | PLATFORM_LINUX,		"video_bink",	    VIDEO_SUBSYSTEM_INTERFACE_VERSION },
	//{ VideoSystem::AVI,			PLATFORM_WIN32,											"avi",				VIDEO_SUBSYSTEM_INTERFACE_VERSION },
	//{ VideoSystem::WMV,			PLATFORM_WIN32,											"wmv",				VIDEO_SUBSYSTEM_INTERFACE_VERSION },
	{ VideoSystem::WEBM,	PLATFORM_LINUX,							"video_webm",  VIDEO_SUBSYSTEM_INTERFACE_VERSION },
	
	{ VideoSystem::NONE,		PLATFORM_NONE, nullptr, nullptr }			// Required to terminate the list
};



//-----------------------------------------------------------------------------
// Setup Singleton for accessing Valve Video Services
//-----------------------------------------------------------------------------
static CValveVideoServices g_VALVeVIDEO;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CValveVideoServices, IVideoServices, VIDEO_SERVICES_INTERFACE_VERSION, g_VALVeVIDEO );


static CVideoCommonServices g_VALVEVIDEOCommon;


//-----------------------------------------------------------------------------
// Valve Video Services implementation
//-----------------------------------------------------------------------------
CValveVideoServices::CValveVideoServices() :
	m_nInstalledSystems( 0 ),
	m_bInitialized( false ),
	m_nMaterialCount( 0 )
{
	for ( int i = 0; i < VideoSystem::VIDEO_SYSTEM_COUNT; i++ )
	{
		m_VideoSystemModule[i]	= nullptr;
		m_VideoSystems[i]		= nullptr;
		m_VideoSystemType[i]	= VideoSystem::NONE;
		m_VideoSystemFeatures[i] = VideoSystemFeature::NO_FEATURES;
	}

}


CValveVideoServices::~CValveVideoServices()
{
	DisconnectVideoLibraries( );
}
	

bool CValveVideoServices::Connect( CreateInterfaceFn factory )
{
	if ( !BaseClass::Connect( factory ) )
	{
		return false;
	}

	if ( g_pFullFileSystem == nullptr || materials == nullptr )
	{
		Msg( "Valve Video Services unable to connect due to missing dependent system\n" );
		return false;
	}

#if defined( USE_SDL )
	g_pLauncherMgr = (ILauncherMgr *)factory( SDLMGR_INTERFACE_VERSION, NULL );
#endif
	
	if ( !ConnectVideoLibraries( factory ) )
	{
		return false;
	}
	
	return ( true );
}


void CValveVideoServices::Disconnect()
{
	DisconnectVideoLibraries();
}


void* CValveVideoServices::QueryInterface( const char *pInterfaceName )
{
	if ( Q_strncmp(	pInterfaceName, VIDEO_SERVICES_INTERFACE_VERSION, Q_strlen( VIDEO_SERVICES_INTERFACE_VERSION ) + 1) == 0 )
	{
		return (IVideoServices*) this;
	}

	return nullptr;
}


bool CValveVideoServices::ConnectVideoLibraries( CreateInterfaceFn factory )
{
	// Don't connect twice..
	AssertExitF( m_bInitialized == false );

	int n = 0;
	
	while ( IS_NOT_EMPTY( s_VideoAppSystems[n].m_pModuleName ) && s_VideoAppSystems[n].m_SystemID != VideoSystem::NONE )
	{
		if (BITFLAGS_SET( s_VideoAppSystems[n].m_Platforms, thisPlatform ) )
		{
			bool success = false;
			CSysModule *pModule = Sys_LoadModule(s_VideoAppSystems[n].m_pModuleName );
			if( pModule != nullptr )
			{
				CreateInterfaceFn fn = Sys_GetFactory( pModule );
				if ( fn != nullptr )
				{
				
					IVideoSubSystem *pVideoSystem = (IVideoSubSystem*) fn( s_VideoAppSystems[n].m_pInterfaceName, NULL );
					if ( pVideoSystem != nullptr && pVideoSystem->Connect( factory ) )
					{
						if ( pVideoSystem->InitializeVideoSystem( &g_VALVEVIDEOCommon ) )
						{
							int slotNum = (int) pVideoSystem->GetSystemID();

							if ( IS_IN_RANGECOUNT( slotNum, VideoSystem::VIDEO_SYSTEM_FIRST, VideoSystem::VIDEO_SYSTEM_COUNT ) )
							{
								Assert( m_VideoSystemModule[slotNum] == nullptr );
								m_VideoSystemModule[slotNum] = pModule;
								m_VideoSystems[slotNum] = pVideoSystem;
								
								m_nInstalledSystems++;
								success = true;
							}
						}
					}
				}
				
				if ( success == false )
				{
					
					Msg( "Error occurred while attempting to load and initialize Video Subsystem\n Video Subsystem module '%s'\n Video Subsystem Interface  '%s'", s_VideoAppSystems[n].m_pModuleName, s_VideoAppSystems[n].m_pInterfaceName );
					Sys_UnloadModule( pModule );
				}
			}			
		}
		
		n++;			
	}			

	// now we query each video system for its capabilities, and supported file extensions
	for ( int i = VideoSystem::VIDEO_SYSTEM_FIRST; i < VideoSystem::VIDEO_SYSTEM_COUNT; i++ )
	{
		IVideoSubSystem *pSubSystem = m_VideoSystems[i];
		if ( pSubSystem != nullptr )
		{
			m_VideoSystemType[i]		= pSubSystem->GetSystemID();
			m_VideoSystemFeatures[i]	= pSubSystem->GetSupportedFeatures();
			
			// get every file extension it handles, and the info about it
			int eCount = pSubSystem->GetSupportedFileExtensionCount();
			Assert( eCount > 0 );
			
			for ( int n = 0; n < eCount; n++ )
			{
				VideoFileExtensionInfo_t	extInfoRec;
				
				extInfoRec.m_FileExtension = pSubSystem->GetSupportedFileExtension( n );
				extInfoRec.m_VideoSubSystem = pSubSystem->GetSystemID();
				extInfoRec.m_VideoFeatures = pSubSystem->GetSupportedFileExtensionFeatures( n );
				
				AssertPtr( extInfoRec.m_FileExtension );
				
				m_ExtInfo.AddToTail( extInfoRec );
			}
		}
	}

	m_bInitialized = true;
	
	return true;
}


bool CValveVideoServices::DisconnectVideoLibraries()
{
	if ( !m_bInitialized )
	{
		return false;
	}

	// free up any objects/resources still out there
	DestroyAllVideoInterfaces();

	for ( int i = 0; i < VideoSystem::VIDEO_SYSTEM_COUNT; i++ )
	{
		if ( m_VideoSystems[i] != nullptr )
		{
			m_VideoSystems[i]->ShutdownVideoSystem();
			m_VideoSystems[i]->Disconnect();
			m_VideoSystems[i] = nullptr;
		}
		
		if ( m_VideoSystemModule[i] != nullptr )
		{
			Sys_UnloadModule( m_VideoSystemModule[i] );
			m_VideoSystemModule[i] = nullptr;
		}
	
		m_VideoSystemType[i]	 = VideoSystem::NONE;
		m_VideoSystemFeatures[i] = VideoSystemFeature::NO_FEATURES;
	}

	m_bInitialized = false;
	
	return true;
}


int CValveVideoServices::DestroyAllVideoInterfaces()
{
	int n = m_RecorderList.Count() + m_MaterialList.Count();

	for ( int i = m_RecorderList.Count() -1; i >= 0; i-- )
	{
		DestroyVideoRecorder( (IVideoRecorder*) m_RecorderList[i].m_pObject );
	}

	for ( int i = m_MaterialList.Count() -1; i >= 0; i-- )
	{
		DestroyVideoMaterial( (IVideoMaterial*) m_MaterialList[i].m_pObject );
	}
	
	return n;
}


InitReturnVal_t CValveVideoServices::Init()
{
	InitReturnVal_t nRetVal = BaseClass::Init();
	if ( nRetVal != INIT_OK )
	{
		return nRetVal;
	}

	// Initialize all loaded subsystems
	for ( int n = VideoSystem::VIDEO_SYSTEM_FIRST; n < VideoSystem::VIDEO_SYSTEM_COUNT; n++ )
	{
		if ( m_VideoSystems[n] != nullptr )
		{
			nRetVal = m_VideoSystems[n]->Init();
			if ( nRetVal != INIT_OK )
			{
				return nRetVal;
			}
		}
	}

	return INIT_OK;
}


void CValveVideoServices::Shutdown()
{
	DestroyAllVideoInterfaces();

	// Shutdown all loaded subsystems
	for ( int n = VideoSystem::VIDEO_SYSTEM_FIRST; n < VideoSystem::VIDEO_SYSTEM_COUNT; n++ )
	{
		if ( m_VideoSystems[n] != nullptr )
		{
			m_VideoSystems[n]->Shutdown();
		}
	}
	
	BaseClass::Shutdown();
}
		
	
// ===========================================================================	
// Inherited from IVideoServices
// ===========================================================================	
	
// Query the available video systems
int CValveVideoServices::GetAvailableVideoSystemCount()
{
	return m_nInstalledSystems;
}


// returns the enumerated video system, *IF* it is installed and working
VideoSystem_t CValveVideoServices::GetAvailableVideoSystem( int n )
{
	if ( n< 0 || n >= m_nInstalledSystems ) 
	{
		return VideoSystem::NONE;
	}
	
	for ( int i = VideoSystem::VIDEO_SYSTEM_FIRST, c = 0; i < VideoSystem::VIDEO_SYSTEM_COUNT; i++ )
	{
		if ( m_VideoSystems[i] != nullptr )
		{
			if ( c == n ) 
			{
				return m_VideoSystemType[i];
			}
			c++;
		}
	}
	
	return VideoSystem::NONE;
}


// ===========================================================================	
// returns the index for the video system...
// ... provided that system is installed and available to do something
// ===========================================================================	
int CValveVideoServices::GetIndexForSystem( VideoSystem_t n )
{
	if ( n >= VideoSystem::VIDEO_SYSTEM_FIRST && n < VideoSystem::VIDEO_SYSTEM_COUNT && m_nInstalledSystems > 0 )
	{
		int i = (int) n;
		if ( m_VideoSystems[i] != nullptr && m_VideoSystemFeatures[i] != VideoSystemFeature::NO_FEATURES )
		{
			return i;
		}
	}
	
	return SYSTEM_NOT_FOUND;
}


VideoSystem_t CValveVideoServices::GetSystemForIndex( int n )
{
	if ( n >= VideoSystem::VIDEO_SYSTEM_FIRST && n < VideoSystem::VIDEO_SYSTEM_COUNT && m_nInstalledSystems > 0 )
	{
		if ( m_VideoSystems[n] != nullptr && m_VideoSystemFeatures[n] != VideoSystemFeature::NO_FEATURES )
		{
			return (VideoSystem_t) n;
		}
	}

	return VideoSystem::NONE;
}


// ===========================================================================	
// video system query functions
// ===========================================================================	
bool CValveVideoServices::IsVideoSystemAvailable( VideoSystem_t videoSystem )
{
	int n = GetIndexForSystem( videoSystem ); 
	return ( n != SYSTEM_NOT_FOUND ) ? true : false;
}


VideoSystemStatus_t CValveVideoServices::GetVideoSystemStatus( VideoSystem_t videoSystem )
{
	int n = GetIndexForSystem( videoSystem ); 
	return ( n!= SYSTEM_NOT_FOUND ) ? m_VideoSystems[n]->GetSystemStatus() : VideoSystemStatus::NOT_INSTALLED;
}


VideoSystemFeature_t CValveVideoServices::GetVideoSystemFeatures( VideoSystem_t videoSystem )
{
	int n = GetIndexForSystem( videoSystem ); 
	return ( n!= SYSTEM_NOT_FOUND ) ? m_VideoSystemFeatures[n] : VideoSystemFeature::NO_FEATURES;

}


const char *CValveVideoServices::GetVideoSystemName( VideoSystem_t videoSystem )
{
	int n = GetIndexForSystem( videoSystem ); 
	return ( n!= SYSTEM_NOT_FOUND ) ? m_VideoSystems[n]->GetVideoSystemName() : nullptr;
}


VideoSystem_t CValveVideoServices::FindNextSystemWithFeature( VideoSystemFeature_t features, VideoSystem_t startAfter )
{
	if ( ( features & VideoSystemFeature::ALL_VALID_FEATURES ) == 0 )
	{
		return VideoSystem::NONE;
	}

	int start = VideoSystem::VIDEO_SYSTEM_FIRST;
	if ( startAfter != VideoSystem::NONE && IS_IN_RANGECOUNT( startAfter, VideoSystem::VIDEO_SYSTEM_FIRST, VideoSystem::VIDEO_SYSTEM_COUNT ) )
	{
		start = (int) startAfter;
	}

	for ( int i = start; i < VideoSystem::VIDEO_SYSTEM_COUNT; i++ )
	{
		if ( m_VideoSystems[i] != nullptr && BITFLAGS_SET( m_VideoSystemFeatures[i], features )	)
		{
			return (VideoSystem_t) i;
		}
	}
	
	return VideoSystem::NONE;
}


// ===========================================================================	
// video services status functions
// ===========================================================================	
VideoResult_t CValveVideoServices::GetLastResult()
{
	return m_LastResult;
}


VideoResult_t CValveVideoServices::SetResult( VideoResult_t resultCode )
{
	m_LastResult = resultCode;
	return resultCode;
}
	
		
// ===========================================================================	
// deal with video file extensions and video system mappings
// ===========================================================================	
int CValveVideoServices::GetSupportedFileExtensionCount( VideoSystem_t videoSystem )
{
	int n = GetIndexForSystem( videoSystem ); 
	
	return ( n == SYSTEM_NOT_FOUND ) ? 0 : m_VideoSystems[n]->GetSupportedFileExtensionCount();
}


const char *CValveVideoServices::GetSupportedFileExtension( VideoSystem_t videoSystem, int extNum )
{
	int n = GetIndexForSystem( videoSystem ); 

	int c = ( n == SYSTEM_NOT_FOUND ) ? 0 : m_VideoSystems[n]->GetSupportedFileExtensionCount();;
	
	return ( extNum < 0 || extNum >= c ) ? nullptr : m_VideoSystems[n]->GetSupportedFileExtension( extNum );
	
}


VideoSystemFeature_t CValveVideoServices::GetSupportedFileExtensionFeatures( VideoSystem_t videoSystem, int extNum )
{
	int n = GetIndexForSystem( videoSystem ); 

	int c = ( n == SYSTEM_NOT_FOUND ) ? 0 : m_VideoSystems[n]->GetSupportedFileExtensionCount();
	
	return ( extNum < 0 || extNum >= c ) ? VideoSystemFeature::NO_FEATURES : m_VideoSystems[n]->GetSupportedFileExtensionFeatures( extNum );
}


VideoSystem_t CValveVideoServices::LocateVideoSystemForPlayingFile( const char *pFileName, VideoSystemFeature_t playMode )
{
	SetResult( VideoResult::BAD_INPUT_PARAMETERS );
	AssertExitV( IS_NOT_EMPTY( pFileName ), VideoSystem::NONE );
	
	VideoSystem_t theSystem = LocateSystemAndFeaturesForFileName( pFileName, nullptr, playMode );

	SetResult( VideoResult::SUCCESS );
	return theSystem;
}


// ===========================================================================	
// Given a video file name, possibly with a set extension, locate the file
//   or a suitable substitute that is playable on the current system
// ===========================================================================	
VideoResult_t CValveVideoServices::LocatePlayableVideoFile( const char *pSearchFileName, const char *pPathID, VideoSystem_t *pPlaybackSystem, char *pPlaybackFileName, int fileNameMaxLen, VideoSystemFeature_t playMode )
{
	AssertExitV( IS_NOT_EMPTY( pSearchFileName ) || pPlaybackSystem == nullptr || pPlaybackSystem == nullptr || fileNameMaxLen <= 0, SetResult( VideoResult::BAD_INPUT_PARAMETERS ) );

	VideoResult_t Status = ResolveToPlayableVideoFile( pSearchFileName, pPathID, VideoSystem::DETERMINE_FROM_FILE_EXTENSION, playMode, 
									true, pPlaybackFileName, fileNameMaxLen, pPlaybackSystem );

	return SetResult( Status );
}



// ===========================================================================	
// Create/destroy a video material
// ===========================================================================	
IVideoMaterial* CValveVideoServices::CreateVideoMaterial( const char *pMaterialName, const char *pVideoFileName, const char *pPathID, VideoPlaybackFlags_t playbackFlags, VideoSystem_t videoSystem, bool PlayAlternateIfNotAvailable )
{
	SetResult( VideoResult::BAD_INPUT_PARAMETERS );
	AssertExitV( IS_NOT_EMPTY( pVideoFileName ), nullptr );
	AssertExitV( videoSystem == VideoSystem::DETERMINE_FROM_FILE_EXTENSION || IS_IN_RANGECOUNT( videoSystem, VideoSystem::VIDEO_SYSTEM_FIRST, VideoSystem::VIDEO_SYSTEM_COUNT ), nullptr );

	// We need to resolve the filename and video system

	char ResolvedFilePath[MAX_PATH];
	VideoSystem_t  actualVideoSystem = videoSystem;

	VideoResult_t Status = ResolveToPlayableVideoFile( pVideoFileName, pPathID, videoSystem, VideoSystemFeature::PLAY_VIDEO_FILE_IN_MATERIAL, PlayAlternateIfNotAvailable,
	                                                   ResolvedFilePath, sizeof(ResolvedFilePath), &actualVideoSystem );

	SetResult( Status );
	if ( Status != VideoResult::SUCCESS )
	{
		return nullptr;
	}

	int sysIndex = GetIndexForSystem( actualVideoSystem );
	
	if ( sysIndex == SYSTEM_NOT_FOUND )
	{
		SetResult( VideoResult::SYSTEM_ERROR_OCCURED );
		return nullptr;
	}
	
	// Create the video material
	IVideoMaterial *pMaterial = m_VideoSystems[sysIndex]->CreateVideoMaterial( pMaterialName, ResolvedFilePath, playbackFlags );

	// Update our list, and return	
	if ( pMaterial != nullptr )
	{
		CActiveVideoObjectRecord_t info;
		info.m_pObject = pMaterial;
		info.m_VideoSystem = sysIndex;
		m_MaterialList.AddToTail( info );
	}

	SetResult( m_VideoSystems[sysIndex]->GetLastResult() );
	return pMaterial;
}


VideoResult_t CValveVideoServices::DestroyVideoMaterial( IVideoMaterial* pVideoMaterial )
{
	AssertPtrExitV( pVideoMaterial, SetResult( VideoResult::BAD_INPUT_PARAMETERS ) );

	for ( int i = 0; i < m_MaterialList.Count(); i++ )
	{
		if ( m_MaterialList[i].m_pObject == pVideoMaterial )
		{
			VideoResult_t Status = m_VideoSystems[ m_MaterialList[i].m_VideoSystem ]->DestroyVideoMaterial( pVideoMaterial );
			m_MaterialList.Remove( i );
			
			return SetResult( Status );
		}
	}

	return SetResult( VideoResult::RECORDER_NOT_FOUND );


	return VideoResult::SUCCESS;
}


int CValveVideoServices::GetUniqueMaterialID()
{
	m_nMaterialCount++;
	return m_nMaterialCount;
}

// ===========================================================================	
// Query availabilily of codec for encoding video
// ===========================================================================	
VideoResult_t CValveVideoServices::IsRecordCodecAvailable( VideoSystem_t videoSystem, VideoEncodeCodec_t codec )
{
	AssertExitV( codec >= VideoEncodeCodec::DEFAULT_CODEC && codec < VideoEncodeCodec::CODEC_COUNT, SetResult( VideoResult::BAD_INPUT_PARAMETERS ) );

	int n = GetIndexForSystem( videoSystem );
	
	if ( n == SYSTEM_NOT_FOUND )
	{
		return SetResult( VideoResult::SYSTEM_NOT_AVAILABLE );
	}

	return m_VideoSystems[n]->CheckCodecAvailability( codec );
}


// ===========================================================================	
// Create/destroy a video encoder		
// ===========================================================================	
IVideoRecorder*	CValveVideoServices::CreateVideoRecorder( VideoSystem_t videoSystem )
{
	int n = GetIndexForSystem( videoSystem );
	
	if ( n == SYSTEM_NOT_FOUND )
	{
		SetResult( VideoResult::SYSTEM_NOT_AVAILABLE );
		return nullptr;
	}

	if ( !BITFLAGS_SET( m_VideoSystemFeatures[n], VideoSystemFeature::ENCODE_VIDEO_TO_FILE ) )
	{
		SetResult( VideoResult::FEATURE_NOT_AVAILABLE );
		return nullptr;
	}

	IVideoRecorder *pRecorder = m_VideoSystems[n]->CreateVideoRecorder();
	
	if ( pRecorder != nullptr )
	{
		CActiveVideoObjectRecord_t info;
		info.m_pObject = pRecorder;
		info.m_VideoSystem = n;
		m_RecorderList.AddToTail( info );
	}

	SetResult( m_VideoSystems[n]->GetLastResult() );
	return pRecorder;
}


VideoResult_t CValveVideoServices::DestroyVideoRecorder( IVideoRecorder *pVideoRecorder )
{
	AssertPtrExitV( pVideoRecorder, SetResult( VideoResult::BAD_INPUT_PARAMETERS ) );

	for ( int i = 0; i < m_RecorderList.Count(); i++ )
	{
		if ( m_RecorderList[i].m_pObject == pVideoRecorder )
		{
			VideoResult_t Status = m_VideoSystems[ m_RecorderList[i].m_VideoSystem ]->DestroyVideoRecorder( pVideoRecorder );
			m_RecorderList.Remove( i );
			
			return SetResult( Status );
		}
	}

	return SetResult( VideoResult::RECORDER_NOT_FOUND );

}


// ===========================================================================	
// Plays a given video file until it completes or the user aborts
// ===========================================================================	
VideoResult_t CValveVideoServices::PlayVideoFileFullScreen( const char *pFileName, const char *pPathID, void *mainWindow, int windowWidth, int windowHeight, int desktopWidth, int desktopHeight, bool windowed, float forcedMinTime, VideoPlaybackFlags_t playbackFlags, VideoSystem_t videoSystem, bool PlayAlternateIfNotAvailable )
{
	SetResult( VideoResult::BAD_INPUT_PARAMETERS );
	AssertExitV( IS_NOT_EMPTY( pFileName ), VideoResult::BAD_INPUT_PARAMETERS );
	AssertExitV( videoSystem == VideoSystem::DETERMINE_FROM_FILE_EXTENSION || IS_IN_RANGECOUNT( videoSystem, VideoSystem::VIDEO_SYSTEM_FIRST, VideoSystem::VIDEO_SYSTEM_COUNT ), VideoResult::BAD_INPUT_PARAMETERS );

	char ResolvedFilePath[MAX_PATH];
	VideoSystem_t  actualVideoSystem = videoSystem;

	VideoResult_t Status = ResolveToPlayableVideoFile( pFileName, pPathID, videoSystem, VideoSystemFeature::PLAY_VIDEO_FILE_FULL_SCREEN, PlayAlternateIfNotAvailable,
	                                                   ResolvedFilePath, sizeof(ResolvedFilePath), &actualVideoSystem );
	                                                   
	if ( Status != VideoResult::SUCCESS )
	{
		return Status;
	}

	int sysIndex = GetIndexForSystem( actualVideoSystem );
	
	if ( sysIndex != SYSTEM_NOT_FOUND )
	{
		return SetResult( m_VideoSystems[sysIndex]->PlayVideoFileFullScreen( ResolvedFilePath, mainWindow, windowWidth, windowHeight, desktopWidth, desktopHeight, windowed, forcedMinTime, playbackFlags ) );
	}
	else
	{
		return SetResult( VideoResult::SYSTEM_ERROR_OCCURED );
	}
	
}


// ===========================================================================	
// Functions to connect sound systems to video systems
// ===========================================================================	
VideoResult_t CValveVideoServices::SoundDeviceCommand( VideoSoundDeviceOperation_t operation, void *pDevice, void *pData, VideoSystem_t videoSystem )
{
	AssertExitV( IS_IN_RANGECOUNT( operation, 0, VideoSoundDeviceOperation::OPERATION_COUNT ), SetResult( VideoResult::BAD_INPUT_PARAMETERS ) );
	
	AssertExitV( videoSystem == VideoSystem::ALL_VIDEO_SYSTEMS || IS_IN_RANGECOUNT( videoSystem, VideoSystem::VIDEO_SYSTEM_FIRST, VideoSystem::VIDEO_SYSTEM_COUNT ), SetResult( VideoResult::BAD_INPUT_PARAMETERS ) );

	int startIdx = (int)  VideoSystem::VIDEO_SYSTEM_FIRST;
	int lastIdx = (int) VideoSystem::VIDEO_SYSTEM_COUNT - 1;
	
	if ( videoSystem != VideoSystem::ALL_VIDEO_SYSTEMS )
	{
		startIdx = lastIdx = GetIndexForSystem( videoSystem );
		if ( startIdx == SYSTEM_NOT_FOUND )
		{
			return SetResult( VideoResult::SYSTEM_NOT_AVAILABLE );
		}
	}
	
	VideoResult_t result = VideoResult::SYSTEM_NOT_AVAILABLE;
	
	for ( int i = startIdx; i <= lastIdx; i++ )
	{
		int n = GetIndexForSystem( (VideoSystem_t) i );
		if ( n != SYSTEM_NOT_FOUND )
		{
			result = m_VideoSystems[n]->VideoSoundDeviceCMD( operation, pDevice, pData );
		}
	}
	
	return SetResult( result );
}


// ===========================================================================	
// Sets the sound devices that the video will decode to
// ===========================================================================	
const wchar_t *CValveVideoServices::GetCodecName( VideoEncodeCodec_t nCodec )
{
	static const char *s_pCodecLookup[VideoEncodeCodec::CODEC_COUNT] =
	{
		"#Codec_MPEG2",
		"#Codec_MPEG4",
		"#Codec_H261",
		"#Codec_H263",
		"#Codec_H264",
		"#Codec_MJPEG_A",
		"#Codec_MJPEG_B",
		"#Codec_SORENSON3",
		"#Codec_CINEPACK",
		"#Codec_WEBM",
	};

	if ( nCodec < 0 || nCodec >= VideoEncodeCodec::CODEC_COUNT )
	{
		AssertMsg( 0, "Invalid codec in CValveVideoServices::GetCodecName()" );
		return NULL;
	}

	return g_pVGuiLocalize->Find( s_pCodecLookup[ nCodec ] );
}

// ===========================================================================	
// Functions to determine which file and video system to use
// ===========================================================================	
VideoResult_t CValveVideoServices::ResolveToPlayableVideoFile( const char *pFileName, const char *pPathID, VideoSystem_t videoSystem, VideoSystemFeature_t requiredFeature, 
									bool PlayAlternateIfNotAvailable, char *pResolvedFileName, int resolvedFileNameMaxLen, VideoSystem_t *pResolvedVideoSystem )
{
	SetResult( VideoResult::BAD_INPUT_PARAMETERS );
	AssertExitV( IS_NOT_EMPTY( pFileName ), VideoResult::BAD_INPUT_PARAMETERS );
	AssertExitV( videoSystem == VideoSystem::DETERMINE_FROM_FILE_EXTENSION || IS_IN_RANGECOUNT( videoSystem, VideoSystem::VIDEO_SYSTEM_FIRST, VideoSystem::VIDEO_SYSTEM_COUNT ), VideoResult::BAD_INPUT_PARAMETERS );
	AssertExitV( requiredFeature != VideoSystemFeature::NO_FEATURES, VideoResult::BAD_INPUT_PARAMETERS );
	AssertExitV( pResolvedFileName != nullptr && resolvedFileNameMaxLen > 0 && pResolvedVideoSystem != nullptr, VideoResult::BAD_INPUT_PARAMETERS );
	
	// clear results should we return failure
	pResolvedFileName[0] = nullchar;
	*pResolvedVideoSystem = VideoSystem::NONE;
	
	int sysIdx = SYSTEM_NOT_FOUND;
	VideoSystemFeature_t sysFeatures = VideoSystemFeature::NO_FEATURES;

	// check the file extension to see if it specifies searching for any compatible video files
	// if so, override a couple input values
	if ( !IsMatchAnyExtension( pFileName ) )
	{
		goto search_for_video;
	}
	
	// is the requested video system available?
	
	// We start with either the specified video system.. OR.. we choose the system based on the file extension
	// Get the system and if it's valid, it's available features
	if ( videoSystem != VideoSystem::DETERMINE_FROM_FILE_EXTENSION )
	{	
		sysIdx		= GetIndexForSystem( videoSystem );				// Caller specified the video system 
		sysFeatures = ( sysIdx != SYSTEM_NOT_FOUND ) ? m_VideoSystemFeatures[sysIdx] : VideoSystemFeature::NO_FEATURES;
	}
	else
	{
		// We need to determine the system to use based on filename
		sysIdx = GetIndexForSystem( LocateSystemAndFeaturesForFileName( pFileName, &sysFeatures, requiredFeature ) );
	}

	// if we don't have a system to play this video.. and aren't allowed to look for an alternative...
	if ( sysIdx == SYSTEM_NOT_FOUND && PlayAlternateIfNotAvailable == false )
	{
		return SetResult( VideoResult::VIDEO_SYSTEM_NOT_FOUND );		// return failure
	}

	char ActualFilePath[MAX_PATH];
	
	// Examine the requested of inferred video system to see if it can do what we want, 
	// and if so, see if the corresponding file is actually found (we support search paths)
	
	// Decision Path for when we have a preferred/specified video system specified to use
	if ( sysIdx != SYSTEM_NOT_FOUND )	
	{
		bool  fileFound = false;

		// if the request system can do the task, see if we can find the file as supplied by the caller
		if ( BITFLAGS_SET( sysFeatures, requiredFeature ) )
		{
			if ( V_IsAbsolutePath( pFileName ) )
			{
				V_strncpy( ActualFilePath, pFileName, sizeof( ActualFilePath ) );
				fileFound = g_pFullFileSystem->FileExists( pFileName, nullptr );
			}
			else 
			{
				fileFound = ( g_pFullFileSystem->RelativePathToFullPath( pFileName, pPathID, ActualFilePath, sizeof( ActualFilePath ) ) != nullptr );
			}
		}
		else	// The specified video system does not support this (required) feature
		{
			// if we can't search for an alternative file, tell them we don't support this
			if ( !PlayAlternateIfNotAvailable )
			{
				return SetResult( VideoResult::FEATURE_NOT_AVAILABLE );
			}
		}

		// We found the specified file, and the video system has the feature support
		if ( fileFound )
		{
			// copy the resolved filename and system and report success
			V_strncpy( pResolvedFileName, ActualFilePath, resolvedFileNameMaxLen );
			*pResolvedVideoSystem = GetSystemForIndex( sysIdx );
			return SetResult( VideoResult::SUCCESS );
		}

		// ok, we have the feature support but didn't find the file to use...
		if ( !PlayAlternateIfNotAvailable )
		{
			// if we can't search for an alternate file, so report file not found
			return SetResult( VideoResult::VIDEO_FILE_NOT_FOUND );
		}
	}

	// Ok, we didn't find the file and a system that could handle it
	// but hey, we are allowed to look for an alternate video file and system

search_for_video:

	// start with the passed in filespec, and change the extension to wildcard
	char SearchFileSpec[MAX_PATH];
	V_strncpy( SearchFileSpec, pFileName, sizeof(SearchFileSpec) );
	V_SetExtension( SearchFileSpec, ".*", sizeof(SearchFileSpec) );
	
	FileFindHandle_t  searchHandle = 0;

	const char *pMatchingFile = g_pFullFileSystem->FindFirstEx( SearchFileSpec, pPathID, &searchHandle );
	
	while ( pMatchingFile != nullptr )
	{
		const char *pExt = GetFileExtension( pMatchingFile );
		
		if ( pExt != nullptr )
		{
			// compare file extensions
			for ( int i = 0; i < m_ExtInfo.Count(); i++ )
			{
				// do we match a known extension?
				if ( stricmp( pExt, m_ExtInfo[i].m_FileExtension ) == STRINGS_MATCH )
				{
					// do we support the requested feature?
					if ( BITFLAGS_SET( m_ExtInfo[i].m_VideoFeatures, requiredFeature ) )
					{
						// Make sure it's a valid system
						sysIdx = GetIndexForSystem( m_ExtInfo[i].m_VideoSubSystem );
						if ( sysIdx != SYSTEM_NOT_FOUND )
						{
							
							// Start with any optional path we got...
							V_ExtractFilePath( pFileName, ActualFilePath, sizeof( ActualFilePath ) );
							// Append the search match file							
							V_strncat( ActualFilePath, pMatchingFile, sizeof( ActualFilePath ) );
							
							if ( V_IsAbsolutePath( ActualFilePath ) )
							{
								V_strncpy( pResolvedFileName, ActualFilePath, resolvedFileNameMaxLen );
							}
							else
							{
								g_pFullFileSystem->RelativePathToFullPath( ActualFilePath, pPathID, pResolvedFileName, resolvedFileNameMaxLen );
							}
							
							// Return the system
							*pResolvedVideoSystem = GetSystemForIndex( sysIdx );
							
							g_pFullFileSystem->FindClose( searchHandle );
							
							return SetResult( VideoResult::SUCCESS );
						}
					}
				}
			}
		}
	
		// not usable.. keep searching		
		pMatchingFile = g_pFullFileSystem->FindNext( searchHandle );
	}	
	
	// we didn't find anything we could use
	g_pFullFileSystem->FindClose( searchHandle );
		
	return SetResult( VideoResult::VIDEO_FILE_NOT_FOUND );
}


VideoSystem_t CValveVideoServices::LocateSystemAndFeaturesForFileName( const char *pFileName,  VideoSystemFeature_t *pFeatures, VideoSystemFeature_t requiredFeatures )
{
	if ( pFeatures != nullptr)
	{
		*pFeatures = VideoSystemFeature::NO_FEATURES;
	}
	
	AssertExitV( IS_NOT_EMPTY( pFileName ), VideoSystem::NONE );
	
	if ( m_ExtInfo.Count() < 1 )
	{
		return VideoSystem::NONE;
	}
	
	// extract the file extension
	
	char fileExt[MAX_PATH];
	
	const char *pExt = GetFileExtension( pFileName );
	if ( pExt == nullptr )
	{
		return VideoSystem::NONE;
	}

	// lowercase it so we can compare	
	V_strncpy( fileExt, pExt, sizeof(fileExt) );
	V_strlower( fileExt );
	
	for ( int i = 0; i < m_ExtInfo.Count(); i++ )
	{
		if ( V_stricmp( fileExt, m_ExtInfo[i].m_FileExtension ) == STRINGS_MATCH )
		{
			// must it have certain feature support?
			if ( requiredFeatures != VideoSystemFeature::NO_FEATURES )
			{
				if ( !BITFLAGS_SET( m_ExtInfo[i].m_VideoFeatures, requiredFeatures ) )
				{
					continue;
				}
			}
		
			if ( pFeatures != nullptr)
			{
				*pFeatures = m_ExtInfo[i].m_VideoFeatures;
			}
			return m_ExtInfo[i].m_VideoSubSystem;
		}
	}
	
	return VideoSystem::NONE;
}


bool CValveVideoServices::IsMatchAnyExtension( const char *pFileName )
{
	if ( IS_EMPTY_STR( pFileName ) )
	{
		return false;
	}
		
	const char* pExt = GetFileExtension( pFileName );
	if ( pExt == nullptr )
	{
		return false;
	}
	
	return ( V_stricmp( pExt, FILE_EXTENSION_ANY_MATCHING_VIDEO ) == STRINGS_MATCH );
}


const char *CValveVideoServices::GetFileExtension( const char *pFileName )
{
	if ( pFileName == nullptr )
	{
		return nullptr;
	}
	
	const char *pExt = V_GetFileExtension( pFileName );
	
	if ( pExt == nullptr )
	{
		return nullptr;
	}

	if ( pExt != pFileName && *( pExt - 1 ) == '.' )
	{
		pExt--;
	}

	return pExt;
}



// ===========================================================================	
// CVideoCommonServices - services used by any/multiple videoSubsystems
//   Functions are put here to avoid duplication and ensure they stay
//   consistant across all installed subsystems
// ===========================================================================	


#ifdef WIN32		
	typedef SHORT (WINAPI *GetAsyncKeyStateFn_t)( int vKey );

	static HINSTANCE s_UserDLLhInst = nullptr;
	GetAsyncKeyStateFn_t s_pfnGetAsyncKeyState = nullptr;
#endif

CVideoCommonServices::CVideoCommonServices()
{
	ResetInputHandlerState();
}


CVideoCommonServices::~CVideoCommonServices()
{
	if ( m_bInputHandlerInitialized )
	{
		TerminateFullScreenPlaybackInputHandler();
	}

}


void CVideoCommonServices::ResetInputHandlerState()
{
	m_bInputHandlerInitialized = false;
	
	m_bScanAll	= false;
	m_bScanEsc	= false;
	m_bScanReturn	= false;
	m_bScanSpace	= false;
	m_bPauseEnabled	= false;
	m_bAbortEnabled	= false;
	m_bEscLast	= false;
	m_bReturnLast	= false;
	m_bSpaceLast	= false;
	m_bForceMinPlayTime	= false;
		
	m_bWindowed = false;
	
	m_playbackFlags = VideoPlaybackFlags::NO_PLAYBACK_OPTIONS;
	m_forcedMinTime = 0.0f;
	
	m_StartTime = 0;
	
#ifdef WIN32
	s_UserDLLhInst = nullptr;
	s_pfnGetAsyncKeyState = nullptr;
#endif	

}

// ===========================================================================	
// Calculate the proper dimensions to play a video in full screen mode
//  uses the playback flags to supply rules for streaching, scaling	and 
//  centering the video
// ===========================================================================	
bool CVideoCommonServices::CalculateVideoDimensions( int videoWidth, int videoHeight, int displayWidth, int displayHeight, VideoPlaybackFlags_t playbackFlags, 
													int *pOutputWidth, int *pOutputHeight, int *pXOffset, int *pYOffset )
{
	AssertExitF( pOutputWidth != nullptr && pOutputHeight != nullptr && pXOffset != nullptr && pYOffset != nullptr );
	AssertExitF( videoWidth >= 16 && videoHeight >= 16 && displayWidth > 64 && displayHeight > 64 );

	// extract relevant options
	bool bFillWindow	= BITFLAGS_SET( playbackFlags, VideoPlaybackFlags::FILL_WINDOW );
	bool bLockAspect	= BITFLAGS_SET( playbackFlags, VideoPlaybackFlags::LOCK_ASPECT_RATIO );
	bool bIntegralScale = BITFLAGS_SET( playbackFlags, VideoPlaybackFlags::INTEGRAL_SCALE );
	bool bCenterVideo	= BITFLAGS_SET( playbackFlags, VideoPlaybackFlags::CENTER_VIDEO_IN_WINDOW );
	
	int curWidth = videoWidth;
	int curHeight = videoHeight;
	
	// Try and just play it actual size?
	if ( !bFillWindow )	
	{
		// is the window the same size or larger?
		if ( curWidth <= displayWidth && curHeight <= displayHeight )
		{
			goto finish;
		}
		else  // we need to shrink the video output
		{
			// if we aren't locking the aspect ratio, just shrink each axis until it fits
			if ( !bLockAspect )
			{
				while ( curWidth > displayWidth)
				{
					curWidth = ( bIntegralScale ) ? curWidth >> 1 : displayWidth;
				}
				while ( curHeight > displayHeight )
				{
					curHeight = ( bIntegralScale ) ? curHeight >> 1 : displayHeight;
				}
				goto finish;
			}
			else   // we are locking the aspect ratio, and need to shrink the video
			{
				// integral scale only....
				if ( bIntegralScale )	
				{
					while ( curWidth > displayWidth || curHeight > displayHeight)
					{
						curWidth >>= 1;
						curHeight >>= 1;
					}
					goto finish;
				}
				else	// can scale variably..
				{
					float Xfactor = ( displayWidth / curWidth );
					float Yfactor = ( displayHeight / curHeight );
					float scale = MIN( Xfactor, Yfactor );
					
					curWidth = (int)  ( curWidth * scale + 0.35f );
					curHeight = (int) ( curHeight * scale + 0.35f );
					clamp( curWidth, 0, displayWidth );
					clamp( curHeight, 0, displayHeight );
					goto finish;
				}
			
			}
		}
	}
	
	// ok.. we are wanting to fill the window....
	if ( bFillWindow )
	{
		// are we locking the aspect ratio?
		if ( bLockAspect )
		{
			// are we only allowed to scale integrally?
			if ( bIntegralScale )
			{
				while ( (curWidth << 1) <= displayWidth && (curHeight << 1) <= displayHeight )
				{
					curWidth <<= 1;
					curHeight <<= 1;
				}
				goto finish;
			}
			else
			{
				float Xfactor = ( (float)displayWidth / curWidth );
				float Yfactor = ( (float)displayHeight / curHeight );
				float scale = MIN( Xfactor, Yfactor );
				
				curWidth = (int)  ( curWidth * scale + 0.35f );
				curHeight = (int) ( curHeight * scale + 0.35f );
				clamp( curWidth, 0, displayWidth );
				clamp( curHeight, 0, displayHeight );
				goto finish;
			}
		}
		else // we are not locking the aspect ratio...
		{
			if ( bIntegralScale )
			{
				while ( (curWidth << 1) <= displayWidth  )
				{
					curWidth <<= 1;
				}
				while ( (curHeight << 1) <= displayHeight )
				{
					curHeight <<= 1;
				}
				goto finish;
			}
			else
			{	
				curWidth = displayWidth;
				curHeight = displayHeight;
				goto finish;
			}
		}
	}	


finish:
	AssertExitF( displayWidth >= curWidth && displayHeight >= curHeight );

	if ( bCenterVideo )
	{
		*pXOffset = ( displayWidth - curWidth ) >> 1;
		*pYOffset = ( displayHeight - curHeight ) >> 1;
	}
	else
	{
		*pXOffset = 0;
		*pYOffset = 0;
	}

	*pOutputWidth = curWidth;
	*pOutputHeight = curHeight;

	return true;

}


float CVideoCommonServices::GetSystemVolume()
{
	ConVarRef volumeConVar( "volume" );
	float sysVolume = volumeConVar.IsValid() ? volumeConVar.GetFloat() : 1.0f;
	clamp( sysVolume, 0.0f, 1.0f);

	return sysVolume;
}

									  

// ===========================================================================	
// Sets up the state machine to receive messages and poll the keyboard
//   while a full-screen video is playing
// ===========================================================================	
VideoResult_t CVideoCommonServices::InitFullScreenPlaybackInputHandler( VideoPlaybackFlags_t playbackFlags, float forcedMinTime, bool windowed )
{
	// already initialized?
	if ( m_bInputHandlerInitialized )
	{
		WarningAssert( "called twice" );
		return VideoResult::OPERATION_ALREADY_PERFORMED;
	}

#ifdef WIN32
	// We need to be able to poll the state of the input device, but we're not completely setup yet, so this spoofs the ability
	HINSTANCE m_UserDLLhInst = LoadLibrary( "user32.dll" );
	if ( m_UserDLLhInst == NULL )
	{
		return VideoResult::SYSTEM_ERROR_OCCURED;
	}

	s_pfnGetAsyncKeyState = (GetAsyncKeyStateFn_t) GetProcAddress( m_UserDLLhInst, "GetAsyncKeyState" );
	if ( s_pfnGetAsyncKeyState == NULL )	
	{
		FreeLibrary( m_UserDLLhInst );
		return VideoResult::SYSTEM_ERROR_OCCURED;
	}
	
#endif

	// save off playback options	
	m_playbackFlags = playbackFlags;
	m_forcedMinTime = forcedMinTime;
	m_bWindowed = windowed;

	// process the pause and abort options
	m_bScanAll =  ANY_BITFLAGS_SET( playbackFlags, VideoPlaybackFlags::PAUSE_ON_ANY_KEY | VideoPlaybackFlags::ABORT_ON_ANY_KEY );
	
	m_bScanEsc	 = m_bScanAll || ANY_BITFLAGS_SET( playbackFlags, VideoPlaybackFlags::PAUSE_ON_ESC | VideoPlaybackFlags::ABORT_ON_ESC ); 
	m_bScanReturn = m_bScanAll || ANY_BITFLAGS_SET( playbackFlags, VideoPlaybackFlags::PAUSE_ON_RETURN | VideoPlaybackFlags::ABORT_ON_RETURN );
	m_bScanSpace  = m_bScanAll || ANY_BITFLAGS_SET( playbackFlags, VideoPlaybackFlags::PAUSE_ON_SPACE | VideoPlaybackFlags::ABORT_ON_SPACE );

	m_bPauseEnabled = ANY_BITFLAGS_SET( playbackFlags, VideoPlaybackFlags::PAUSE_ON_ESC | VideoPlaybackFlags::PAUSE_ON_RETURN | VideoPlaybackFlags::PAUSE_ON_SPACE | VideoPlaybackFlags::PAUSE_ON_ANY_KEY );
	m_bAbortEnabled = ANY_BITFLAGS_SET( playbackFlags, VideoPlaybackFlags::ABORT_ON_ESC | VideoPlaybackFlags::ABORT_ON_RETURN | VideoPlaybackFlags::ABORT_ON_SPACE | VideoPlaybackFlags::ABORT_ON_ANY_KEY );

	// Setup the scan options
	m_bEscLast	 = false;
	m_bReturnLast = false;
	m_bSpaceLast  = false;

	// Other Movie playback	state init
	m_bForceMinPlayTime = BITFLAGS_SET( playbackFlags, VideoPlaybackFlags::FORCE_MIN_PLAY_TIME ) && ( forcedMinTime > 0.0f );

	// Note the start time
	m_StartTime = Plat_FloatTime();

	// and we're on
	m_bInputHandlerInitialized = true;
	
	return VideoResult::SUCCESS;
}


// ===========================================================================	
//  Pumps the message loops and checks for a supported event
//  returns true if there is an event to check
// ===========================================================================	
bool CVideoCommonServices::ProcessFullScreenInput( bool &bAbortEvent, bool &bPauseEvent, bool &bQuitEvent )
{

	bAbortEvent = false;
	bPauseEvent = false;
	bQuitEvent = false;

	if ( !m_bInputHandlerInitialized )
	{
		WarningAssert( "Not Initialized to call" );
		return false;
	}


	// Pump OS Messages
#if defined( WIN32 )
	MSG msg;
	while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
	{
		// did we get a quit message?
		if ( msg.message == WM_QUIT )
		{
			::PostQuitMessage( msg.wParam );
			return true;			
		}
	
		// todo - look for alt-tab events, etc?
	
		TranslateMessage( &msg );
		DispatchMessage( &msg );
	}
	// Escape, return, or space stops or pauses the playback
	bool bEscPressed	= ( m_bScanEsc )    ? ( s_pfnGetAsyncKeyState( VK_ESCAPE ) & 0x8000 ) != 0 : false;
	bool bReturnPressed	= ( m_bScanReturn ) ? ( s_pfnGetAsyncKeyState( VK_RETURN ) & 0x8000 ) != 0 : false;
	bool bSpacePressed	= ( m_bScanSpace )  ? ( s_pfnGetAsyncKeyState( VK_SPACE ) & 0x8000 ) != 0  : false;
#elif defined(OSX)
	g_pLauncherMgr->PumpWindowsMessageLoop();
	// Escape, return, or space stops or pauses the playback
	bool bEscPressed    = ( m_bScanEsc )    ? CGEventSourceKeyState( kCGEventSourceStateCombinedSessionState, kVK_Escape ) : false;
	bool bReturnPressed = ( m_bScanReturn ) ? CGEventSourceKeyState( kCGEventSourceStateCombinedSessionState, kVK_Return ) : false;
	bool bSpacePressed  = ( m_bScanSpace )  ? CGEventSourceKeyState( kCGEventSourceStateCombinedSessionState, kVK_Space )  : false;
#elif defined(LINUX) || defined(PLATFORM_BSD)
	g_pLauncherMgr->PumpWindowsMessageLoop();

	// Escape, return, or space stops or pauses the playback
	bool bEscPressed	= false;
	bool bReturnPressed	= false;
	bool bSpacePressed	= false;

	g_pLauncherMgr->PeekAndRemoveKeyboardEvents( &bEscPressed, &bReturnPressed, &bSpacePressed );
#endif

	// Manual debounce of the keys, only interested in unpressed->pressed transitions
	bool bEscEvent = ( bEscPressed != m_bEscLast ) && bEscPressed;
	bool bReturnEvent = ( bReturnPressed != m_bReturnLast ) &&  bReturnPressed;
	bool bSpaceEvent = ( bSpacePressed != m_bSpaceLast ) && bSpacePressed;
	bool bAnyKeyEvent = bEscEvent || bReturnEvent || bSpaceEvent;

	m_bEscLast = bEscPressed;
	m_bReturnLast = bReturnPressed;
	m_bSpaceLast = bSpacePressed;

	// Are we forcing a minimum playback time?
	// if so, no Abort or Pause events until the necessary time has elasped
	if ( m_bForceMinPlayTime )
	{
		double elapsedTime = Plat_FloatTime() - m_StartTime;
		if ( (float) elapsedTime > m_forcedMinTime )
		{
			m_bForceMinPlayTime = false;		// turn off forced minimum
		}
	}

	// any key events to check? ( provided minimum enforced playback has occurred )
	if ( m_bForceMinPlayTime == false && bAnyKeyEvent )
	{
		// check for aborting the movie
		if ( m_bAbortEnabled )
		{
			bAbortEvent = ( bAnyKeyEvent && BITFLAGS_SET( m_playbackFlags, VideoPlaybackFlags::ABORT_ON_ANY_KEY ) ) ||
						  ( bEscEvent && BITFLAGS_SET( m_playbackFlags, VideoPlaybackFlags::ABORT_ON_ESC ) ) ||
				          ( bReturnEvent && BITFLAGS_SET( m_playbackFlags, VideoPlaybackFlags::ABORT_ON_RETURN ) ) ||
						  ( bSpaceEvent && BITFLAGS_SET( m_playbackFlags, VideoPlaybackFlags::ABORT_ON_SPACE ) );
			
		}
			
		// check for pausing the movie?
		if ( m_bPauseEnabled )
		{
			bPauseEvent = ( bAnyKeyEvent && BITFLAGS_SET( m_playbackFlags, VideoPlaybackFlags::PAUSE_ON_ANY_KEY ) ) ||
						  ( bEscEvent && BITFLAGS_SET( m_playbackFlags, VideoPlaybackFlags::PAUSE_ON_ESC ) ) ||
				          ( bReturnEvent && BITFLAGS_SET( m_playbackFlags, VideoPlaybackFlags::PAUSE_ON_RETURN ) ) ||
						  ( bSpaceEvent && BITFLAGS_SET( m_playbackFlags, VideoPlaybackFlags::PAUSE_ON_SPACE ) );
		}
	}				

	// notify if any events triggered
	return ( bAbortEvent || bPauseEvent );
}




VideoResult_t CVideoCommonServices::TerminateFullScreenPlaybackInputHandler()
{

	if ( !m_bInputHandlerInitialized )
	{
		WarningAssert( "Not Initialized to call" );
		return VideoResult::OPERATION_OUT_OF_SEQUENCE;
	}

#if defined ( WIN32 )
	FreeLibrary( s_UserDLLhInst );		// and free the dll we needed
#endif

	ResetInputHandlerState();
	
	return VideoResult::SUCCESS;
	
}