//
// Asura_Texture_Streaming.h
//
// Created by KJFL - 10 March 2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __ASURA_TEXTURE_STREAMING_H__
#define __ASURA_TEXTURE_STREAMING_H__

#ifdef ASURA_USE_TEXTURE_STREAMING

#include "Core/Asura_StringBuffer.h"
#include "Core/Asura_TextureManagement.h"
#include "Core/Asura_Timers.h"
#include "ChunkSystem/Asura_Chunk_Resourcefile.h"

// Memory limit is initialised to 0 (interpreted as unlimited) if a project hasn't defined it.
#ifndef ASURA_TEXTURE_STREAMING_MEMORY_ALLOWED_MB
	#define ASURA_TEXTURE_STREAMING_MEMORY_ALLOWED_MB 0
#endif //#ifndef ASURA_TEXTURE_STREAMING_MEMORY_ALLOWED_MB

#ifndef ASURA_TEXTURE_STREAMING_DEFAULT_MAX_LOADED_TEXTURES
	#define ASURA_TEXTURE_STREAMING_DEFAULT_MAX_LOADED_TEXTURES 128
#endif //#ifndef ASURA_TEXTURE_STREAMING_DEFAULT_MAX_LOADED_TEXTURES

class Asura_Semaphore;

class Asura_Streaming_Texture
{
	public:

		enum AST_STATUS
		{
			AST_NOTLOADED,
			AST_LOADING,
			AST_LOADED,
			AST_FREENEXTFRAME,
			AST_FREEIN2FRAMES,
			AST_FREEIN3FRAMES,
		};

		void Initialise( u_int iTextureIndex, int iTextureUsage )
		{
			m_uHashID = Asura_Texture_Management::GetHashIDFromIndex( iTextureIndex );
			m_uPositionInFile = 0;
			m_uFileSize = 0;
			m_iTextureIndex = iTextureIndex;
			m_uPriority = 0;
			m_uChunkFlags = 0;
			m_iTextureUsage = iTextureUsage;
			m_uLastUsedFrameStamp = 0;
			m_eStatus = AST_NOTLOADED;
#ifdef ASURA_PLATFORM_USE_FILE_SYSTEM_CACHE
			m_bInCache = false;
#endif
		}

		void Initialise( Asura_Hash_ID uHashID, u_int uPositionInFile, u_int uFileSize, u_int uChunkFlags)
		{
			m_uHashID = uHashID;
			m_uPositionInFile = uPositionInFile;
			m_uFileSize = uFileSize;
			m_iTextureIndex = Asura_Texture_Management::GetIndexFromHashID( uHashID );
			m_uPriority = 0;
			m_uChunkFlags = uChunkFlags;
			m_uLastUsedFrameStamp = 0;
			m_eStatus = AST_NOTLOADED;

			m_iTextureUsage = -1;

#ifdef ASURA_PLATFORM_USE_FILE_SYSTEM_CACHE
			m_bInCache = false;
#endif
		}

		int GetTextureIndex( void ) const
		{
			if ( m_iTextureIndex == -1 ) 
			{
				m_iTextureIndex = Asura_Texture_Management::GetIndexFromHashID( m_uHashID );
			}
			return m_iTextureIndex;
		}

		u_int GetPriority( void ) const
		{
			if ( Asura_Timers::GetFrameNumber() != m_uLastUsedFrameStamp )
			{
				return 0;
			}
			else
			{
				return m_uPriority;
			}
		}

		u_int GetLastUsedFrameStamp( void ) const { return m_uLastUsedFrameStamp; }
		
		AST_STATUS GetStatus( void ) const { return m_eStatus; }

		const Asura_String* GetFilename( void ) const
		{
			return Asura_Texture_Management::GetTextureName( m_iTextureIndex );
		}

		Asura_Hash_ID GetHashID( void ) const { return m_uHashID; }

		u_int GetPositionInFile( void ) const { return m_uPositionInFile; }

		u_int GetFileSize( void ) const { return m_uFileSize; }

		u_int GetChunkFlags( void ) const { return m_uChunkFlags; }

		int GetTextureUsage(void ) const { return m_iTextureUsage; }

		void SetPriority( u_int uPriority )
		{
			const u_int uCurrentFrame = Asura_Timers::GetFrameNumber();
			
			if ( uCurrentFrame != m_uLastUsedFrameStamp )
			{
				m_uPriority = uPriority;
				m_uLastUsedFrameStamp = uCurrentFrame;
			}
			else
			{
				m_uPriority = Asura_Maths::Max( m_uPriority, uPriority );
			}
		}

		void SetStatus( AST_STATUS eStatus ) { m_eStatus = eStatus; }

		void SetPositionInFile( u_int uPosition ) { m_uPositionInFile = uPosition; }

		void SetFileSize( u_int uFileSize ) { m_uFileSize = uFileSize; }

		void SetChunkFlags( u_int uChunkFlags )  { m_uChunkFlags = uChunkFlags; }

#ifdef ASURA_PLATFORM_USE_FILE_SYSTEM_CACHE
		bool IsInCache( void ) { return m_bInCache; }
		void SetIsInCache( bool bInCache ) { m_bInCache = bInCache; }
#endif

	private:

		Asura_Hash_ID	m_uHashID;
		u_int			m_uPositionInFile;
		u_int			m_uFileSize;
		u_int			m_uChunkFlags;

		mutable int		m_iTextureIndex;

		u_int			m_uPriority;
		u_int			m_uLastUsedFrameStamp;

		AST_STATUS		m_eStatus;

		int				m_iTextureUsage;

		#ifdef ASURA_PLATFORM_USE_FILE_SYSTEM_CACHE
		bool			m_bInCache;
		#endif
};



class Asura_Texture_Streaming_System
{
	public:
		static void Initialise( void );
		static void Reset( void );
		static void Shutdown( void );

		static void Update( void );

		static bool IsTextureLoaded( int iTextureIndex );

		static void SetTexturePriority( int iTextureIndex, u_int uPriority );

		static Asura_Streaming_Texture* GetBestTextureToLoad( void );

		static u_int GetNumberOfStreamingTextures( void ) { return s_uNumberOfStreamingTextures; }

		static Asura_Streaming_Texture* GetStreamingTexture( u_int u );

		static const char* GetTextureStreamingFilename( void ) { return s_xTextureStreamingFilename.GetString(); }

#ifdef ASURA_TOOLS

		static void Tools_RegisterTexture( int iTextureIndex, int iTextureUsage );

		static bool Tools_IsTextureRegistered( int iTextureIndex );
	
		static void Tools_FindStreamingTextures( void );

		static void Tools_SetTextureStreamingFilename( const char* const szFilename )
		{
			s_xTextureStreamingFilename = szFilename;
		}
#endif

		// Special testing/demoing functions that allow game code to do similar things to the console commands, so we can demonstrate the system without needing the DVS active.
		static void Test_SetLoadingDelay(float fDelay);
		static void Test_UnloadAll();

		// Set the maximum number of streaming textures that can be loaded at one time
		static void SetMaxNumberOfLoadedTextures( u_int uMaxTextures ) { s_uMaxNumberOfLoadedTextures = uMaxTextures; }

	private: friend class Asura_Chunk_Texture_Streaming;

		static void Platform_Initialise();
		static void Platform_Shutdown();

		static bool ConsoleCommand_UnloadAll( bool bHelp );
#ifdef ASURA_PLATFORM_USE_FILE_SYSTEM_CACHE
		static bool ConsoleCommand_RemoveFromCache( bool bHelp );
#endif

		static void TextureStreamingThread( const void* pParam );

		static bool ShouldUpdatePriorities( void );

		// returns true if successful (e.g. it could open the streaming texture file, etc)
		static bool Platform_StartStreaming( void );

		static void Platform_EndStreaming( void );
		static void Platform_StreamTexture( Asura_Streaming_Texture& xTexture );

		static void Platform_UnloadTexture( u_int uTextureID );

		static u_int					s_uNumberOfStreamingTextures;

		static Asura_Streaming_Texture	s_axStreamingTextures[ASURA_MAX_NO_OF_TEXTURES];
		static short					s_sTextureIndexToStreamingConversion[ASURA_MAX_NO_OF_TEXTURES];

		static Asura_Semaphore*			s_pxLoadingThreadTerminated;
		static Asura_Semaphore*			s_pxTextureLoadRequired;
		static Asura_Semaphore*			s_pxLoadingThreadReady;
	
		static Asura_StringBuffer<128>	s_xTextureStreamingFilename;

		static u_int					s_uMaxNumberOfLoadedTextures;
		static u_int					s_uNumberOfLoadedTextures;

		static u_int					s_uTextureMemoryUsedInBytes;
};

#endif //#ifdef ASURA_USE_TEXTURE_STREAMING

#endif//__ASURA_TEXTURE_STREAMING_H__

