/*********************************************************************//**
*	\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.cpp
**********************************************************************/

#include "spriteslib.h"
#include <fstream>
#include "sys_console.h"
#include "mathematic.h"
#include <sstream>
#include "textureslib.h"
#include "algebraic_ext.h"


using namespace std;
using namespace systemConsole;

SpritesLibrary spritesLibrary;



SpriteCore *SpritesLibrary::getCore( std::string filename )
{
	spritemap_t::iterator iter = m_SpritesMap.find( filename );
	if( iter != m_SpritesMap.end() )
		return &iter->second;
	return NULL;
}

bool SpritesLibrary::loadSpriteData( std::string filename, SpriteCore *pCore )
{
	if( !pCore )
		return false;

	m_SpriteFile.clearDatabase();
	m_SpriteFile.loadSprite( filename );

	if( m_SpriteFile.m_Parameters.empty() || m_SpriteFile.m_Files.empty() )
		return false;

	for( list<Parameter>::iterator iter = m_SpriteFile.m_Parameters.begin(); iter != m_SpriteFile.m_Parameters.end(); iter++ )
	{
		if( iter->name == "fps" )
			pCore->m_uiFps = atoi(iter->value.c_str());
		else if( iter->name == "draw_type" )
			pCore->m_iBlendType = atoi(iter->value.c_str() );
	}

	pCore->m_uiNumFrames = m_SpriteFile.m_Files.size();
	pCore->m_Textures = new unsigned int[ pCore->m_uiNumFrames ];

	unsigned int i = 0;
	for( list<SingleFile>::iterator iter = m_SpriteFile.m_Files.begin(); iter != m_SpriteFile.m_Files.end(); iter++ )
	{
		TextureElement element;
		element.image.setFilename( iter->filename );	//timto nastavime typ obrazku, ktery je v pameti
		element.image.filename = filename;				//data sou v souboru spritu
		element.image.fileoffset = iter->offset;		//nastavime offset
		textureLibrary.applyTexture( &element );		//nacteme data obrazku a vytvorime texturu
		pCore->m_Textures[i++] = element.texture_id;	//ulozime si ID textury
		if( !pCore->width )
		{
			pCore->width = element.image.width;
			pCore->height = element.image.height;
		}
	}
	return true;
}

/** @param sprite Objekt spritu, kam se maji informace ulozit.
*	@param filename Cesta k souboru spritu, ktery se ma nacist.
*	@param axis_type Typ osy pri vykreslovani ( AXIS_TYPE_SPECIFICATED se nezadava; pro pouziti uzivatelsky definovane osy pouzijte funkci applySprite(Sprite&,String,Axis&) ).
*	@return True, pokud se sprite v poradku nacte.
*/
bool SpritesLibrary::applySprite(Sprite &sprite, std::string filename, const int axis_type )
{
	SpriteCore *pCore = getCore( filename );
	if( !pCore )	//neni v databazi
	{
		std::pair< spritemap_t::iterator, bool> result;
		result = m_SpritesMap.insert( make_pair( filename, SpriteCore() ) );

		if( result.second )	
		{
			pCore = &result.first->second;
		}
		else	//nelze vlozit
			return false;
	}

	
	if( !pCore->m_Textures )	//sprite neni nacten
	{
		if( !loadSpriteData( SPRITES_DIRECTORY + filename, pCore ) )
			return false;
	}
	

	if( sprite.m_pCoreSprite )
		unloadSprite( sprite );

	sprite.m_pCoreSprite = pCore;
	sprite.m_iAxisType = axis_type;
	sprite.reset();

	pCore->m_uiUsage++;
	
	if( sprite.m_iAxisType < AXIS_TYPE_SPECIFICATED_TEXTURE_ORIGINAL )	//pokud se sprite nema roztahovat, tak vytvarime Display List
	{
		createDList( pCore );
		pCore->m_uiDListUsage++;	//pocet uzivatelu tohoto display listu
	}

	return true;
}

/** @param pCore Jadro spritu, ze ktereho se ma display list vytvorit. */
void SpritesLibrary::createDList( SpriteCore *pCore )
{
	if( !pCore )
		return;

	pCore->m_uiDListIndex = glGenLists( 1 );	//vygenerujeme display list

	float halfw = pCore->width / 2.0f;
	float halfh = pCore->height / 2.0f;

	glNewList( pCore->m_uiDListIndex, GL_COMPILE );
		glBegin( GL_QUADS );
			glTexCoord2i( 0,0 );	glVertex3f( -halfw, 0.0f, -halfh );
			glTexCoord2i( 0,1 );	glVertex3f( -halfw, 0.0f, halfh );
			glTexCoord2i( 1,1 );	glVertex3f( halfw, 0.0f, halfh );
			glTexCoord2i( 1,0 );	glVertex3f( halfw, 0.0f, -halfh );
		glEnd();
	glEndList();

}

void SpritesLibrary::checkUsage()
{
	if( !m_SpritesMap.empty() )
	{
		for( spritemap_t::iterator iter = m_SpritesMap.begin(); iter != m_SpritesMap.end(); iter++ )
		{
			if( iter->second.m_uiUsage == 0 )	//testujeme, jestli se ma smazat cely sprite
			{
				deleteSpriteData( &iter->second );
				continue;
			}

			if( iter->second.m_uiDListUsage == 0 && iter->second.m_uiDListIndex )	//pak otestujeme, jestli se ma smazat alespon display list
				deleteSpriteDList( &iter->second );
			
		}
	}
}

/** @param pCore Jadro spritu, jehoz data se maji smazat. */
void SpritesLibrary::deleteSpriteData( SpriteCore *pCore )
{
	if( pCore )
	{
		glDeleteTextures( pCore->m_uiNumFrames, pCore->m_Textures );
		delete [] pCore->m_Textures;
		if( pCore->m_uiDListIndex )
			deleteSpriteDList( pCore );
		pCore->clear();
	}

}

/** @param pCore Jadro spritu v nemz se ma smazat dsplay list. */
void SpritesLibrary::deleteSpriteDList( SpriteCore *pCore )
{
	if( pCore )
	{
		glDeleteLists( pCore->m_uiDListIndex, 1 );
		pCore->m_uiDListIndex = 0;	
	}
}

/** @param sprite Sprite, ktery ma byt smazan. */
void SpritesLibrary::unloadSprite( Sprite &sprite )
{
	if( sprite.m_pCoreSprite )
	{
		if( sprite.m_iAxisType < AXIS_TYPE_SPECIFICATED_TEXTURE_ORIGINAL && sprite.m_pCoreSprite->m_uiDListUsage )
			sprite.m_pCoreSprite->m_uiDListUsage--;
		if( sprite.m_pCoreSprite->m_uiUsage )
			sprite.m_pCoreSprite->m_uiUsage--;
			
		sprite.m_pCoreSprite = NULL;
	}
}

void SpritesLibrary::destroy()
{
	for( spritemap_t::iterator iter = m_SpritesMap.begin(); iter != m_SpritesMap.end(); iter++ )
	{
		deleteSpriteData( &iter->second );
	}
	m_SpritesMap.clear();
}
