class KFWeaponAmbientEchoHandler extends Object
	within KFPawn;

enum EEchoDirection
{
    EED_Front,
    EED_Right,
    EED_Left,
    EED_Rear,
    EED_Max
};

struct native EchoSet
{
	/** Components to play events on */
    var AkComponent Components[4];

    /** Echo events to play */
    var AKEvent     Events[4];

    /** Time that echo should start playing (allows for delay) */
    var float       EchoStartTimes[4];

    /** Time that echo should stop playing (allows for delay) */
    var float       EchoStopTimes[4];

    /** Whether echo loop has been started (bool arrays aren't allowed) */
    var byte        Started[4];

    /** Whether echo loop has been stopped (bool arrays aren't allowed) */
    var byte        Stopped[4];
};

/** Array of currently-playing looping echo sounds (each set has four direction echoes) */
var array< EchoSet > EchoSets;

/**
 * Starts the chain of starting/stopping looping echo sounds for looping fire weapons
 * On fire, a set of four directional echoes is grabbed from the audio device, and each echo is given a delay time to start
 * On stop fire, each echo is given a delay time to stop, and then they are returned to the audio device
 * @param NewAmbientSound the sound that was played that we want to handle echoes for
 */
simulated function HandleEchoes(AkEvent NewAmbientSound)
{
    if ( WorldInfo.NetMode == NM_DedicatedServer )
    {
        return;
    }

    StopAllEchoes();

    if ( NewAmbientSound != None)
    {
        StartEchoSet( NewAmbientSound );
    }
}

/*
 * Create a set of four directional echoes and give them a start delay based on location within reverb volume
 */
simulated function StartEchoSet( AkEvent NewAmbientSound )
{
    local vector FrontLocation, RightLocation, LeftLocation, RearLocation, ViewLocation;
    local float EchoDistance, EchoDelay, ViewDist;
	local PlayerController LocalPC;
	local rotator ViewRotation;
	local ReverbVolume EchoVolume;
	local int EchoIdx;

	LocalPC = GetALocalPlayerController();
	if( LocalPC != none )
	{
		LocalPC.GetPlayerViewPoint( ViewLocation, ViewRotation );
	}
	else
	{
		`log(GetFuncName()@"called with no local PC!");
		return;
	}

    ViewDist = VSizeSq(ViewLocation-Location);

    class'KFReverbVolume'.static.CalculateEchoVolumeAndDistance( WorldInfo, Location, EchoVolume, EchoDistance );

    EchoSets.Add( 1 );

    // Play all 4 positional echoes if the listener is within the echo distance
    if( EchoDistance == 0 || (ViewDist < EchoDistance * EchoDistance) )
    {
        // For the purposes of placing the echo locations, we need an echo distance of at least 1.0
        if( EchoDistance < 1.0 )
        {
            EchoDistance = 1.0;
        }

	    class'KFReverbVolume'.static.CalculateEchoLocationAndDelay( EchoVolume, Location, vect(1,0,0) >> Rotation, EchoDistance, FrontLocation, EchoDelay );
	    DelayedStartEcho( EED_Front, FrontLocation, NewAmbientSound.EchoFront, WorldInfo.TimeSeconds + EchoDelay );
	    class'KFReverbVolume'.static.CalculateEchoLocationAndDelay( EchoVolume, Location, vect(0,-1,0) >> Rotation, EchoDistance, LeftLocation, EchoDelay );
	    DelayedStartEcho( EED_Left, LeftLocation, NewAmbientSound.EchoRight, WorldInfo.TimeSeconds + EchoDelay );
	    class'KFReverbVolume'.static.CalculateEchoLocationAndDelay( EchoVolume, Location, vect(0,1,0) >> Rotation, EchoDistance, RightLocation, EchoDelay );
	    DelayedStartEcho( EED_Right, RightLocation, NewAmbientSound.EchoLeft, WorldInfo.TimeSeconds + EchoDelay );
	    class'KFReverbVolume'.static.CalculateEchoLocationAndDelay( EchoVolume, Location, vect(-1,0,0) >> Rotation, EchoDistance, RearLocation, EchoDelay );
	    DelayedStartEcho( EED_Rear, RearLocation, NewAmbientSound.EchoRear, WorldInfo.TimeSeconds + EchoDelay );

        //FlushPersistentDebugLines();
        //DrawDebugSphere( FrontLocation, 8, 10, 255, 0, 0, true );
        //DrawDebugSphere( LeftLocation, 8, 10, 255, 0, 0, true );
        //DrawDebugSphere( RightLocation, 8, 10, 255, 0, 0, true );
        //DrawDebugSphere( RearLocation, 8, 10, 255, 0, 0, true );
    }
    else
    {
        // Set the echo to play based on the looping fire sound that was played
        DelayedStartEcho( EED_Front, Location, NewAmbientSound.EchoMono, WorldInfo.TimeSeconds + EchoDelay );

		// "stop" the rest so that echo set is removed in tick
		EchoIdx = EchoSets.Length - 1;
		EchoSets[EchoIdx].Stopped[EED_Left] = 1;
		EchoSets[EchoIdx].Stopped[EED_Right] = 1;
		EchoSets[EchoIdx].Stopped[EED_Rear] = 1;
    }

    if( EchoDelay == 0 )
    {
    	// possibly start echoes now instead of next frame
    	TickEchoes();
    }
}

/*
 * Sets up single echo and gives it an AkComponent from the audio device
 */
simulated function DelayedStartEcho( EEchoDirection EDir, vector EchoLocation, AkEvent EchoSound, float EchoStartTime )
{
    local int EchoSetIndex;
    local KFPlayerController LocalKFPC;

    LocalKFPC = KFPlayerController( GetALocalPlayerController() );
    if( LocalKFPC != none )
    {
        EchoSetIndex = EchoSets.Length - 1;
        EchoSets[ EchoSetIndex ].Components[ EDir ] = LocalKFPC.GetPooledAkComponent();
        EchoSets[ EchoSetIndex ].Components[ EDir ].Location = EchoLocation;
        EchoSets[ EchoSetIndex ].Components[ EDir ].bStopWhenOwnerDestroyed = true;
        EchoSets[ EchoSetIndex ].Events[ EDir ] = EchoSound;
        EchoSets[ EchoSetIndex ].EchoStartTimes[ EDir ] = EchoStartTime;
    }
}

/*
 * Determines when all direction echoes for an echo set should stop being played
 */
simulated function StopEchoSet( optional int EchoSetIndex = -1, optional bool bStopImmediate = false )
{
    local float EchoDistance, EchoDelay;
    local ReverbVolume EchoVolume;
    local vector EchoLocation;

	if( EchoSetIndex < 0 )
	{
		EchoSetIndex = EchoSets.Length - 1;
	}

	if( EchoSetIndex < 0 || EchoSetIndex >= EchoSets.Length )
	{
	   return;
	}

	if( bStopImmediate )
	{
	    // stop echoes now instead of next frame
		StopEcho(EchoSetIndex, EED_Front);
		StopEcho(EchoSetIndex, EED_Left);
		StopEcho(EchoSetIndex, EED_Right);
		StopEcho(EchoSetIndex, EED_Rear);

        EchoSets.Remove( EchoSetIndex, 1 );
	}
	else
	{
	    class'KFReverbVolume'.static.CalculateEchoVolumeAndDistance( WorldInfo, Location, EchoVolume, EchoDistance );
	    class'KFReverbVolume'.static.CalculateEchoLocationAndDelay( EchoVolume, Location, vect(1,0,0) >> Rotation, EchoDistance, EchoLocation, EchoDelay );
	    EchoSets[ EchoSetIndex ].EchoStopTimes[ EED_Front ] = WorldInfo.TimeSeconds + EchoDelay;
	    class'KFReverbVolume'.static.CalculateEchoLocationAndDelay( EchoVolume, Location, vect(0,-1,0) >> Rotation, EchoDistance, EchoLocation, EchoDelay );
	    EchoSets[ EchoSetIndex ].EchoStopTimes[ EED_Left ] = WorldInfo.TimeSeconds + EchoDelay;
	    class'KFReverbVolume'.static.CalculateEchoLocationAndDelay( EchoVolume, Location, vect(0,1,0) >> Rotation, EchoDistance, EchoLocation, EchoDelay );
	    EchoSets[ EchoSetIndex ].EchoStopTimes[ EED_Right ] = WorldInfo.TimeSeconds + EchoDelay;
	    class'KFReverbVolume'.static.CalculateEchoLocationAndDelay( EchoVolume, Location, vect(-1,0,0) >> Rotation, EchoDistance, EchoLocation, EchoDelay );
	    EchoSets[ EchoSetIndex ].EchoStopTimes[ EED_Rear ] = WorldInfo.TimeSeconds + EchoDelay;
	}
}

/*
 * Determines stop time for all echoes in all echo sets
 @param bImmediate - tells echoes to stop immediately, otherwise they will stop after a delay determined by the reverb volume
 */
simulated function StopAllEchoes( optional bool bImmediate )
{
    local int i;

    for( i = EchoSets.Length - 1; i >= 0; i-- )
    {
        // stop/delay stop all echo sets that haven't been scheduled to stop, in case there are more than one (could happen due to flaky server connection)
        if( bImmediate || EchoSets[i].EchoStopTimes[EED_Front] == 0 )
        {
            StopEchoSet( i, bImmediate );
        }
    }
}

/*
 * Updates echo sets (starts them after start delay, stops them after stop delay, cleans them up when finished)
 */
simulated function TickEchoes()
{
	local int i, j, NumStoppedEchoes;
    local float WorldTimeSeconds;

    WorldTimeSeconds = WorldInfo.TimeSeconds;

    for( i = 0; i < EchoSets.Length; ++i )
    {
    	NumStoppedEchoes = 0;
        for( j = 0; j < EED_Max; ++j )
        {
            if( EchoSets[i].Stopped[j] == 0 )
            {
                if( EchoSets[i].Started[j] == 0 && EchoSets[i].EchoStartTimes[j] > 0 && WorldTimeSeconds > EchoSets[i].EchoStartTimes[j] )
                {
                    StartEcho( i , j );
                }
                else if( EchoSets[i].Started[j] > 0 && EchoSets[i].EchoStopTimes[j] > 0 && WorldTimeSeconds > EchoSets[i].EchoStopTimes[j] )
                {
                    StopEcho( i , j );
                    ++NumStoppedEchoes;
                }
            }
            else
            {
            	++NumStoppedEchoes;
            }
        }

        // if all echoes in an echo set have been stopped, then the set is finished -- remove it
        if( NumStoppedEchoes == EED_Max )
        {
        	EchoSets.Remove( i--, 1 );
        }
    }
}

/*
 * Plays a single echo
 */
simulated function StartEcho( int EchoSetIndex, int EchoIndex )
{
    EchoSets[ EchoSetIndex ].Started[ EchoIndex ] = 1;
    EchoSets[ EchoSetIndex ].Components[ EchoIndex ].PlayEvent( EchoSets[EchoSetIndex].Events[EchoIndex] );
}

/*
 * Stops a single echo
 */
simulated function StopEcho( int EchoSetIndex, int EchoIndex )
{
    EchoSets[ EchoSetIndex ].Stopped[ EchoIndex ] = 1;
    if( EchoSets[EchoSetIndex].Components[EchoIndex] != none )
    {
        EchoSets[ EchoSetIndex ].Components[ EchoIndex ].StopEvents();
    }
}
