/*********************************************************************//**
*	 Textures Library
*	\author Michal Jirous
*	\date 10.10.2008
*	\file textureslib.h
*	\brief Knihovna textur. Dokaze nacitat nejruznejsi formaty obrazku
*			a vytvaret z nich textury. Dale umoznuje nacitat soubory
*			typu WAD3, coz je databaze textur, kde kazda textura ma
*			identifikacni jmeno.
**********************************************************************/

#ifndef __TEXTURES_LIB_H__
#define __TEXTURES_LIB_H__

#include <map>
#include <SDL/SDL_opengl.h>
#include <string>
#include <SDL/SDL.h>


/** @brief Hlavicka souboru WAD3 */
struct Wad3Header
{
	char	identification[4];	/*!< @brief Identifikace WAD3 souboru. Hodnota musi byt WAD3, jinak se soubor nenacte */
	int	numlumps;		/*!< @brief Pocet zaznamu v archivu (LUMPS) */
	int	infotableofs;		/*!< @brief Pocet bytu od zacatku souboru k seznamu zaznamu */
};

/** @brief Informace o ulozenem obrazku */
struct Wad3FileInfo
{
	int	filepos;				/*!< offset k datum od zacatku souboru*/
	int	disksize;				/*!< realna velikost bez komprese*/
	int	size;					/*!< vzdy uncompressed, jinak se textura nenacte*/
	char	type;					/*!< typ obrazku*/
	char	compression;				/*!< typ komprese*/
	char	pad1,					/*!< padding*/
		pad2;					/*!< padding*/
	char	name[16];				/*!< must be null terminated*/
};

/**Hlavicka jedne textury*/
struct Wad3MipMap
{
	char		name[16];		/*!< LUMP name*/
	Uint32		width,			/*!< zakaldni velikosti textury*/
			height;			/*!< zakaldni velikosti textury*/
	Uint32		offsets[4];		/*!< four mip maps stored */
};

/** @name Zakladni vlastnosti kompilace textur. 
*@{ */
const int DEFAULT_WRAP = GL_REPEAT;				/*!< @brief Chovani textur za hranicemi texturovacich souradnic */
const int DEFAULT_MIN_COMPILATION = GL_LINEAR_MIPMAP_LINEAR;	/*!< @brief Vlastnosti interpolace texelu.*/
const int DEFAULT_MAG_COMPILATION = GL_LINEAR;			/*!< @brief Vlastnosti interpolace texelu.*/
//@}

/**  @brief Typy obrazku, ktere knihovna umi nacitat.
*/
enum picture_types
{
	PICTURE_DEFAULT = 0,	/*!< Pocatecni nastaveni. Vhodne pro obrazky typu jpg, png, bmp atd. */
	PICTURE_WAD3,		/*!< Status obrazku typu WAD3. */
	PICTURE_RAW,		/*!< Status obrazku ve formatu RAW. */
	PICTURE_TGA		/*!< Pro obrazky typu TGA (SDL_image potrebuje pri nacitani pres RWops vedet, ze jde o tga. */
};

/** @brief Objekt udrzuje vsechny potrebne informace o obrazku, ktere jsou potreba k nacitani a praci s pixely.
*
* 
*/
struct ImageInfo 
{
	unsigned int 	width,	/*!< @brief Vyska obrazku. */
 			height,	/*!< @brief Sirka obrazku. */
 			bpp;	/*!< @brief Bitova hloubka obrazku. */
	unsigned int 	fileoffset, 	/*!< @brief Offset obrazku od zacatku souboru. */
		type;		/*!< @brief Typ obrazku z vyctu #picture_types. */
	
	void setFilename( std::string sFilename ); 	/*!< @brief Funkce nastavi cestu k souboru obrazku. */
	std::string getFilename();			/*!< @brief Funkce ziska cestu k souboru obrazku. */
	
	/*!  @brief Konstruktor. Inicializuje promenne tohoto objektu. */
	ImageInfo()		
	{
		width = height = bpp = 0;
		fileoffset = 0;
		type = PICTURE_DEFAULT;
	}	
//private:
	std::string filename;			/* Cesta k souboru obrazku */
};



/** @brief Dodatecne imformace o vlastnostech textury.
*/
struct AdditionalTexInfo
{
	/*! @name Vlastnosti textury za hranici obrazku. Promenne muze nabyvat hodnot: GL_REPEAT, GL_CLAMP, GL_CLAMP_TO_ENGE.
	* @{*/ 
	int 	wrap_s,	/*!< @brief 	Ve smeru S. */
		wrap_t;	/*!< @brief	Ve smeru T. */	
	/**@}*/

	/** @name zpusob vypoctu barvy pixelu dle texturovacich souradnic
	* @{	
	*/
	/** @brief Zvetsovaci funkce. 
		*	Funkce pouzita pri zvetsovani. Moznosti: (GL_LINEAR, GL_NEAREST) */
	int 	compilationTypeMag,
		
	/** @brief Zmensovaci funkce. 
	* Funkce pouzita pri zmensovani. Moznosti: (GL_LINEAR, GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST,
	* GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR).*/		
		compilationTypeMin;		
	// @}
	AdditionalTexInfo();
};




/** @brief Objekt textury.
*	Tento objekt slouzi k ukladani informaci o texture a obrazku, ze ktereho textura vznikla. Neukladaji se pouze samotne pixely.
*/
struct TextureElement
{
	ImageInfo image;		/*!< @brief Parametry obrazku. */
	std::string key;		/*!< @brief Identifikacni klic textury */
	unsigned int texture_id;	/*!< @brief Identifikacni cislo zkompilovane textury v OpenGL */
	unsigned int shared_index;	/*!< @brief Pocet aktualich uziti teto textury */
	AdditionalTexInfo *additInfo;	/*!< @brief Nestandardni kompilovaci informace o texture */
	TextureElement *animNext;	/*!< @brief Nasledujici textura v animaci */
	TextureElement *switcher;	/*!< @brief prvni prvek druhe animace pri prepnuti stavu */
	TextureElement();		/*!< @brief Konstruktor inicializuje vsechny promenne */
	~TextureElement();		/*!< Destruktor maze dodatecne informace #additInfo, pokud byly definovany */
	TextureElement( std::string key );	/*!< @brief Konstruktor s nastavenim identifikacni promenne @param key Identifikacni retezec */
	void init();			/*!<  @brief Inicializace promennych */
	unsigned char material;
};


enum
{
	MATERIAL_CONCRETE = 0,
	MATERIAL_SAND,
	MATERIAL_METAL,
	MATERIAL_WOOD,
	MATERIAL_FLESH,
	MATERIAL_GLASS,
	MATERIAL_SNOW,

	TEXTURE_MATERIALS_COUNT
};


const char TEXTURE_MATERIALS[TEXTURE_MATERIALS_COUNT] = {
	'c',	//Concrete
	's',	//Sand
	'm',	//Metal
	'w',	//Wood
	'f',	//flesh
	'g',	//glass
	'n'		//snow
};




/** @brief Hlavni trida, ktera ovlada nacitani textur.
*	Trida TexturesLibrary obsahuje deklarace a definice promennych a funkci, ktere se pouzivaji k nacitani pixelu z dat obrazku
*	a nasledneho zkompilovani do textury. 
*
*/
class TexturesLibrary
{
	std::map<std::string,TextureElement> m_TexturesMap;		//mapa textur ( klic - texture )
	void checkAnimRelations( TextureElement *element );		//spojuje textury do animace
	void relateNext( TextureElement *current, TextureElement *next );
	void setStartingTextures( TextureElement *current,std::string sSwitcherPrefix, std::string sNextPrefix, std::string sRemainPart );
	void setBeforeTexture( TextureElement *current, std::string sBeforeTexture );
	void setNextTexture( TextureElement *current, std::string sNextTexture );
	
	TextureElement * loadTexture( TextureElement *element );	//naste texturu dle parametru v TextureElement
	
	bool proceedTextureAnimationLoad( TextureElement *element );
	void proceedTextureAnimationUnload( TextureElement *element );

    unsigned char* loadPixelsFromRAW( SDL_RWops *rw, ImageInfo &image );	
    unsigned char* loadPixelsFromWAD3MipMap( SDL_RWops *rw, ImageInfo &image );		//nacte pixely z WAD3 dle jmena jedne z LUMP
    unsigned char* loadPixelsFromDefaultType( SDL_RWops *rw, ImageInfo &image );
	void printError( std::string sError );



	
public:
	int determineMaterialType( std::string texture_name );
	TexturesLibrary();
	bool loadWAD3( const char* filename );				/*!< @brief Nacte vsechny LUMPS (nazvy obrazku) z WAD3 souboru do databaze. */
	void swapRows( unsigned char* pixels, ImageInfo &image ); 	/*!< @brief Prehodi radky ve vertikalni ose. */
	
	/** @name Nacitani pixelu.
	* @{*/
	unsigned char* loadPixels( TextureElement *element );					/*!< @brief Nacte pixely dle parametru typu TextureElement. */
	unsigned char* loadPixels( ImageInfo &image );						/*!< @brief Nacte pixely dle parametru typu ImageInfo. */
	unsigned char* loadPixelsFromSDLSurface( SDL_Surface *surface, ImageInfo &image );	/*!< @brief Nacte pixely ze SDL_Surface. */
	unsigned char* loadPixelsRW( SDL_RWops *rw,  ImageInfo &image );			/*!< @brief Nacte pixely dle SDL_RWops. */
       	//@}
        
	/** @name Ziskani ukazatele na element v databazi. 
	* @{*/
	TextureElement *getTexture( std::string key );		/*!< @brief Vyhledavani podle klice. */
	TextureElement *getTexture( unsigned int texture_id );	/*!< @brief Vyhledavani podle OpenGL ID textury. */
	//@}

	TextureElement *createTexture( TextureElement *element, unsigned char* pixels );	/*!< @brief Vytvoreni textury. */

	/** @name Pouziti textury.
	* @{*/	
	TextureElement *applyTexture( std::string key );				/*!< @brief Nacteni textury a prirazenych animaci, pokud je potreba. */
	TextureElement *applyTexture( TextureElement *element );			/*!< @brief Nacteni textury a prirazenych animaci, pokud je potreba. */
	TextureElement *applyTexture( SDL_RWops *rw, int type = PICTURE_DEFAULT);	/*!< @brief Nacteni textury, pokud je potreba. */
	TextureElement *applyTexture( TextureElement *current, SDL_RWops *rw);		/*!< @brief Nacteni textury, pokud je potreba. */
	//@}

	/** @name Pridani zaznamu o texture. 
	*@{ */
	TextureElement *addEntry( std::string key, bool checkForAnim = true );				/*!< @brief Prida novy zaznam dle klice. */
	TextureElement *addEntry( std::string key, std::string filename, bool checkForAnim = true );	/*!< @brief Prida novy zaznam dle klice vcetne cesty k souboru. */
	TextureElement *addEntry( std::string key, const char* filename, bool checkForAnim = true );	/*!< @brief Prida novy zaznam dle klice vcetne cesty k souboru. */
	//@}

	unsigned char* make_it_transparent( unsigned char* pixels, TextureElement *current );		/*!< @brief Provede operaci zpruhledneni vybranych pixelu. */
	
	/** @name Dodatecne parametry textury
	*@{*/
	void setTextureWraps( TextureElement *element, int wrap_s = DEFAULT_WRAP, int wrap_t = DEFAULT_WRAP );		/*!< @brief Nastavi vlastnosti WRAP. */
	void setTextureCompilations( TextureElement *element, int mag = DEFAULT_MAG_COMPILATION, int min = DEFAULT_MIN_COMPILATION );	/*!< @brief Nastavi MIN a MAG filtry. */
	//@}

	/** @name Uvolneni textury
	* @{*/
	void unloadTexture( unsigned int iTextureID );	/*!< @brief Uvolneni dle OpenGL ID textury. */
	void unloadTexture( std::string key );		/*!< @brief Uvolneni dle klice textury. */
	void unloadTexture( TextureElement *element );	/*!< @brief Uvolneni dle objektu TextureElement. */
	//@}

	/** @name Smazani prvku databaze
	* @{*/
	bool deleteTexture( TextureElement *element );	/*!< @brief Smazani dle objektu TextureElement. */
	bool deleteTexture( unsigned int texture_id );	/*!< @brief Smazani dle OpenGL ID textury. */
	bool deleteTexture( std::string key );		/*!< @brief Smazani dle klice textury. */
	void deleteAll();				/*!< @brief Smaze celou databazi textur */
	//@}

	void checkTexturesUsage();			/*!< @brief Test pouzivanosti textur */



	/* Textures shared usage pool */
	
};


struct TexturePooled
{

	TextureElement *m_pTextureElement;
	size_t reference_counter;
	TexturePooled() : m_pTextureElement(NULL), reference_counter(0) {}
	void bind();
	void switchTexture();
	void animTexture();
	void unload();
private:
	friend class TexturePool;
	void unconnect();
};

typedef std::map<std::string,TexturePooled> texturesPool_t;



class TexturePool
{
	texturesPool_t m_TexturesSharedPool;
public:
	TexturePooled *Pool_applyTexture( std::string key );				/*!< @brief Nacteni textury a prirazenych animaci, pokud je potreba. */
	//TexturePooled *Pool_applyTexture( TextureElement *element );			/*!< @brief Nacteni textury a prirazenych animaci, pokud je potreba. */
	//TexturePooled *Pool_applyTexture( SDL_RWops *rw, int type = PICTURE_DEFAULT);	/*!< @brief Nacteni textury, pokud je potreba. */
	//TexturePooled *Pool_applyTexture( TextureElement *current, SDL_RWops *rw);		/*!< @brief Nacteni textury, pokud je potreba. */

	TexturePooled *Pool_getTexture( std::string key );		/*!< @brief Vyhledavani podle klice. */
	//TextureElement *Pool_getTexture( unsigned int texture_id );	/*!< @brief Vyhledavani podle OpenGL ID textury. */

	void Pool_checkTexturesUsage();			/*!< @brief Test pouzivanosti textur */
	void Pool_destroy();


	void Pool_animNextAll();
	void Pool_switchNextAll();

};







inline void texBind( GLuint id )	{ glBindTexture( GL_TEXTURE_2D, id ); }
inline void texUnload( GLuint id )	{ glDeleteTextures( 1, &id ); }



extern TexturesLibrary textureLibrary;	/*!< @brief Objekt knihovny textur. V cele aplikaci je jedna instance dostacujici. */

#endif

