/*********************************************************************//**
*	\brief 3D Models Library.
*	Knihovna 3D modelu se skeletalni animaci. Pracuje s knihovnou Cal3D,
*	ktera se stara o vsechny vypocty behem animace. Cal3D ale neimplementuje
*	zadne vykreslovani, protoze se snazi byt nezavisla na grafickem API, 
*	proto bylo nutne vytvorit vlastni vykreslovani. Tato operace byla
*	prevzata z ukazkoveho programu, ktery prezentuje knihovnu Cal3D.
*	Ve vysledku to probiha tak, ze Cal3D vygeneruje pixely, normaly
*	a texturovaci souradnice do pole, ktere se pomoci vertex arrays pote
*	vykresli. Krome vykreslovani je zde podle vzoroveho prikladu i nacitani
*	modelu. Hlavni funkci teto knihovny je ale sdileni modelu i kdyz jen
*	v omezene mire. Pro kazdy model existuje tzv. CoreModel, ktery obsahuje
*	staticke informace o modelu. Prave tento objekt je v knihovne vzdy pouze
*	jeden pro kazdy pouzivany model. Z neho se pak vytvareji konkretni instance,
*	jez uz nelze sdilet, protoze je kazdy naprosto jiny dle prave provadene animace.
*	I tak se ale usetri hodne pameti kvuli texturam, ktere se nactou vzdy jen jednou.
*	Knihovna umoznuje vkladat zaznamy (klice) modelu, vytvaret instance modelu,
*	vykreslovat a samozrejme i mazat.
*	
*	Krome modelu typu Cal3D umoznuje knihovna pracovat se statickymi modely typu
*	MS3D (Milkshape 3D) a animovanymi typu *.mdl, ktery pouziva Half-life 1 + vsechny jeho
*	modifikace. U obou typu je implementovana velice efektivni sdileni dat.
*	
*
*	\author Michal Jirous
*	\date 1.11.2008
*	\file modelslib.h
**********************************************************************/

#ifndef __MODELS_LIBRARY_H__
#define __MODELS_LIBRARY_H__

#include <map>
#include <string>
#include "cal3d/cal3d.h"
#include <vector>
#include "StudioModel.h"

/** Jmenny prostor knihovny modelu.
*/
namespace modelLib
{


class CAbstractCoreModel;
class ModelsLibrary;

static const char* MODEL_DIRECTORY = "models/";	/*!< @brief Adresar s modely. */
const float DEFAULT_DELAYIN = 0.3f;		/*!< @brief Zakladni cas spousteni animace. */
const float DEFAULT_DELAYOUT = 0.3f;	/*!< @brief Zakladni cas vypinani animace. */

/** @brief Abstraktni predpis elementu modelu. */
class ModelElement
{
protected:
	CAbstractCoreModel *m_pCoreModel;
public:
	/** @brief Default konstruktor. */
	ModelElement(){}
	virtual ~ModelElement(){}
	virtual void draw() = 0;	/*!< @brief Funkce pro vykresleni modelu. */
	virtual void update(float)	{}/*!< @brief Funkce pro aktualizaci dat modelu. */
	virtual void getBoundingBox( float *mins, float *maxs ){}
	std::string getFileName();
	
};


class CCal3DCoreModel;
/** @brief Konkretni instance modelu.
*	Tento objekt predstavuje vzdy jedinecny model, a proto ho neni mozne sdilet. Vytvari se z objektu CCal3DCoreModel,
*	ktery obsahuje vsechny informace o vyslednem modelu. Zde je implementovano vykreslovani a ostatni ovladaci funkce
*/
class Cal3DModelElement : public ModelElement
{
	CalModel* m_calModel;
	float m_fRenderScale;		/*!< @brief Modifikator velikosti pri vykreslovani. */
	float m_fLODLevel;			/*!< @brief Uroven detailu. */
	unsigned int m_uiCurrentAnimation;
	unsigned int *m_pTotalAnimations;	/*!< @brief Pouze ukazatel na pocet animaci. */
	int *m_pAnimations;					/*!< @brief Ukazatel na pole indexu animaci, ktery nalezi tride CCal3DCoreModel. */
	void render();
public:
	Cal3DModelElement( CCal3DCoreModel *model, unsigned int *pNumAnimations, int *pAnimations  );	/*!< @brief Konstruktor inicializuje parametru objektu. */
	void draw();					/*!< @brief Vykresleni modelu. */
	void setLOD( float lod = 1.0f );	/*!< @brief Nastaveni urovne detailu. */
	void setAnimation( unsigned int animation, float delay, float motion_blend );	/*!< @brief Nastaveni animace s opakovanim. */
	void removeAnimation( unsigned int animation, float delay );	/*!< @brief Odstrani zadanou animaci. */
	void executeAnimation( unsigned int animation, float delayin = DEFAULT_DELAYIN, float delayout = DEFAULT_DELAYOUT  );		/*!< @brief Provedeni jednorazove animace. */
	void removeExecutedAnimation( unsigned int animation );	/*!< @brief Odstrani zadanou animaci. */
	void update( float elapsetseconds );			/*!< @brief Aktualizace modelu. */
	void setRenderScale( float scale );				/*!< @brief Nastavi modifikator velikosti pri vykreslovani. */
	CalModel* getCalModel();						/*!< @brief Funkce poskytne ukazatel na data modelu. */
	~Cal3DModelElement();		/*!< @brief Desktruktor maze objekt modelu, ktery se vygeneroval v konstruktoru. */
};

#define CAL3D(x) (reinterpret_cast<Cal3DModelElement*>(x))




class CCoreHLmdl;

/** @brief Element modelu typu MDL hry Half-Life. */
class HLModelElement : public ModelElement
{
	float m_fFrame;
	unsigned int m_iSequence;
	float m_fFrameRate;
	float m_fAnimTime;
	bool m_bPlaying;
	bool m_bLooping;
	int m_iSkin, m_iGroup, m_iPart;
public:
	HLModelElement( CCoreHLmdl *pModel );	/*!< @brief Konstruktor inicializuje data. */
	bool isPlaying();						/*!< @brief Zjistuje, zda se model prehrava. */
	unsigned int getCurrentSequence();		/*!< @brief Zjistuje cislo aktualni sekvence. */
	void setFrame( float fFrame );			/*!< @brief Nastavi aktualni snimek. */
	void playSequence( unsigned int iSequence, bool loop = false );	/*!< @brief Prehraje sekvenci. */
	void draw();							/*!< @brief Vykresli model. */
	void update( float elapsetseconds );	/*!< @brief Aktualizuje model. */
	virtual void getBoundingBox( float *mins, float *maxs );
	int getGroupsCount();
	int getGroupPartsCount( int group );
	int getSkinsCount();
	void setGroupAndPart( int group, int part );
	void setSkin( int skin );
	void getSequenceInfo( float &frameRate, float &groundSpeed );
};

#define HLMDL(x) (reinterpret_cast<HLModelElement*>(x))




class CMS3DCoreModel;

/** @brief Element modelu MS3D. */
class MS3DModelElement : public ModelElement
{
	unsigned int m_DListIndex;
public:
	MS3DModelElement( unsigned int dlist_index, CMS3DCoreModel *pCore );	/*!< @brief Konsturktor inicializuje data. */
	void draw();									/*!< @brief Vykresleni modelu. */
	virtual void getBoundingBox( float *mins, float *maxs );
};

#define MS3D(x) (reinterpret_cast<MS3DModelElement*>(x))







/** @brief Typy modelu. */
enum
{
	CAL3DMODEL = 0,		/*!< @brief Model typu CAL3D. */
	MS3DMODEL,			/*!< @brief Model typu MS3D. */
	HLMDL
};

/** @brief Abstraktni trida, ktera realizuje zakladni predpis modelu vcetne	
*	zakladnich promennych a funkci.
*/
class CAbstractCoreModel
{
protected:
	std::string m_sFilename;			/*!< @brief Identifikacni klic a cesta k souboru modelu. */
	unsigned int m_uiShared_index;		/*!< @brief Ukazatel uziti daneho modelu. */
	int m_iType;						/*!< @brief Typ modelu pro identifikaci. */
	virtual void clearData() = 0;				/*!< @brief Predpis funkce pro uvolneni dat modelu. */
	void clear() { m_uiShared_index = 0; clearData(); }	/*!< @brief Funkce pro reinicializaci a smazani alokovanych dat modelu. */
	virtual bool create() = 0;						/*!< @brief Funkce nacte data modelu a provede potrebne vypocty, aby bylo mozne model pouzivat. */
public:
	CAbstractCoreModel( std::string filename );	/*!< @brief Konstruktor inicializuje zakladni promenne. */
	int getType();								/*!< @brief Vraci typ modelu. */
	void checkUsage();				/*!< @brief Kontroluje pocet pouziti tohoto modelu a pokud se rovna nule, tak smaze data modelu. */
	void unloadModel();							/*!< @brief Snizuje pocet pouziti tohoto modelu o jedna. */
	virtual ~CAbstractCoreModel() { }			/*!< @brief Predpis destruktoru. */
	std::string getFileName();
};






/** @brief Trida jadra modelu Cal3D, ktera udrzuje pocatecni stav a data modelu.	
*
*/
class CCal3DCoreModel : public CAbstractCoreModel
{
	friend class Cal3DModelElement;
	unsigned int m_uiNumAnimations;		/*!< @brief Pocet animaci modelu. */
	int *m_pAnimations;		/*!< @brief Pole animaci konkretniho modelu. */
	CalCoreModel* m_calCoreModel;		/*!< @brief Objekt knihovny Cal3D, ktery udrzuje informace o modelu. */
	float m_fRenderScale;
	bool create();
	void clearData();
public:
	CCal3DCoreModel( std::string filename );			/*!< @brief Konstruktor nastavi klic modelu a  inicializuje ostatni promenne. */
	Cal3DModelElement *applyModel();					/*!< @brief Funkce vytvari novy ModelElement. */
	~CCal3DCoreModel();								/*!< @brief Smazani vsech alokovanych dat. */
};





/** @brief Trida modelu MS3D, ktera po nacteni dat ze souboru vygeneruje a nastavi display list pro vykreslovani.
*/
class CMS3DCoreModel : public CAbstractCoreModel
{
	std::vector<unsigned int> m_Textures;
	unsigned int m_uiDListIndex;
	bool create();
	void clearData();
	float mins[3], maxs[3];
public:
	void getBoundingBox( float *mins, float *maxs );
	CMS3DCoreModel( std::string filename );		/*!< @brief Konstruktor nastavi klic modelu a inicializuje ostatni promenne. */
	MS3DModelElement *applyModel();			/*!< @brief Funkce indikuje zadost o pouziti daneho modelu. */
	~CMS3DCoreModel();				/*!< @brief Smazani vsech alokovanych dat. */
};

#define MS3DCORE(x) ((CMS3DCoreModel*)x)





/** @brief Jadro modelu mdl (Half-Life). */
class CCoreHLmdl : public CAbstractCoreModel
{
	StudioModel m_Model;
	bool m_bReady;
	bool create();
	void clearData();
public:
	CCoreHLmdl( std::string filename );	/*!< @brief Konstruktor inicializuje data. */
	HLModelElement* applyModel();		/*!< @brief Vytvoreni noveho elementu dle vnistrich dat. */
	StudioModel *getStudioModel();		/*!< @brief Bezpecne zjisteni objektu s vlastnimi daty modelu. */
	~CCoreHLmdl();						/*!< @brief Dealokace vsech dat. */
};

#define HLCORE(x) ((CCoreHLmdl*)x)





/** @brief Hlavni trida tohoto modulu, ktera obstarava praci s databazi modelu.
*	Kazdy model v databazi je identifikovan klicem (retezec), podle ktereho knihovna pozna o jaky model se jedna.
*/
class ModelsLibrary
{
	typedef std::map< std::string, CAbstractCoreModel* > modelsmap_t;
	modelsmap_t m_ModelsMap;
	CAbstractCoreModel *getCoreModel( std::string filename );
public:
	void unloadModel( std::string filename );			/*!< @brief Funkce uvolnuje model. */
	void unloadAndFreeModelElement( ModelElement *&element );
	void checkModelsUsage();							/*!< @brief Kontroluje uziti jednotlivych modelu a pokud nejsou potreba, tak je smaze. */
	void destroy();										/*!< @brief Funkce smaze celou databazi. */
	void freeModelElement( ModelElement *&element );	/*!< @brief Smaze model a hodnotu promenne nastavi na NULL. */
	MS3DModelElement *applyStaticMS3DModel( std::string filename );	/*!< @brief Nacteni a pouziti modelu typu MS3D. */
	Cal3DModelElement *applyCal3DModel( std::string filename );	/*!< @brief Nacteni a pouziti modelu typu CAL3D. */
	HLModelElement *applyHalfLifeMDL( std::string filename );	/*!< @brief Nacteni a pouziti modelu Half-Life mdl. */
};

extern ModelsLibrary modelLibrary;				/*!< @brief Instance ModelsLibrary. V projektu staci pouze jedna. */

}
#endif

