#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "soundent.h"

LINK_ENTITY_TO_CLASS(soundent, CSoundEnt);

CSoundEnt *pSoundEnt;

void CSound::Clear(void)
{
	m_vecOrigin = g_vecZero;
	m_iType = 0;
	m_iVolume = 0;
	m_flExpireTime = 0;
	m_iNext = SOUNDLIST_EMPTY;
	m_iNextAudible = 0;
}

void CSound::Reset(void)
{
	m_vecOrigin = g_vecZero;
	m_iType = 0;
	m_iVolume = 0;
	m_iNext = SOUNDLIST_EMPTY;
}

BOOL CSound::FIsSound(void)
{
	if (m_iType & (bits_SOUND_COMBAT | bits_SOUND_WORLD | bits_SOUND_PLAYER | bits_SOUND_DANGER))
		return TRUE;

	return FALSE;
}

BOOL CSound::FIsScent(void)
{
	if (m_iType & (bits_SOUND_CARCASS | bits_SOUND_MEAT | bits_SOUND_GARBAGE))
		return TRUE;

	return FALSE;
}

void CSoundEnt::Spawn(void)
{
	pev->solid = SOLID_NOT;
	Initialize();

	pev->nextthink = gpGlobals->time + 1;
}

void CSoundEnt::Think(void)
{
	pev->nextthink = gpGlobals->time + 0.3;

	int iPreviousSound = SOUNDLIST_EMPTY;
	int iSound = m_iActiveSound;

	while (iSound != SOUNDLIST_EMPTY)
	{
		if (m_SoundPool[iSound].m_flExpireTime <= gpGlobals->time && m_SoundPool[iSound].m_flExpireTime != SOUND_NEVER_EXPIRE)
		{
			int iNext = m_SoundPool[iSound].m_iNext;
			FreeSound(iSound, iPreviousSound);
			iSound = iNext;
		}
		else
		{
			iPreviousSound = iSound;
			iSound = m_SoundPool[iSound].m_iNext;
		}
	}

	if (m_fShowReport)
	{
		ALERT(at_aiconsole, "Soundlist: %d / %d  (%d)\n", ISoundsInList(SOUNDLISTTYPE_ACTIVE), ISoundsInList(SOUNDLISTTYPE_FREE), ISoundsInList(SOUNDLISTTYPE_ACTIVE) - m_cLastActiveSounds);
		m_cLastActiveSounds = ISoundsInList(SOUNDLISTTYPE_ACTIVE);
	}
}

void CSoundEnt::Precache(void)
{
}

void CSoundEnt::FreeSound(int iSound, int iPrevious)
{
	if (!pSoundEnt)
		return;

	if (iPrevious != SOUNDLIST_EMPTY)
		pSoundEnt->m_SoundPool[iPrevious].m_iNext = pSoundEnt->m_SoundPool[iSound].m_iNext;
	else
		pSoundEnt->m_iActiveSound = pSoundEnt->m_SoundPool [iSound].m_iNext;

	pSoundEnt->m_SoundPool[iSound].m_iNext = pSoundEnt->m_iFreeSound;
	pSoundEnt->m_iFreeSound = iSound;
}

int CSoundEnt::IAllocSound(void)
{
	if (m_iFreeSound == SOUNDLIST_EMPTY)
	{
		ALERT(at_console, "Free Sound List is full!\n");
		return SOUNDLIST_EMPTY;
	}

	int iNewSound = m_iFreeSound;
	m_iFreeSound = m_SoundPool[m_iFreeSound].m_iNext;
	m_SoundPool[iNewSound].m_iNext = m_iActiveSound;
	m_iActiveSound = iNewSound;
	return iNewSound;
}

void CSoundEnt::InsertSound(int iType, const Vector &vecOrigin, int iVolume, float flDuration)
{
	if (!pSoundEnt)
		return;

	int iThisSound = pSoundEnt->IAllocSound();

	if (iThisSound == SOUNDLIST_EMPTY)
	{
		ALERT(at_console, "Could not AllocSound() for InsertSound() (DLL)\n");
		return;
	}

	pSoundEnt->m_SoundPool[iThisSound].m_vecOrigin = vecOrigin;
	pSoundEnt->m_SoundPool[iThisSound].m_iType = iType;
	pSoundEnt->m_SoundPool[iThisSound].m_iVolume = iVolume;
	pSoundEnt->m_SoundPool[iThisSound].m_flExpireTime = gpGlobals->time + flDuration;
}

void CSoundEnt::Initialize(void)
{
	int i = 0;
	m_cLastActiveSounds = 0;
	m_iFreeSound = 0;
	m_iActiveSound = SOUNDLIST_EMPTY;

	for (; i < MAX_WORLD_SOUNDS ; i++)
	{
		m_SoundPool[i].Clear();
		m_SoundPool[i].m_iNext = i + 1;
	}

	m_SoundPool[i - 1].m_iNext = SOUNDLIST_EMPTY;

	for (i = 0; i < gpGlobals->maxClients; i++)
	{
		int iSound = pSoundEnt->IAllocSound();

		if (iSound == SOUNDLIST_EMPTY)
		{
			ALERT(at_console, "Could not AllocSound() for Client Reserve! (DLL)\n");
			return;
		}

		pSoundEnt->m_SoundPool[iSound].m_flExpireTime = SOUND_NEVER_EXPIRE;
	}

	if (CVAR_GET_FLOAT("displaysoundlist") == 1)
		m_fShowReport = TRUE;
	else
		m_fShowReport = FALSE;
}

int CSoundEnt::ISoundsInList(int iListType)
{
	int iThisSound = 0;

	if (iListType == SOUNDLISTTYPE_FREE)
		iThisSound = m_iFreeSound;
	else if (iListType == SOUNDLISTTYPE_ACTIVE)
		iThisSound = m_iActiveSound;
	else
		ALERT(at_console, "Unknown Sound List Type!\n");

	if (iThisSound == SOUNDLIST_EMPTY)
		return 0;

	int i = 0;

	while (iThisSound != SOUNDLIST_EMPTY)
	{
		i++;
		iThisSound = m_SoundPool[iThisSound].m_iNext;
	}

	return i;
}

int CSoundEnt::ActiveList(void)
{
	if (!pSoundEnt)
		return SOUNDLIST_EMPTY;

	return pSoundEnt->m_iActiveSound;
}

int CSoundEnt::FreeList(void)
{
	if (!pSoundEnt)
		return SOUNDLIST_EMPTY;

	return pSoundEnt->m_iFreeSound;
}

CSound *CSoundEnt::SoundPointerForIndex(int iIndex)
{
	if (!pSoundEnt)
		return NULL;

	if (iIndex > (MAX_WORLD_SOUNDS - 1))
	{
		ALERT(at_console, "SoundPointerForIndex() - Index too large!\n");
		return NULL;
	}

	if (iIndex < 0)
	{
		ALERT(at_console, "SoundPointerForIndex() - Index < 0!\n");
		return NULL;
	}

	return &pSoundEnt->m_SoundPool[iIndex];
}

int CSoundEnt::ClientSoundIndex(edict_t *pClient)
{
	int iReturn = ENTINDEX(pClient) - 1;

#ifdef _DEBUG
	if (iReturn < 0 || iReturn > gpGlobals->maxClients)
		ALERT(at_console, "** ClientSoundIndex returning a bogus value! **\n");
#endif

	return iReturn;
}