
#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __TOOLS_TEXTURETILER_TTL_H_
#define __TOOLS_TEXTURETILER_TTL_H_

#include "Core/Asura.h"

// Core
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_TextureManagement.h"

// Tools
#include "Misc/Tools_DynamicArray.h"
#include "TextureTiler/Tools_TextureTiler_Algorithm.h"
#include "TextureTiler/Tools_TextureTiler_Export.h"

class Asura_Chunk_Stream;

enum TOOLS_TT_ERROR_CODE
{
	TOOLS_TT_OK,
	TOOLS_TT_FAIL,
	TOOLS_TT_FILE_NOT_FOUND,
};

// defines the set of strings used to identify different fields in a *.ttl file when a TTL is saveed|loaded.
struct Tools_TT_ListToken
{
	enum TOOLS_TT_LIST_TOKEN
	{
		TOOLS_TT_LIST_TOKEN_NULL = 0,
		TOOLS_TT_LIST_TOKEN_HEIGHT,
		TOOLS_TT_LIST_TOKEN_WIDTH,
		TOOLS_TT_LIST_TOKEN_BPP,
		TOOLS_TT_LIST_TOKEN_TEXTURE,
		TOOLS_TT_LIST_TOKEN_DDSFORMAT,
		TOOLS_TT_LIST_TOKEN_PSP_PIXEL_FORMAT,
		TOOLS_TT_LIST_TOKEN_TPL_ALPHA_BITS,
		TOOLS_TT_LIST_TOKEN_PADDING,
		TOOLS_TT_LIST_TOKEN_ALGORITHM,

		TOOLS_TT_LIST_TOKEN_NUM_TOKENS,
	};

	static const char* s_aszTools_TT_List_Token[TOOLS_TT_LIST_TOKEN_NUM_TOKENS];

	static const char* GetName( TOOLS_TT_LIST_TOKEN eToken )
	{
		if( (eToken<0) || (eToken>=TOOLS_TT_LIST_TOKEN_NUM_TOKENS) )
		{
			eToken = TOOLS_TT_LIST_TOKEN_NULL;
		}

		return s_aszTools_TT_List_Token[eToken];
	}
};

// Represents a TTL 
// 
// contains a unique set of Tools_TT_Tile objects which have been addded to the TTL
// and determines the TTL's width and height (in pixles), BytesPP, target filename, the
// tiling algorithm used and should cover any other properties yet to be added.
class Tools_TT_TiledTextureList
{
public:
	enum TOOLS_TT_BYTESPERPIXEL
	{	
		TOOLS_TT_BYTESPERPIXEL_4,
		TOOLS_TT_BYTESPERPIXEL_1,

		TOOLS_TT_BYTESPERPIXEL_MAX
	};

	Tools_TT_TiledTextureList( const u_int uReserve = 20 );
	Tools_TT_TiledTextureList( const Tools_TT_TiledTextureList& xOther );
	~Tools_TT_TiledTextureList();

	const Tools_TT_TiledTextureList& operator=( const Tools_TT_TiledTextureList& xOther );

	const bool Insert( const int iTextureID ); 									// try to add a texture to the collection, returns true on success
	const bool Insert( const Asura_String& xTextureFileName );					// try to add a texture file to Asura_Texture_Management then add the same texture to the collection. Calls LoadTextures if the arg has not already been loaded. Returns true on success
	const bool Insert( const int* piTextureIDCollection, const int iSize );		// try to add a collection of textures to this, returns false if any insertions fail, true otherwise
	const bool Insert( const Tools_TT_Tile& xTile );							// copy an exiting tile into this

	const bool Erase( const int iTextureID );   // try to remove a texture from the collection, returns true on success
	
	const Tools_TT_Tile& operator[]( const u_int uIndex ) const; // returns a const refference to the subtexture at the index provided within a const TTL's internal array
	Tools_TT_Tile& operator[]( const u_int uIndex ); // returns a refference to the subtexture at the index provided within the TTL's internal array
	const Tools_TT_Tile* const GetTextureFromID( const int iTextureID ) const; // returns a pointer to the subtexture with the Asura_TextureManagement ID suplied as an argument, NULL if subtexture not found

	const u_int GetTextureIndexFromID( const int iTextureID ) const; // returns the index of the subtexture within the TTL's internal array
	const u_int GetTextureIndexFromName( const char* szTextureName ) const; // returns the index of the subtexture within the TTL's internal array

	const u_int Size() const; 	// returns the number of subtextures in the TTL, tiled or otherwise
	const u_int GetTiledTextureCount() const; // return the number of textures that were successfully tiled last time Regenerate() was called

	const Tools_TT_Vector_2i GetDimension() const; // returns a vector describing the witdth and height of the TTL (in pixels)
	const int GetWidth() const;
	const int GetHeight() const;
	const int GetBytesPP() const; 
	const TOOLS_TT_ALGORITHM_TYPE GetTilingAlgorithm() const;
	
	void SetDimension( const Tools_TT_Vector_2i& xDimension );
	void SetWidth( const int iWidth );
	void SetHeight( const int iHeight );
	void SetBytesPP( const int iBytesPP );
	void SetTilingAlgorithm( const TOOLS_TT_ALGORITHM_TYPE eTilingAlgorithm );
	
	const bool ContainsTexture( const int iTextureID ) const; // returns true if a texture with the specified id exists in the collection

	const int Regenerate(); 	// rearange textures within TTL using the specified algorithm

	// create internal text file containing list of textures included in TTL 
	const bool WriteFile( const Asura_String& xFileName );
	const bool WriteChunk( const Asura_String& xOutputFileName,const Tools_Platform_Type eTargetPlatform, const bool bCompressChunk = false );
	const bool WriteChunk( const Asura_String& xOutputFileName,const Asura_String& xResourceChunkName,const Tools_Platform_Type eTargetPlatform, const bool bCompressChunk = false );
	const bool WriteChunk( Asura_Chunk_Stream& xStream,const Asura_String& xOutputFileName,const Tools_Platform_Type eTargetPlatform,const bool bCompressChunk = false );
	const bool WriteMultipleChunks( const Asura_String& xOutputFileName,const Tools_Platform_Type eTargetPlatform,const bool bCompressChunk = false,Tools_DynamicArray<Asura_String>* pxOutput = NULL ); // write multiple TT (and acompaniying resource) chunks of the size defined in the *this and containing a unique set of tiles to a single file untill all tileable textues in *this have been processed 
	const bool WriteMultipleChunks( Asura_Chunk_Stream& xStream,const Asura_String& xOutputFileName,const Tools_Platform_Type eTargetPlatform,const bool bCompressChunk = false,Tools_DynamicArray<Asura_String>* pxOutput = NULL );
	const bool WriteTexture( const Asura_String& xOutputFileName );

	const bool ReadFile( const Asura_String& xFileName );
	const TOOLS_TT_ERROR_CODE ReadChunk( const Asura_String& xFileName,const bool bExtractMissingTextures = false );
	const TOOLS_TT_ERROR_CODE ReadTiledTextureInfoArray( Asura_TiledTextureInfo* const pxTextureInfoBegin,Asura_TiledTextureInfo* const pxTextureInfoEnd );

	// fills the argument with pixel data riped from the textures contained in the TTL
	const Tools_TT_Export_RawPixelData& GetRawPixelData( Tools_TT_Export_RawPixelData& xRawPixelDataOutput ) const;

	const bool IsModified() const;
	const bool HasPadding() const;
	void SetPadding( const bool bPadding );
	const bool HasDataNamePrefix() const;
	void SetDataNamePrefix( const bool bPrefix );
	void SetModified( const bool bModified );

	// export formats
	void SetDDSFormat( const Tools_TT_DDSCompressionFormat::TOOLS_TT_EXPORT_DDS_COMPRESSION_FORMAT eFormat );
	const Tools_TT_DDSCompressionFormat::TOOLS_TT_EXPORT_DDS_COMPRESSION_FORMAT GetDDSFormat() const;
	void SetPSPPixelFormat( const int iFormat );
	const int GetPSPPixelFormat() const;
	void SetTPLAlphaBits( const Tools_TT_TPLAlphaBits::TOOLS_TT_EXPORT_TPL_ALPHA_BITS eFormat );
	const Tools_TT_TPLAlphaBits::TOOLS_TT_EXPORT_TPL_ALPHA_BITS GetTPLAlphaBits() const;
	void SetTextureFlags( const int iFlags ) { m_iTextureFlags = iFlags; };
	void SetTextureFlag( const int iFlag,const bool bState = true );
	const int GetTextureFlags() const { return m_iTextureFlags; }

	void Clear(); // erase all elements from the TTL

	static const Asura_String& GetDataNamePrefix(){return s_xDataNamePrefix;}

private:
	// <-- private member methods -->

	// increase the size of the array holding the bitmap info objects by ExpansionBlockSize
	void ExpandCollection();

	// set all TextureInfo object's tiled flag to false
	void UnsetAllTiledStatus();

	void InitialiseFromFile( const Asura_String& xFileName );

	// <-- private member data -->
	Tools_DynamicArray<Tools_TT_Tile> m_xTextureInfoCollection;

	bool m_bHasPadding;                         // true if subtextures should be surrounded by a 1 pixel border when tiled
	bool m_bPrefixDataName;                     // if true, the tiled texture image data is prefixed with s_xDataNamePrefix in an asr file
	bool m_bModified;							// true if TTL has changed since last save
	int m_iWidth;					  			// width of TTL in pixels
	int m_iHeight;								// height of TTL in pixels
	int m_iBytesPP;								// Bits Per Pixel
	u_int m_uTiledTextureCount;	

	TOOLS_TT_ALGORITHM_TYPE m_eTilingAlgorithm;
		
	// export options
	Tools_TT_DDSCompressionFormat::TOOLS_TT_EXPORT_DDS_COMPRESSION_FORMAT m_eDDSFormat;
	Tools_TT_TPLAlphaBits::TOOLS_TT_EXPORT_TPL_ALPHA_BITS                 m_eTPLAlphaBits;
	int m_iPSPPixelFormat;
	int m_iTextureFlags;

	// <-- private static member data -->
	static const Asura_String s_xDataNamePrefix;
};

// returns a const refference to the subtexture at the index provided within a const TTL's internal array
inline const Tools_TT_Tile& Tools_TT_TiledTextureList::operator[]( const u_int uIndex ) const
{
	Asura_Assert( uIndex < Size(),"Requesting element outside of array" );

	return m_xTextureInfoCollection[uIndex];
}

// returns a refference to the subtexture at the index provided within the TTL's internal array
inline Tools_TT_Tile& Tools_TT_TiledTextureList::operator[]( const u_int uIndex )
{
	Asura_Assert( uIndex < Size(),"Requesting element outside of array" );

	return m_xTextureInfoCollection[uIndex];
}

// returns the number of subtextures in the TTL, tiled or otherwise
inline const u_int Tools_TT_TiledTextureList::Size() const
{
	return m_xTextureInfoCollection.GetSize();
}

// return the number of textures that were successfully tiled last time Regenerate() was called
inline const u_int Tools_TT_TiledTextureList::GetTiledTextureCount() const
{
	return m_uTiledTextureCount;
}

// returns a vector describing the witdth and height of the TTL (in pixels)
inline const Tools_TT_Vector_2i Tools_TT_TiledTextureList::GetDimension() const
{
	return Tools_TT_Vector_2i( GetWidth(),GetHeight() );
}

inline void Tools_TT_TiledTextureList::SetDimension( const Tools_TT_Vector_2i& xDimension )
{
	m_bModified = true;
	m_iWidth    = xDimension.x();
	m_iHeight   = xDimension.y();
}

inline const int Tools_TT_TiledTextureList::GetWidth() const
{
	return m_iWidth;
}

inline void Tools_TT_TiledTextureList::SetWidth( const int iWidth )
{
	m_bModified = true;
	m_iWidth = iWidth;
}

inline const int Tools_TT_TiledTextureList::GetHeight() const
{
	return m_iHeight;
}

inline void Tools_TT_TiledTextureList::SetHeight( const int iHeight )
{
	m_bModified = true;
	m_iHeight = iHeight;
}

// returns the Bytes Per Pixel of the TTL
inline const int Tools_TT_TiledTextureList::GetBytesPP() const
{
	return m_iBytesPP;
}

inline void Tools_TT_TiledTextureList::SetBytesPP( const int iBytesPP )
{
	if( !(iBytesPP<0) && (iBytesPP<5) )
	{
		m_bModified = true;
		m_iBytesPP = iBytesPP;
	}
}

inline const TOOLS_TT_ALGORITHM_TYPE Tools_TT_TiledTextureList::GetTilingAlgorithm() const
{
	return m_eTilingAlgorithm;
}

inline void Tools_TT_TiledTextureList::SetTilingAlgorithm( const TOOLS_TT_ALGORITHM_TYPE eTilingAlgorithm )
{
	if( eTilingAlgorithm < TOOLS_TT_NUM_ALGORITHM )
	{
		m_eTilingAlgorithm = eTilingAlgorithm;
	}
	else
	{
		Asura_Assert( false,"requested algorithm not recognised" );
	}
}

inline const bool Tools_TT_TiledTextureList::IsModified() const
{
	return m_bModified;
}

inline const bool Tools_TT_TiledTextureList::HasPadding() const
{
	return m_bHasPadding;
}

inline void Tools_TT_TiledTextureList::SetPadding( const bool bPadding ) 
{
	// if padding state has changed then retile 
	if( bPadding != m_bHasPadding )
	{
		m_bHasPadding = bPadding;
		Regenerate();
	}
}

inline const bool Tools_TT_TiledTextureList::HasDataNamePrefix() const
{
	return m_bPrefixDataName;
}

inline void Tools_TT_TiledTextureList::SetDataNamePrefix( const bool bPrefix )
{
	m_bPrefixDataName = bPrefix;
}

inline void Tools_TT_TiledTextureList::SetModified( const bool bModified )
{
	m_bModified = bModified;
}

// export formats
inline void Tools_TT_TiledTextureList::SetDDSFormat( const Tools_TT_DDSCompressionFormat::TOOLS_TT_EXPORT_DDS_COMPRESSION_FORMAT eFormat )
{
	if( eFormat < Tools_TT_DDSCompressionFormat::TOOLS_TT_EXPORT_DDS_COMPRESSION_FORMAT_NUM )
	{
		m_eDDSFormat = eFormat;
	}
}

inline const Tools_TT_DDSCompressionFormat::TOOLS_TT_EXPORT_DDS_COMPRESSION_FORMAT Tools_TT_TiledTextureList::GetDDSFormat() const
{	
	return m_eDDSFormat;	
}

inline void Tools_TT_TiledTextureList::SetPSPPixelFormat( const int iFormat )
{
	if( (iFormat == Tools_TT_PSPPixelFormat ::GetValue(0)) || (iFormat == Tools_TT_PSPPixelFormat::GetValue(1)))
	{
		m_iPSPPixelFormat = iFormat;
	}
}

inline const int Tools_TT_TiledTextureList::GetPSPPixelFormat() const
{
	return m_iPSPPixelFormat;
}

inline void Tools_TT_TiledTextureList::SetTPLAlphaBits( const Tools_TT_TPLAlphaBits::TOOLS_TT_EXPORT_TPL_ALPHA_BITS eFormat )
{
	if( eFormat < Tools_TT_TPLAlphaBits::TOOLS_TT_EXPORT_TPL_ALPHA_BITS_NUM )
	{
		m_eTPLAlphaBits = eFormat;
	}
}

inline const Tools_TT_TPLAlphaBits::TOOLS_TT_EXPORT_TPL_ALPHA_BITS Tools_TT_TiledTextureList::GetTPLAlphaBits() const
{
	return m_eTPLAlphaBits;
}

// erase all elements from the TTL
inline void Tools_TT_TiledTextureList::Clear()
{
	m_xTextureInfoCollection.Reset();
}

// <-- Convenience Methods -->

// calculate the uv coordinates of all subtextures from the size of the TTL and the location of each texture within it
void Tools_TT_UpdateTTLUVCoordinates( Tools_TT_TiledTextureList& xTTL );

// reduce the width and height of the TTL to the minimum required to hold all the tiled textures.
void Tools_TT_ClipTTLFreeSpace( Tools_TT_TiledTextureList& xTTL );

// increases the width and height of a TTL to the nearest power of two. This is nessary if mipmaps are to be created from the tiled texture.
void Tools_TT_ExpandTTLToNearestPowerOfTwo( Tools_TT_TiledTextureList& xTTL );

// reads in the next resouce chunk in the file and generates a seperate texture file for each of the tiles in in the TTInfo collection by cuting up the Tiled Texture image. returns the number of texture files saved to disk
const int Tools_TT_ExtractTexturesFromResourceChunk( const Asura_String& xInputFileName,const Asura_String& xTargetTextureName,Asura_TiledTextureInfo* const pxTextureInfoBegin,Asura_TiledTextureInfo* const pxTextureInfoEnd );

// creates the correct name for the texture resource chunk that should acompany a TT chunk for a specifed platform
const Asura_String& Tools_TT_CreateResourceChunkName( Asura_String& xReturnString,const Asura_String& xOutputFileName,const Tools_Platform_Type eTargetPlatform,const bool bPrefixName = false );

// predicate class which returns true if a texture with the specified ID has been tiled in the TTL provided at construction
class IsTextureTiled
{
public:
	IsTextureTiled( const Tools_TT_TiledTextureList* pxTTL );

	const bool operator()( const int iTextureID ) const
	{
		return m_pxTTL->GetTextureFromID( iTextureID )->IsTiled();
	}

	const bool operator()( const char* szTextureName ) const
	{
		return this->operator()( Asura_Texture_Management::GetTextureIndex( szTextureName ) );
	}

private:
	const Tools_TT_TiledTextureList* m_pxTTL;
};


#endif // __TOOLS_TEXTURETILER_TTL_H_
