/*********************************************************************//**
*	3D WAV Sound Library.
*	Knihovna zvuku, ktera umi nacitat zvukove soubory typu WAV. Obsahuje 	
*	jednoduche struktury a funkce pro prehravani zvuku a umoznuje sdilet
*	data mezi zvukovymi elementy (zdroji zvuku ve scene), ktere maji
*	prehravat to same. Vyuziva se knihovny OpenAL, ktera je velice podobna
*	OpenGL stylem programovani.
*
*	author: Michal Jirous
*	date: 25.11.2008
*	file: soundslib.cpp
**********************************************************************/


#include "soundslib.h"
#include <iostream>
#include <AL/alut.h>

#include <vorbis/vorbisfile.h>
#include "error_handler.h"

using namespace std;
CSoundLibrary soundLibrary;

/****************************************
*
*	SOUND ELEMENT = VIRTUAL SOURCE
*
*****************************************/

SoundElement::SoundElement()
{
	memset( this, 0, sizeof( SoundElement ) );	//jak jednoducha inicializace :)
	m_iState = AL_STOPPED;
	gain = 1.0f;
	pitch = 1.0f;
	radius = 512.0f;
}

/** @param pitch Procentualni hodnota, ktera meni frekvenci zvuku (0 - any), default: 1, vice informaci v dokumentaci OpenAL. */
void SoundElement::setPitch( ALfloat pitch )
{
	this->pitch = pitch;
}

/** @param gain Udava zmenu hlasitosti zdroje (0 - any) default: 1, vice informaci v dokumentaci OpenAL. */
void SoundElement::setGain( ALfloat gain )
{
	this->gain = gain;

	if( getState() == AL_PLAYING )
	{
		//zde si muzeme byt jisti, ze source je ten spravny, protoze zvuk se prave prehrava a kdyz se neprehrava, tak to nemusime nastavovat
		alSourcef( soundLibrary.m_MySources[preferedSourceId].sourceId, AL_GAIN, gain );	
	}
}

void SoundElement::setRadius( ALfloat radius )
{
	this->radius = radius;
}

/** @param loop Nastavuje opakovani prehravan: (AL_TRUE / AL_FALSE). */
void SoundElement::setLooping( ALint loop )
{
	this->looping = loop;
}

/** 	@param x Souradnice x.
*	@param y Souradnice y.
*	@param z Souradnice z.
*/
void SoundElement::setPosition( ALfloat x, ALfloat y, ALfloat z )
{
	memcpy( position, &x, sizeof(position) );

	if( getState() == AL_PLAYING )
	{
		//zde si muzeme byt jisti, ze source je ten spravny, protoze zvuk se prave prehrava a kdyz se neprehrava, tak to nemusime nastavovat
		alSourcefv( soundLibrary.m_MySources[preferedSourceId].sourceId, AL_POSITION, position );	
	}
}

#include <math.h>
float v[3];

bool SoundElement::processRangeCheck()
{
	if( looping && !relativity )	//ukoncovat prehravani ma cenu jen u opakujicich se zvuku
	{
		
		v[0] = position[0] - soundLibrary.m_fListenerPos[0];
		v[1] = position[1] - soundLibrary.m_fListenerPos[1];
		v[2] = position[2] - soundLibrary.m_fListenerPos[2];
		float dist = sqrt(  v[0] * v[0] + v[1] * v[1] +	v[2] * v[2] );
	
		if( dist > radius )	//je moc daleko, tak ho neprehravej
			return false;
	}
	
	return true;
}


void SoundElement::update()
{
	if( shouldPlay )
	{
		bool r = processRangeCheck();
		ALint state = getState();
		if( state == AL_PLAYING && !r )
 			soundLibrary.stop( *this );
		else if( state != AL_PLAYING && r )
			soundLibrary.play( *this );
	
	
	}
}


/** 	@param x Souradnice x.
*	@param y Souradnice y.
*	@param z Souradnice z.
*/
void SoundElement::setVelocity( ALfloat x, ALfloat y, ALfloat z )
{
	memcpy( velocity, &x, sizeof(velocity) );
}


/** @param relativity Nastavi relativitu pozice tohoto zdroje vuci posluchaci ( AL_TRUE | AL_FALSE ). */
void SoundElement::setSourceRelative( ALboolean relativity )
{
	this->relativity = relativity;
}

/** @param preference Nastavi preferovani soucasneho zdroje ( AL_TRUE | AL_FALSE ). */
void SoundElement::setPreference( ALboolean preference )
{
	stayWithPreferedSource = (preference == AL_TRUE);
}

/** @param index Identifikacni cislo zdroje, ktery se ma preferovat. */
void SoundElement::setPreferedSourceId( int index )
{
	preferedSourceId = index;
}

void SoundElement::play()
{
	if( looping && !relativity )
	{
		shouldPlay = AL_TRUE;
		if( processRangeCheck() )
			soundLibrary.play( *this );
	}
	else
		soundLibrary.play( *this );

	
}

void SoundElement::playIfNotPlaying()
{
	if( getState() != AL_PLAYING )
		play();
}


void SoundElement::pause()
{
	soundLibrary.pause( *this );
	shouldPlay = AL_FALSE;	//TODO ?
}

void SoundElement::stop()
{
	soundLibrary.stop( *this );
	shouldPlay = AL_FALSE;
}

/** @return Stav prehravani zvuku: AL_INITIAL, AL_PLAYING, AL_PAUSED, AL_STOPPED. */
ALint SoundElement::getState()
{
	if( preferedSourceId >=0 && preferedSourceId < SL_MAX_SOURCES )
	{
		if( m_uiId == soundLibrary.m_MySources[preferedSourceId].lastSoundElemId )	//jsou stejny
			alGetSourcei( soundLibrary.m_MySources[preferedSourceId].sourceId, AL_SOURCE_STATE, &m_iState);
	}
	return m_iState;
}



/********************************************************
*
*	CORE SOUND
*
********************************************************/


CoreSound::CoreSound()
{
	m_uiBufferID = 0;
	m_uiSharedIndex = 0;
	m_bLooping = AL_FALSE;
}



/*********************************************************
*
*	SOUND LIBRARY CLASS
*
**********************************************************/

/** @param relativity Nastavi standardni relativitu pozice nove vytvarenych SoundElement vuci posluchaci ( AL_TRUE | AL_FALSE ). */
void CSoundLibrary::setDefaultRelativity( ALboolean relativity )
{
	if( relativity )
		m_uiParameters |= SL_RELATIVITY;
	else
		m_uiParameters &= ~SL_RELATIVITY;
}

/** @param force Nastavi preferovani zdroju pro nove vytvarene SoundElement ( AL_TRUE | AL_FALSE ). */
void CSoundLibrary::setDefaultForceSourceUse( ALboolean force )
{
	if( force )
		m_uiParameters |= SL_FORCE_SOURCE;
	else
		m_uiParameters &= ~SL_FORCE_SOURCE;
}

/** @param Id Standardni identifikacni cislo zdroje, ktery se ma preferovat pro nove vytvarene SoundElement. */
void CSoundLibrary::setDefaultSourdeId( int Id )
{
	if( Id < m_iNumSources )
	{
		m_iDefaultSourceId = Id;
	}
}



void CSoundLibrary::pushAttrib()
{
	m_Stack.push( m_uiParameters );
	m_Stack.push( m_iDefaultSourceId );
}

void CSoundLibrary::popAttrib()
{
	m_iDefaultSourceId = m_Stack.top();
	m_Stack.pop();
	m_uiParameters = m_Stack.top();
	m_Stack.pop();
}


// This function loads a .ogg file into a memory buffer and returns
// the format and frequency.
bool CSoundLibrary::LoadOGG(const char *fileName, std::vector<char> &buffer, ALenum &format, ALsizei &freq)
    {
    int endian = 0;                         // 0 for Little-Endian, 1 for Big-Endian
    int bitStream;
    long bytes;
    char array[SL_BUFFER_SIZE];                // Local fixed size array
    FILE *f;

    // Open for binary reading
    f = fopen(fileName, "rb");

    if (f == NULL)
    {
		ERR_FILENOTFOUND( fileName );
        return false;
	}
    // end if

    vorbis_info *pInfo;
    OggVorbis_File oggFile;

    // Try opening the given file
    if ( ov_open( f, &oggFile, NULL, 0 ) != 0 )
	{
		ERR_OPENINGFILEFOR(fileName,"decoding...");
		return false;
	}
    // end if

    // Get some information about the OGG file
    pInfo = ov_info(&oggFile, -1);

    // Check the number of channels... always use 16-bit samples
    if (pInfo->channels == 1)
        format = AL_FORMAT_MONO16;
    else
        format = AL_FORMAT_STEREO16;
    // end if

    // The frequency of the sampling rate
    freq = pInfo->rate;

    // Keep reading until all is read
    do
    {
		// Read up to a buffer's worth of decoded sound data
		bytes = ov_read(&oggFile, array, SL_BUFFER_SIZE, endian, 2, 1, &bitStream);

		if (bytes < 0)
		{
			ov_clear(&oggFile);
			PRINT_ERROR( "Error decoding " << fileName << "..." );
			return false;
		}
		// end if

		// Append to end of buffer
		buffer.insert(buffer.end(), array, array + bytes);
    }
    while (bytes > 0);

    // Clean up!
    ov_clear(&oggFile);
	return true;
}


CoreSound *CSoundLibrary::getCoreSound( std::string filename )
{
	soundmap_t::iterator iter = m_SoundMap.find( filename );
	if( iter != m_SoundMap.end() )
		return &iter->second;
	return NULL;
}

CoreSound *CSoundLibrary::addEntry( std::string filename )
{
	if( filename.empty() )
		return NULL;

	std::pair<soundmap_t::iterator,bool> res;
	res = m_SoundMap.insert( make_pair(filename,CoreSound() ) );
	if( res.second )
		return &res.first->second;
	return NULL;
}

/** 	@param element Cilovy SoundElement, kam se maji ulozit informace o zdroji.
*	@param sFilename Cesta k souboru s daty.
*	@return False v pripade chyby, jinak true.
*/
bool CSoundLibrary::loadSound( SoundElement &element, std::string sFilename, int iSourceElementId )
{
	return loadSoundInternal( element, sFilename, iSourceElementId );
}


#define CHECK_AL_ERROR() if ( (errorType = alGetError()) != AL_NO_ERROR ) \
							{	\
								bError = true; \
								PRINT_ERROR( alGetString(errorType) ); \
							}	


bool CSoundLibrary::loadSoundInternal( SoundElement &element, std::string sFilename, int iSourceElementId )
{
	sFilename = SL_SOUND_DIRECTORY + sFilename;
	CoreSound *pCoreSound = getCoreSound( sFilename );
	
	if( !pCoreSound )
	{
		pCoreSound = addEntry( sFilename );
		if( !pCoreSound )
			return false;
	}
	
	bool bError = false; ALenum errorType;
	//musime nacist data
	if( pCoreSound->m_uiBufferID == 0 )	//data nejsou zatim nactena
	{
		size_t index = sFilename.find_last_of( '.' ) + 1;
		if( index < sFilename.length() && index > 0 )
		{
			

			vector<char> bufferData;
			ALenum format;                          // The sound data format
			ALsizei freq;                           // The frequency of the sound data

			string ext = sFilename.substr( index );
			
			if( !bError )
			{
				//OGG format
				if( ext == "ogg" )
				{
					if( !LoadOGG( sFilename.c_str(), bufferData, format, freq) )
						bError = true;

					alGenBuffers( 1, &pCoreSound->m_uiBufferID );

					if ( (errorType = alGetError()) != AL_NO_ERROR ) 
					{	
						bError = true; 
						PRINT_ERROR( alGetString(errorType) ); 
					}	

					alBufferData( pCoreSound->m_uiBufferID, format, &bufferData[0], static_cast<ALsizei>(bufferData.size()), freq );
					
					if ( (errorType = alGetError()) != AL_NO_ERROR ) 
					{	
						bError = true; 
						PRINT_ERROR( alGetString(errorType) ); 
					}	
				}

				//WAV format
				else if( ext == "wav" )
				{
					pCoreSound->m_uiBufferID = alutCreateBufferFromFile( sFilename.c_str() );
					pCoreSound->m_bLooping = AL_FALSE;

					if ( (errorType = alGetError()) != AL_NO_ERROR ) 
					{	
						bError = true; 
						PRINT_ERROR( alGetString(errorType) ); 
					}	
				}
				else
					bError = true;
			}

			//error checking
			if( bError )
			{
				if( pCoreSound->m_uiBufferID )
				{
					alDeleteBuffers( 1, &pCoreSound->m_uiBufferID );
					if ( (errorType = alGetError()) != AL_NO_ERROR ) 
					{	
						bError = true; 
						PRINT_ERROR( alGetString(errorType) ); 
					}	
					pCoreSound->m_uiBufferID = 0;
					m_SoundMap.erase( sFilename );
				}
				return false;
			}
		}

		pCoreSound->m_sKeyFilename = sFilename;
		pCoreSound->m_uiSharedIndex = 0;
	}

	//pro jistotu
	if ( (errorType = alGetError()) != AL_NO_ERROR ) 
	{	
		bError = true; 
		PRINT_ERROR( alGetString(errorType) ); 
	}	
	
	if ( bError )
		return false;

	element.pCore = pCoreSound;
	//unsigned int k = element.m_uiSourceID;

	//ALenum j = alGetError();

	//alGenSources(1, &element.m_uiSourceID );

	/*if ( (errorType = alGetError()) != AL_NO_ERROR ) 
	{	
		bError = true; 
		ALuint h = 0;
		ALsizei n = 1;
		alGenSources( n, &h );
		errorType = alGetError();
		PRINT_ERROR( alGetString(errorType) ); 
	}	*/

	element.setPosition( 0.0f, 0.0f, 0.0f );	//reset pozice
	element.setVelocity( 0.0f, 0.0f, 0.0f );
	element.setLooping( pCoreSound->m_bLooping );

	element.setSourceRelative( (m_uiParameters & SL_RELATIVITY) != NULL );
	element.setPreference( (m_uiParameters & SL_FORCE_SOURCE) != NULL );
	
	if( iSourceElementId == SL_AUTOMATIC )
		element.setPreferedSourceId( m_iDefaultSourceId );
	else
		element.setPreferedSourceId( iSourceElementId );

	element.m_uiId = m_uiSoundElementIdentificator++;

	pCoreSound->m_uiSharedIndex++;

	//alSourcei( element.m_uiSourceID, AL_BUFFER, pCoreSound->m_uiBufferID );
	//alSourcei( element.m_uiSourceID, AL_SOURCE_RELATIVE, g_DefaultRelativity );
	//alSourcef( element.m_uiSourceID, AL_ROLLOFF_FACTOR, 1.0f );
	//alSourcef( element.m_uiSourceID, AL_REFERENCE_DISTANCE, 200.0f );

	/*if ( (errorType = alGetError()) != AL_NO_ERROR ) 
	{	
		bError = true; 
		PRINT_ERROR( alGetString(errorType) ); 
	}	*/
	//pro jistotu
	
	/*if ( bError )
		return false;*/

	return true;
}

/** @param element SoundElement, ktery se ma prehravat. */
void CSoundLibrary::play( SoundElement &element )
{
	if( !element.pCore )
		return;

	int sourceIndex = element.preferedSourceId;

	bool needUpdate = false;

	if( sourceIndex >= m_iNumSources || sourceIndex < 0)
	{
		sourceIndex = determineSourceAdept();
		element.preferedSourceId = sourceIndex;
		element.stayWithPreferedSource = false;
		needUpdate = true;
	}

	
	if( !needUpdate && m_MySources[sourceIndex].lastSoundElemId != element.m_uiId )
	{
		if( !element.stayWithPreferedSource )
		{
			sourceIndex = determineSourceAdept();
			element.preferedSourceId = sourceIndex;
		}
		needUpdate = true;
	}
	
	ALuint sourceId = m_MySources[sourceIndex].sourceId;

	if( needUpdate )
	{
		alSourceStop( sourceId );

		//musime nastavit source
		alSourcei( sourceId, AL_BUFFER, element.pCore->m_uiBufferID );
		alSourcei( sourceId, AL_SOURCE_RELATIVE, element.relativity );
		alSourcef( sourceId, AL_ROLLOFF_FACTOR, 1.0f );
		alSourcef( sourceId, AL_REFERENCE_DISTANCE, element.fullradius );

		alSourcefv( sourceId, AL_VELOCITY, element.velocity );	//reset pozice
		alSourcefv( sourceId, AL_POSITION, element.position );	//reset pozice
		alSourcei ( sourceId, AL_LOOPING, element.looping );
		alSourcef ( sourceId, AL_GAIN, element.gain );
		alSourcef ( sourceId, AL_PITCH, element.pitch );
		alSourcef ( sourceId, AL_MAX_DISTANCE, element.radius );
	}

	m_MySources[sourceIndex].usage++;
	m_MySources[sourceIndex].lastSoundElemId = element.m_uiId;
	

	alSourcePlay( sourceId );


}

/** @param element SoundElement, ktery se ma zastavit a vratit na zacatek prehravani. */
void CSoundLibrary::stop( SoundElement &element )
{
	if( element.preferedSourceId < m_iNumSources && m_MySources[element.preferedSourceId].lastSoundElemId == element.m_uiId )
		alSourceStop( m_MySources[element.preferedSourceId].sourceId );
}

/** @param element SoundElement, ktery se ma pozastavit. */
void CSoundLibrary::pause( SoundElement &element )
{
	if( element.preferedSourceId < m_iNumSources && m_MySources[element.preferedSourceId].lastSoundElemId == element.m_uiId )
		alSourcePause( m_MySources[element.preferedSourceId].sourceId );
}



/** @param element Funkce snizi hodnotu reference na data zvuku dle ukazatele v tomto parametru. */
void CSoundLibrary::unloadSound( SoundElement &element )
{
	if( element.pCore )
	{
		if( element.pCore->m_uiSharedIndex )
			element.pCore->m_uiSharedIndex--;

		element.pCore = NULL;
		//smazeme reproduktor
		//alDeleteSources( 1, &element.m_uiSourceID );
		//if ( alGetError() != AL_NO_ERROR )
		//	AL_NO_ERROR; //error
		//element.m_uiSourceID = 0;
	}
}

void CSoundLibrary::checkSoundsUsage()
{
	for( soundmap_t::iterator iter = m_SoundMap.begin(); iter != m_SoundMap.end(); iter++ )
		if( iter->second.m_uiSharedIndex == 0 )
		{
			alDeleteBuffers( 1, &iter->second.m_uiBufferID );
			if ( alGetError() != AL_NO_ERROR )
				AL_NO_ERROR; //error
			iter->second.m_uiBufferID = 0;
		}
}


/** @param x Pozice v ose x.
*	@param y Pozice v ose y.
*	@param z Pozice v ose z.
*/
void CSoundLibrary::setListenerPosition( ALfloat x, ALfloat y, ALfloat z )
{
	m_fListenerPos[0] = x;
	m_fListenerPos[1] = y;
	m_fListenerPos[2] = z;
	alListenerfv( AL_POSITION, m_fListenerPos );
}

/** @param x Smer pohybu v ose x.
*	@param y Smer pohybu v ose y.
*	@param z Smer pohybu v ose z.
*/
void CSoundLibrary::setListenerVelocity( ALfloat x, ALfloat y, ALfloat z )
{
	alListener3f( AL_VELOCITY, x, y, z );
}


/** @param at_x Smer pohledu v ose x.
*	@param at_y Smer pohledu v ose y.
*	@param at_z Smer pohledu v ose z.
*	@param up_x Smer vzhuru v ose x.
*	@param up_y Smer vzhuru v ose y.
*	@param up_z Smer vzhuru v ose z.
*/
void CSoundLibrary::setListenerOrientation( ALfloat at_x, ALfloat at_y, ALfloat at_z, ALfloat up_x, ALfloat up_y, ALfloat up_z )
{
	alListenerfv( AL_ORIENTATION, &at_x );
}

/** @param values Pole tri hodnot, ktere urcuji pozici posluchace. (x, y, z). */
void CSoundLibrary::setListenerPosition( ALfloat *values )
{
	memcpy( m_fListenerPos, values, sizeof( m_fListenerPos ) );
	alListenerfv( AL_POSITION, values );
}

/** @param values Pole tri hodnot, ktere urcuji pohyb posluchace. (x, y, z). */
void CSoundLibrary::setListenerVelocity( ALfloat *values )
{
	alListenerfv( AL_VELOCITY, values );
}

/** @param values Pole sesti prvku, ktere urcuji orientaci posluchace. Prvni tri jsou smer pohledu a druhe tri je smer vzhuru. */
void CSoundLibrary::setListenerOrientation( ALfloat *values )
{
	alListenerfv( AL_ORIENTATION, values );
}

/** @param argc Pocet parametru
*	@param argv Pole retezcu, kde jsou parametry.
*/
void CSoundLibrary::init( int *argc, char *argv[] )
{
	CONSOLE_PRINT_LN( "Initializing sound library module." );
	alutInit( argc, argv );
	ALenum errorType;

	if( errorType = alGetError() )
	{
		PRINT_ERROR( alGetString(errorType) );
		return;
	}
	
	m_iNumSources = 0;

	CONSOLE_PRINT( "\tGenerating hardware sources..." );
	//now lets generate maximum sources
	for( int i = 0; i < SL_MAX_SOURCES; i++ )
	{
		alGenSources( 1, &m_MySources[i].sourceId );

		if(  errorType = alGetError() )
			break;

		m_iNumSources++;
		m_MySources[i].lastSoundElemId = 0;
		m_MySources[i].reserved = false;
		m_MySources[i].lastSoundElemId = false;
	}
	CONSOLE_PRINT_LN( "done" );
	
	m_uiParameters = 0;
	m_iDefaultSourceId = 0;
	m_uiSoundElementIdentificator = 1;


	alDistanceModel( AL_LINEAR_DISTANCE_CLAMPED );
	CONSOLE_PRINT_LN( "\tSound library is ready for use." );
}

/** @param source_elem_id Indentifikacni cislo pozadovaneho zdroje. 
*	@return true, pokud byl zdroj pridelen, jinak false.
*/
bool CSoundLibrary::reserveSource(int source_elem_id )
{
	if( source_elem_id >= m_iNumSources || source_elem_id < 0|| (m_MySources[source_elem_id].reserved))
		return false;
	m_MySources[source_elem_id].reserved = true;
	return true;
}

/** @param source_elem_id Indentifikacni cislo uvolnovaneho zdroje.
*	@return true, pokud byl zdroj uvolnen, jinak false.
*/
bool CSoundLibrary::freeSource( int source_elem_id )
{
	if( source_elem_id >= m_iNumSources || source_elem_id < 0 || !(m_MySources[source_elem_id].reserved ) )
		return false;
	m_MySources[source_elem_id].reserved = false;
	return true;
}

/** @param source_elem_id Promenna kam bude ulozeno identifikacni cislo prvniho nalezeneho volneho zdroje.
*	@return true, pokud byl zdroj nalezen, jinak false.
*/
bool CSoundLibrary::reserveFreeSource(  int *source_elem_id )
{
	if( source_elem_id )
	{
		int index = determineSourceAdept();
		if( index != -1 )
		{
			*source_elem_id = index;
			return true;
		}
	}
	return false;
}

int CSoundLibrary::determineSourceAdept()
{
	int index = -1;
	for( int i = 0; i < m_iNumSources; i++ )
	{
		if( !m_MySources[i].reserved )
		{
			ALint state;
			alGetSourcei( m_MySources[i].sourceId, AL_SOURCE_STATE, &state);
			if( state == AL_STOPPED || state == AL_INITIAL )
			{
				if( index == -1 )
					index = i;
				else if( m_MySources[i].usage < m_MySources[index].usage )
					index = i;
			}
		}
	}
	return index;	
}

void CSoundLibrary::quit()
{
	destroy();
	alutExit();
}

void CSoundLibrary::destroy()
{
	for( soundmap_t::iterator iter = m_SoundMap.begin(); iter != m_SoundMap.end(); iter++ )
		if( iter->second.m_uiBufferID )
		{
			alDeleteBuffers( 1, &iter->second.m_uiBufferID );
			if ( alGetError() != AL_NO_ERROR )
				AL_NO_ERROR; //error
			iter->second.m_uiBufferID = 0;
		}
		m_SoundMap.clear();

	for( int i = 0; i < m_iNumSources; i++ )
		alDeleteSources( 1, &m_MySources[i].sourceId );
}


#ifdef PHOBIA_TIME
	#include "globaltime.h"
	#define GETTIME() global_time::getGlobalTime()
#else
	#include <time.h>
	#ifdef WIN32
		#define GETTIME() clock()
	#else
		#define GETTIME() clock()/1000
	#endif
#endif



/** @param element SoundElement, ktery se ma prehrat.
*	@param delay_ms Doba v milisekundach, za kterou se ma zvuk prehrat.
*/
void CSoundLibrary::postSoundPlay( SoundElement *element, unsigned int delay_ms   )
{
	SoundEvent e;
	e.pElement = element;
	e.targettime = GETTIME() + delay_ms;
	m_PostSoundList.push_back( e );
}

void CSoundLibrary::clearPostSounds()
{
	m_PostSoundList.clear();
}

void CSoundLibrary::run()
{
	unsigned int currentTime = GETTIME();
	for( list<SoundEvent>::iterator iter = m_PostSoundList.begin(); iter != m_PostSoundList.end();  )
	{
		if( currentTime > (*iter).targettime )
		{
			if( (*iter).pElement )
			{
				(*iter).pElement->play();
				iter = m_PostSoundList.erase( iter );
			}
		}
		else
			++iter;
	}
}
