/***************************************************************************
 *
 * crowd.c
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

/*
 * includes
 */

#include "ult_64.h"
#include "generic.h"
#include "define.h"
#include "ml.h"
#include "main.h"
#include "sfx.h"
#include "anim.h"
//#include "softmix.h"
//#include "decoder.h"
#include "dma.h"
#include "memory.h"
#include "file.h"
#include "crowd.h"
#include "audio.h"
#include "adpcm.h"
#include "teamdata.h"
#include "shell.h"
#include "ult_64.h"
#include "resman.h"
#include "sfx.h"
#include "seq.h"
#include "stats.h"

#include "misc.h"

#include "audcrowd.h"


/*
 * defines
 */

#define CAPACITY_LEVEL 60

#define FRAME_RELAX1			0
#define FRAME_RELAX2			1
#define FRAME_CLAP1				2
#define FRAME_CLAP2				3
#define FRAME_CLAP3				4
#define FRAME_FREETHROW1	5
#define FRAME_FREETHROW2	6
#define FRAME_FREETHROW3	7
#define FRAME_COUNT				8

#define ACTION_RELAX			0
#define ACTION_CLAP				1
#define ACTION_FREETHROW	2


#define COMP_BLOCKSIZE		34
#define DECOMP_BLOCKSIZE	64
#define BLOCKS_TO_FETCH		(CROWD_BUFSIZE/DECOMP_BLOCKSIZE)
#define BYTES_TO_FETCH		(BLOCKS_TO_FETCH*COMP_BLOCKSIZE)
#define SAVE_STATE_BLOCK	((CROWD_BUFSIZE-CROWD_MIX_WINDOW)/DECOMP_BLOCKSIZE)

#define MAX_CROWD_SAMPLES	16

#define CROWD_QUEUE_SIZE	2048
#define CROWD_BUFSIZE		8192
#define CROWD_MIX_WINDOW        2048
#define COMM_ILOG_BITS		13

#define STATUS_EMPTY		0

/*
 * typedefs
 */

/*
 * structures
 */

typedef struct		_crowdSample
{
	unsigned long	base;
	unsigned long	len;
} crowdSample;


// Externs...

extern SB
	asb__gRoundMatch[4][4];


/*
 * globals
 */

int			crowdLastVol;

signed short		crowdDecompBuffer[DECOMP_BLOCKSIZE];

int			crowdBufSpooling[2];
int			crowdNeedsDecompress[2];
int			crowdFirstBlock[2];
int			crowdEnable=0;
int			crowdVolume=0;

crowdSample		crowdSamples[MAX_CROWD_SAMPLES];

unsigned char		*crowdBuffers[2];
unsigned char		*crowdMixBuf;
unsigned char		*crowdTailBuf;
struct sfxQueue		*crowdAudioQueue;

int			crowdDmaChannel;
int			crowdSpooling;

int			crowdCurrentSample;
int			crowdNextSample;
int			crowdSampleBase;
int			crowdSampleLen;
int			crowdSampleOffset;
int			crowdCurrentBuffer;
int			crowdCurrentOffset;
int			crowdNumSamples;
ADPCM_STATE_T		crowdStates[2];


SCrowdAnim
	CrowdAnims[3];

s32
	CrowdAttendance;


static s32
	ClapTime;

static u8
	TeamRatings[] =
	{
		 6, // Atlanta Hawks
		 7, // Boston Celtics
		 4, // Charlotte Hornets
		 5, // Chicago Bulls
		 4, // Cleveland Cavaliers
		 3, // Dallas Mavericks
		 3, // Denver Nuggets
		 5, // Detroit Pistons
		 2, // Golden State Warriors
		 8, // Houston Rockets
		10, // Indiana Pacers
		 1, // LA Clippers
		10, // LA Lakers
		 9, // Miami Heat
		 5, // Milwaukee Bucks
		 7, // Minnesota Timberwolves
		 6, // New Jersey Nets
		10, // New York Knicks
		 7, // Orlando Magic
		 9, // Philadelphia 76ers
		 6, // Phoenix Suns
		10, // Portland Trailblazers
		 8, // Sacramento Kings
		 7, // San Antonio Spurs
		 8, // Seattle Supersonics
		 4, // Toronto Raptors
		10, // Utah Jazz
		 1, // Vancouver Grizzlies
		 3, // Washington Wizards
	};


/*
 * function prototypes
 */


/*
 * code
 */


/********************************************************************/

static void SetAnim( const s32 team, const u8 action )
{
	static u8
		frames[3] = { FRAME_RELAX1, FRAME_CLAP1, FRAME_FREETHROW1 };
	SCrowdAnim
		*anim;

	anim = &CrowdAnims[team];

	if (anim->Action != action)
	{
		anim->Action = action;
		anim->Frame = frames[action];
		anim->Wait = 0;
		anim->Dir = 1;
	}

	if (team != CROWD_MAIN)
	{
		if (team == CROWD_HOME)
			SetAnim( CROWD_MAIN, action );

		if (action != ACTION_RELAX)
		{
			if (CrowdAnims[1-team].Action == action)
				SetAnim( 1-team, ACTION_RELAX );
		}
	}
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

static void
	*AudioBank;

static s32
	AudioId = 0,
	EffectId = 0,
	Volume = AUDIO_MAX_VOLUME>>3,
	Check = FALSE,
	ToPlay = -1;

s32
	Capacity,
	VolLevel,
	VolAlter,
	AlterTime;


static void CalcLevel()
{
	s32
		num,
		level;

	// Attendance...

	if (Capacity)
		level = ((CrowdAttendance - CAPACITY_LEVEL) * 10) / (100 - CAPACITY_LEVEL);
	else
		level = (CrowdAttendance * 10) / CAPACITY_LEVEL;


	// Quarter...

	num = pcl__gMatch->sb___mQuarter + 1;
	if (num == 2)
		level += 3;
	else if (num == 3)
		level += 7;
	else if (num >= 4)
		level += 10;


	// Score difference...

	num = pcl__gMatch->apcl_mTeamRam[0]->uw___tPoints -
				pcl__gMatch->apcl_mTeamRam[1]->uw___tPoints;
	if (num >= 15)
	{
		if (num < 20)
			level += ((20-num) * 8) / 5;
	}
	else if (num >= 10)
		level += (((num-10) * 3) / 4) + 5;
	else if (num >= 0)
		level += ((10-num) / 2) + 5;
	else if (num >= -10)
		level += (num+10);


	// Match importance...

	if (sb___gPlyRound >= 0)			// Playoffs.
	{
		level += sb___gPlyRound + 6;

		// If in last 3 games of each round, bump the match importance...

		num = sb___gPlyIndex & 15;
		if (num >= (asb__gRoundMatch[pcl__gOptions->sb___PlayoffLength][sb___gPlyRound] - 3))
			level++;
	}
	else if (pcl__gOptions->sw___SeasonMatch)			// Season.
	{
		level += 5;

		// If in last 5 games of season, bump the match importance...

		if (pcl__gMatch->apcl_mTeamRam[0]->cl___tStatSeason.uw___tGP >= (NumGamesInSeason[pcl__gOptions->sb___SeasonLength] - 5))
			level++;
	}
	else
		level += 8;			// Exhibition.


	// Momentum...

	if (mom_team == 0)
		level += momentum / 10;


	VolLevel = Volume;
	VolLevel += (Volume * level) / 50;
}


void Crowd_Init()
{
	s32
		i,
		team;

	// Home team...
	team = ErrorCheckTeamStadium( pcl__gMatch->asb__mTeam[0] );
	CrowdAttendance = TeamRatings[team] * 10;
	CrowdAttendance -= (rand() * 11) >> 15;

	// Away team...
	team = ErrorCheckTeamStadium( pcl__gMatch->asb__mTeam[1] );
	CrowdAttendance += TeamRatings[team] * 2;
	CrowdAttendance -= (rand() * 3) >> 15;

	if (CrowdAttendance < 30) CrowdAttendance = 30;
	if (CrowdAttendance > 100) CrowdAttendance = 100;

	if (sb___gPlyRound >= 0)			// Playoffs.
		CrowdAttendance = 100;

	if (PracticeMode)
		CrowdAttendance = 0;

	if (CrowdAttendance >= CAPACITY_LEVEL)
		Capacity = TRUE;
	else
		Capacity = FALSE;


	CalcLevel();


	AudioBank = Audio_LoadBank( "CROWD.BNK" );

	if (CrowdAttendance && currentProcess->status != PROCESS_FREEING)
	{
		if (Capacity)
			AudioId = Audio_SfxPlay( AudioBank, SFX_AMBBIG, VolLevel );
		else
			AudioId = Audio_SfxPlay( AudioBank, SFX_AMBSMALL, VolLevel );

		Audio_SfxRepeat( AudioId, TRUE );
	}


	for (i=0; i<3; i++)
	{
		CrowdAnims[i].Action	= ACTION_RELAX;
		CrowdAnims[i].Frame		= FRAME_RELAX1;
		CrowdAnims[i].Wait		= 0;
		CrowdAnims[i].Dir			= 1;
	}

	ClapTime = 0;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void Crowd_Deinit()
{
	if (AudioId)
		Audio_SfxStop( AudioId );

	Audio_Process();
	Audio_Process();

	Audio_FreeBank( AudioBank );
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void Crowd_Volume( const u32 volume )
{
	Volume = (volume >> 3);
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void Crowd_Animate()
{
	static s8
		waits[3] = { 7, 4, 6 };
	SCrowdAnim
		*anim;
	s32
		i;

	if (Paused)
		return;


	if (ClapTime)
		ClapTime--;


	anim = CrowdAnims;

	for (i=0; i<3; i++)
	{
		anim->Wait++;
		if (anim->Wait >= waits[anim->Action])
		{
			anim->Wait = 0;

			anim->Frame += anim->Dir;
			if (anim->Frame != FRAME_CLAP2 && anim->Frame != FRAME_FREETHROW2)
				anim->Dir = -anim->Dir;
		}

		if (anim->Action == ACTION_CLAP)
		{
			if (!ClapTime)
				SetAnim( i, ACTION_RELAX );
		}

		anim++;
	}
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void Crowd_Update()
{
	if (Paused || !CrowdAttendance)
		return;


	CalcLevel();

	Audio_SfxVolume( AudioId, VolLevel );


#if 0
	if (AudioId)
	{
		if (Check)
		{
			if (!Audio_SfxActive( AudioId ))
				AudioId = 0;
		}
		else
			Check = TRUE;
	}

	if (!AudioId)
	{
		if (ToPlay >= 0)
		{
			AudioId = Audio_SfxPlay( AudioBank, ToPlay, VolLevel );
			ToPlay = -1;
		}
		else
		{
			AudioId = Audio_SfxPlay( AudioBank, SFX_AMBBIG, VolLevel );
			Audio_SfxRepeat( AudioId, TRUE );
		}

		Check = FALSE;
	}
#endif

	if (ToPlay >= 0)
	{
		EffectId = Audio_SfxPlay( AudioBank, ToPlay, VolLevel );
		ToPlay = -1;
	}
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

static void PlaySfx( s32 index )
{
//	Audio_SfxRepeat( AudioId, FALSE );
	ToPlay = index;
}


void Crowd_React( const s32 team, const s32 type )
{
	if (team == CROWD_HOME)
	{
		switch (type)
		{
			case CROWD_MISS:
//				Audio_SfxStop( AudioId );
//				AudioId = Audio_SfxPlay( AudioBank, SFX_CHEER2 );
				PlaySfx( SFX_SMALLMISS );
				break;

			case CROWD_BLOCK:
//				Audio_SfxStop( AudioId );
//				AudioId = Audio_SfxPlay( AudioBank, SFX_CHEER2 );
				PlaySfx( SFX_SMALLCHEER );
				break;

			case CROWD_FOUL:
				PlaySfx( SFX_BOO );
				break;

			case CROWD_SHOT:
				SetAnim( team, ACTION_CLAP );
				ClapTime = (5 * ONE_SEC);
				PlaySfx( SFX_SMALLCHEER );
				break;

			case CROWD_3PT:
				SetAnim( team, ACTION_CLAP );
				ClapTime = (5 * ONE_SEC);
				PlaySfx( SFX_CHEER2 );
				break;

			case CROWD_DUNK:
				SetAnim( team, ACTION_CLAP );
				ClapTime = (5 * ONE_SEC);
				PlaySfx( SFX_BIGCHEER2 );
				break;
		}
	}
	else if (team == CROWD_AWAY)
	{
		switch (type)
		{
			case CROWD_BLOCK:
				PlaySfx( SFX_BOO );
				break;

			case CROWD_FOUL:
				PlaySfx( SFX_BOO );
				break;

			case CROWD_SHOT:
				SetAnim( team, ACTION_CLAP );
				ClapTime = (5 * ONE_SEC);
				break;

			case CROWD_3PT:
				SetAnim( team, ACTION_CLAP );
				ClapTime = (5 * ONE_SEC);
				break;

			case CROWD_DUNK:
				SetAnim( team, ACTION_CLAP );
				ClapTime = (5 * ONE_SEC);
				break;
		}
	}
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void Crowd_FreeThrow( const s32 enable )
{
	if (enable)
	{
		if (CrowdAnims[CROWD_HOME].Action != ACTION_FREETHROW)
			SetAnim( CROWD_HOME, ACTION_FREETHROW );
	}
	else
		SetAnim( CROWD_HOME, ACTION_RELAX );
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void Crowd_Pause( const s32 pause )
{
	if (pause)
	{
		if (AudioId)
		{
			Audio_SfxStop( AudioId );
			AudioId = 0;
		}

		Audio_SfxStop( EffectId );
	}
	else
	{
		if (!AudioId && CrowdAttendance)
		{
			if (Capacity)
				AudioId = Audio_SfxPlay( AudioBank, SFX_AMBBIG, VolLevel );
			else
				AudioId = Audio_SfxPlay( AudioBank, SFX_AMBSMALL, VolLevel );

			Audio_SfxRepeat( AudioId, TRUE );
		}
	}
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/
