/*********************************************************************//**
*	\brief Sprites Library.
*	Trida implementuje knihovnu spritu, umoznuje jejich sdileni
*	mezi objekty, nacitani, vykreslovani. Primarne se stara o databazi
*	spritu, ktere jsou ulozeny pod klicem, cimz je cesta k souboru spritu.
*	Knihovna umi sprite nacist ze souboru s pomoci komponenty SpriteFile.
*	O vykreslovani a aktualizaci v case se stara objekt Sprite, ktery
*	je urcen pro uzivatele spritu.
*
*	\author Michal Jirous
*	\date 22.1.2008
*	\file spriteslib.h
**********************************************************************/

#ifndef __SPRITESLIB_H__
#define __SPRITESLIB_H__

#include <map>
#include <string>
#include "algebraic.h"

/** @brief Typy vykreslovani spritu. */
enum sprites_draw_types
{
	NORMAL = 0,		/*!< @brief Standardni vykresleni bez specialnich funkci. */
	ALPHA_TEST,		/*!< @brief Vykreslovani s pouzitim alfa testu. */
	BLACK_BLEND,	/*!< @brief Vykreslovani s pouzitim blendingu, kde se vypousti cerna barva. */
	ALPHA_BLEND		/*!< @brief Vykreslovani s pouzitim blendingu, kde se porovnava alfa slozka pixelu. */
};

/** @brief Typy os spritu, ktere primo ovlivnuji jeho orientaci v prostoru. */
enum sprite_axes
{
	AXIS_TYPE_NO_AXIS = 0,	/*!< @brief Sprite bez osy = otoceni vzdy kolmo ke kamere. */
	AXIS_TYPE_X_AXIS,		/*!< @brief Sprite se otaci pouze podle osy X a je natocen kolmo ke kamere. */
	AXIS_TYPE_Y_AXIS,		/*!< @brief Sprite se otaci pouze podle osy Y a je natocen kolmo ke kamere. */
	AXIS_TYPE_Z_AXIS,		/*!< @brief Sprite se otaci pouze podle osy Z a je natocen kolmo ke kamere. */
	AXIS_TYPE_SPECIFICATED_TEXTURE_ORIGINAL,	/*!< @brief Sprite ma specifikovanou vlastni osu, ale neni roztazen a je natocen kolmo ke kamere. */
	AXIS_TYPE_SPECIFICATED_TEXTURE_COPY,	/*!< @brief Sprite ma specifikovanou vlastni osu, je roztazen mezi body osy a je natocen kolmo ke kamere. */
	AXIS_TYPE_SPECIFICATED_TEXTURE_EXPAND
};

const std::string SPRITES_DIRECTORY = "Sprites/";	/*!< @brief Adresar se sprity. */

struct SpriteCore;


//TODO - to algebraic
struct Axis
{
	alg::Point A,B;
	Axis( const alg::Point &a, const alg::Point &b ) : A(a), B(b) {}
	Axis(){}
};

/** @brief Objekt sprite, ktery slouzi uzivatelum k ovladani spritu. */
class Sprite
{
	friend class SpritesLibrary;
	SpriteCore *m_pCoreSprite;
	int m_iAxisType;
	float m_fCurrentFrame;
	void drawWithSpecificAxis( const alg::Vector &toCameraDirection );
	void drawFrameSelect();
	void drawPrologue();
	void drawEpilogue();
public:
	bool isWorking()	{ return m_pCoreSprite != NULL; }	/*!< @brief Funkce zjistuje, zda lze sprite prehravat. */
	Sprite() { memset(this,0,sizeof(Sprite));}				/*!< @brief Konstruktor inicializuje vsechna data. */
	bool m_bIsPlaying;	/*!< @brief Indikuje, zda se sprite prehrava. */
	bool m_bIsLooping;	/*!< @brief Indikuje, zda se sprite muze opakovat. */
	void draw( const alg::Vector &cameraPosition, const alg::Vector &targetPosition );	/*!< @brief Funkce vykresluje sprite. */
	void draw( const alg::Point &cameraPosition, const Axis &axis );	/*!< @brief Funkce vykresluje sprite. */

	void update( int elapsed_ms );	/*!< @brief Aktualizuje prehravani v zavislosti na ubehnutem case. */
	void reset();					/*!< @brief Resetuje prehravani spritu. */
	float getFrame();				/*!< @brief Zjisti aktualni snimek. */
	void setFrame( float frame );	/*!< @brief Nastavi aktualni snimek. */
	void unload();					/*!< @brief Smaze tento sprite. */
	void play();					/*!< @brief Spusti prehravani. */
	int getRenderType();			/*!< @brief Zjisti typ vykreslovani spritu. */
	float getRadius( );	/*!< @brief Zjisti polomer ohraniceni spritu. */
};

/** @brief Jadro spritu, ktere obsahuje vsechna potrebna data jako sou textury nebo display list. */
struct SpriteCore
{
	unsigned int m_uiUsage;			/*!< @brief Pocitadlo referenci tohoto spritu. */
	unsigned int m_uiNumFrames;		/*!< @brief Pocet snimku spritu. */
	unsigned int *m_Textures;		/*!< @brief Pole textur (obrazku snimku). */
	int m_uiFps;					/*!< @brief Rychlost prehravani (snimku za sekundu). */
	unsigned int m_uiDListIndex;	/*!< @brief Identifikacni cislo display listu spritu. */
	unsigned int m_uiDListUsage;	/*!< @brief Pocitadlo referenci display listu. */
	int m_iBlendType;				/*!< @brief Typ vykreslovani z #sprites_draw_types. */
	unsigned int	width,			/*!< @brief Sirka spritu. */
					height;			/*!< @brief Vyska spritu. */
	void clear()	{ memset(this,0,sizeof(SpriteCore)); }	/*!< @brief Funkce smaze a resetuje vsechna data. */
	SpriteCore() { clear(); }		/*!< @brief Konstruktor inicializuje objekt funkci clear. */
	
};

/** @brief Definice typu databaze spritu. */
typedef std::map<std::string, SpriteCore> spritemap_t;



#include "spritefile.h"

/** @brief Hlavni objekt spravujici databazi spritu. */
class SpritesLibrary
{
	SpriteFile m_SpriteFile;
	spritemap_t m_SpritesMap;
	SpriteCore *getCore( std::string filename );
	bool loadSpriteData( std::string filename, SpriteCore *pCore );
	void deleteSpriteData( SpriteCore *pCore );
	void deleteSpriteDList(SpriteCore *pCore );
	void createDList( SpriteCore *pCore );
public:
	bool applySprite( Sprite &sprite, std::string filename, const int axis_type = AXIS_TYPE_NO_AXIS );	/*!< @brief Aplikuje sprite definovany souborem. */
	void unloadSprite( Sprite &sprite );	/*!< Snizi pocitadlo referenci jadra spritu o jedna a pokud sprite vyuziva display list, tak snizi pocet referenci display listu take o jedna. */
	void checkUsage();		/*!< @brief Otestuje uziti spritu a u kazdeho, ktery se jiz nepouziva, se smazou data. */
	void destroy();			/*!< Smaze celou databazi. */
};


extern SpritesLibrary spritesLibrary;	/*!< @brief Instance tridy spritesLibrary (v aplikaci vzdy staci jedna). */

#endif /*__SPRITESLIB_H__*/

