#include maps\mp\_utility;
#include common_scripts\utility;

CONST_COOP_USE_BAR_TIME_MIN = 1.25;
CONST_COOP_USE_BAR_TIME_MAX = 2.5;

init()
{
	if ( !level.teamBased )
		return;
	
	level.streakSupportQueueAllies = [];
	level.streakSupportQueueAxis = [];
	level.streakSupporDisabledCount = [];
	
	SetDvar( "scr_coop_util_delay", "1" );
}

/*
=============
///ScriptDocBegin
"Name: promptForStreakSupport( <streakPlayer>, <joinText>, <needSupportVO>, <buddyJoinedVO> )"
"Summary: Shows a hold-x-to-support prompt on all friendly players in team based modes.  Returns the id of the prompt so it can be stopped."
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <streakTeam>: Team that owns the scorestreak, should match streakPlayer's team if defined."
"MandatoryArg: <joinText>: Text to display for the prompt to the allies."
"OptionalArg: <splashRef>: Reference string of the splash to show."
"OptionalArg: <needSupportVO>: VO reference to play on characters telling them coop support is needed."
"OptionalArg: <buddyJoinedVO>: VO reference to play on streak player telling him a buddy has joined."
"OptionalArg: <streakPlayer>: Player that owns the scorestreak."
"OptionalArg: <joinedVO>: VO reference to play on player telling him he has joined the streak."
"Example: promptID = promptForStreakSupport( player, &"MP_OSP_JOIN", "orbitalsupport_splash", "osp_assist", "osp_buddy_joined", player, "osp_joined" );"
"SPMP: MP"
///ScriptDocEnd
=============
*/
promptForStreakSupport( streakTeam, joinText, splashRef, needSupportVO, buddyJoinedVO, streakPlayer, joinedVO )
{
	if ( !level.teamBased ) // only do coop prompts in team based modes
		return;
	
	AssertEx( !IsDefined( streakPlayer ) || streakPlayer.team == streakTeam, "promptForStreakSupport: streakPlayer's team needs to match streakTeam." );
	
	streakPromptOrigin = ( 0, 0, 0 );
	if ( IsDefined( streakPlayer ) )
		streakPromptOrigin = streakPlayer.origin;
	
	streakPrompt = Spawn( "script_model", streakPromptOrigin );
	streakPrompt Hide();
	streakPrompt.team = streakTeam;
	streakPrompt.needSupportVO = needSupportVO;
	streakPrompt.buddyJoinedVO = buddyJoinedVO;
	streakPrompt.streakPlayer = streakPlayer;
	streakPrompt.joinedVO = joinedVO;
	streakPrompt.joinText = joinText;
	streakPrompt.splashRef = splashRef;
	streakPrompt.active = false;
	streakPrompt.promptID = getUniqueStreakPromptID();
	if ( IsDefined( streakPlayer ) )
		streakPrompt DisablePlayerUse( streakPlayer );
	
	addStreakSupportPrompt( streakPrompt );
	
	return streakPrompt.promptID;
}

/*
=============
///ScriptDocBegin
"Name: stopPromptForStreakSupport( <promptID> )"
"Summary: Hides/removes the hold-x-to-support prompt for the prompt ID."
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <promptID>: Id assigned to coop prompt when started."
"Example: stopPromptForStreakSupport( id );"
"SPMP: MP"
///ScriptDocEnd
=============
*/
stopPromptForStreakSupport( promptID )
{
	if ( !level.teamBased ) // only do coop prompts in team based modes
		return;

	foreach ( streakPrompt in level.streakSupportQueueAllies )
	{
		if ( streakPrompt.promptID == promptID )
		{
			thread removeStreakSupportPrompt( streakPrompt );
			return;
		}
	}
	
	foreach ( streakPrompt in level.streakSupportQueueAxis )
	{
		if ( streakPrompt.promptID == promptID )
		{
			thread removeStreakSupportPrompt( streakPrompt );
			return;
		}
	}
}

/*
=============
///ScriptDocBegin
"Name: waittillBuddyJoinedStreak( <promptID> )"
"Summary: Will wait until someone activates the prompt and return the player."
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <promptID>: Id assigned to coop prompt when started."
"Example: buddy = waittillBuddyJoinedStreak( id );"
"SPMP: MP"
///ScriptDocEnd
=============
*/
waittillBuddyJoinedStreak( promptId )
{
	while ( true )
	{
		level waittill( "buddyJoinedStreak", buddy, id );
		
		if ( id == promptID )
			return buddy;
	}
}

/*
=============
///ScriptDocBegin
"Name: playerSetupCoopStreak()"
"Summary: Call this during the blackout to setup the players weapon so the hud hides."
"Module: Utility"
"CallOn: Player"
"Example: buddy playerSetupCoopStreak();"
"SPMP: MP"
///ScriptDocEnd
=============
*/
playerSetupCoopStreak( delay )
{
	self playerSetupCoopStreakInternal( delay );
}

/*
=============
///ScriptDocBegin
"Name: playerResetAfterCoopStreak()"
"Summary: Will take away the laptop and reset the player."
"Module: Utility"
"CallOn: Player"
"Example: buddy playerResetAfterCoopStreak();"
"SPMP: MP"
///ScriptDocEnd
=============
*/
playerResetAfterCoopStreak()
{
	self playerResetAfterCoopStreakInternal();
}

/*
=============
///ScriptDocBegin
"Name: playerStopPromptForStreakSupport()"
"Summary: Hides all hold-x-to-support prompts for the player."
"Module: Utility"
"CallOn: Level"
"Example: player playerStopPromptForStreakSupport();"
"SPMP: MP"
///ScriptDocEnd
=============
*/
playerStopPromptForStreakSupport()
{
	if ( !level.teamBased ) // only do coop prompts in team based modes
		return;

	if( !IsDefined(level.streakSupporDisabledCount[self.guid]) )
		level.streakSupporDisabledCount[self.guid] = 0;
	
	level.streakSupporDisabledCount[self.guid]++;
	
	if( level.streakSupporDisabledCount[self.guid] > 1 )
		return;
	
	if ( self.team == "allies" )
	{
		foreach ( streakPrompt in level.streakSupportQueueAllies )
		{
			streakPrompt DisablePlayerUse( self );
		}
	}
	else
	{	
		foreach ( streakPrompt in level.streakSupportQueueAxis )
		{
			streakPrompt DisablePlayerUse( self );
		}
	}
}

/*
=============
///ScriptDocBegin
"Name: playerStartPromptForStreakSupport()"
"Summary: Shows all hold-x-to-support prompts for the player."
"Module: Utility"
"CallOn: Level"
"Example: player playerStartPromptForStreakSupport();"
"SPMP: MP"
///ScriptDocEnd
=============
*/
playerStartPromptForStreakSupport()
{
	if ( !level.teamBased ) // only do coop prompts in team based modes
		return;

	level.streakSupporDisabledCount[self.guid]--;
	
	assert( level.streakSupporDisabledCount[self.guid] >= 0 );
	
	if( level.streakSupporDisabledCount[self.guid] > 0 )
		return;
	
	if ( self.team == "allies" )
	{
		foreach ( streakPrompt in level.streakSupportQueueAllies )
		{
			if ( self != streakPrompt.streakPlayer )
				streakPrompt EnablePlayerUse( self );
		}
	}
	else
	{	
		foreach ( streakPrompt in level.streakSupportQueueAxis )
		{
			if ( self != streakPrompt.streakPlayer )
				streakPrompt EnablePlayerUse( self );
		}
	}
}

// ----------------------------------------------------------------------------------
// Queue management

addStreakSupportPrompt( streakPrompt )
{
	if ( streakPrompt.team == "allies" )
	{
		level.streakSupportQueueAllies[ level.streakSupportQueueAllies.size ] = streakPrompt;
		
		if ( level.streakSupportQueueAllies.size == 1 )
			level thread startStreakSupportPrompt( streakPrompt );
	}
	else // team == "axis"
	{
		level.streakSupportQueueAxis[ level.streakSupportQueueAxis.size ] = streakPrompt;
		
		if ( level.streakSupportQueueAxis.size == 1 )
			level thread startStreakSupportPrompt( streakPrompt );
	}
}

removeStreakSupportPrompt( streakPrompt )
{
	wasActive = streakPrompt.active;
	streakPrompt.active = false;
	streakPrompt notify( "streakPromptStopped" );
	
	if ( streakPrompt.team == "allies" )
	{
		level.streakSupportQueueAllies = array_remove( level.streakSupportQueueAllies, streakPrompt );
		
		if ( wasActive && level.streakSupportQueueAllies.size > 0 )
			level thread startStreakSupportPrompt( level.streakSupportQueueAllies[0] );
	}
	else // team == "axis"
	{
		level.streakSupportQueueAxis = array_remove( level.streakSupportQueueAxis, streakPrompt );
		
		if ( wasActive && level.streakSupportQueueAxis.size > 0 )
			level thread startStreakSupportPrompt( level.streakSupportQueueAxis[0] );
	}
	thread delayDeletePrompt( streakPrompt );
}

delayDeletePrompt( streakPrompt )
{
	wait 1;
	streakPrompt Delete();
}

getUniqueStreakPromptID( team )
{
	maxID = 0;
	
	foreach ( streakPrompt in level.streakSupportQueueAllies )
	{
		if ( streakPrompt.promptID >= maxID )
			maxID = streakPrompt.promptID + 1;
	}
	
	foreach ( streakPrompt in level.streakSupportQueueAxis )
	{
		if ( streakPrompt.promptID >= maxID )
			maxID = streakPrompt.promptID + 1;
	}
	
	return maxID;
}


// -----------------------------------------------------------------------------------------------------
// Prompt logic

startStreakSupportPrompt( streakPrompt )
{
	streakPrompt.active = true;
	
	level thread handlePrompt( streakPrompt );
	level thread onConnectPrompt( streakPrompt );
	
	foreach ( player in level.players )
	{
		if ( IsDefined( streakPrompt.streakPlayer ) && player == streakPrompt.streakPlayer )
			continue;
		
		if ( isReallyAlive( player ) && player.team == streakPrompt.team )
			player thread playerSetupStreakPrompt( streakPrompt );
		
		player thread playerOnSpawnPrompt( streakPrompt );
	}
}

onConnectPrompt( streakPrompt ) // self == level
{
	level endon( "game_ended" );
	streakPrompt endon( "streakPromptStopped" );

	while ( true )
	{
		level waittill( "connected", player );
		
		player thread playerOnSpawnPrompt( streakPrompt );
	}
}

playerOnSpawnPrompt( streakPrompt ) // self == player
{
	level endon( "game_ended" );
	self endon( "disconnect" );
	streakPrompt endon( "streakPromptStopped" );

	while ( true )
	{
		self waittill( "spawned_player" );
		
		if ( self.team == streakPrompt.team )
			self thread playerSetupStreakPrompt( streakPrompt );
	}
}

playerSetupStreakPrompt( streakPrompt ) // self == player
{
	level endon( "game_ended" );
	self endon( "death" );
	self endon( "disconnect" );
	streakPrompt endon( "streakPromptStopped" );
	
	while ( self isUsingRemote() || self isInRemoteTransition() )
		waitframe();
	
	playerDisabledWait( streakPrompt );
	
	self thread playerDisplayJoinRequest( streakPrompt );
	self thread playerTakeStreakSupportInput( streakPrompt );
}

playerDisabledWait( streakPrompt )
{
	if( !IsDefined(level.streakSupporDisabledCount[self.guid]) )
		return;
	
	if( level.streakSupporDisabledCount[self.guid] > 0 )
	{
		streakPrompt DisablePlayerUse( self );
		while( level.streakSupporDisabledCount[self.guid] > 0 )
			waitframe();
	}
}

playerDisplayJoinRequest( streakPrompt ) //self = player
{
	level endon( "game_ended" );
	self endon( "death" );
	self endon( "disconnect" );
	streakPrompt endon( "streakPromptStopped" );
	
	if ( IsDefined( streakPrompt.splashRef ) )
		self thread maps\mp\gametypes\_hud_message::coopKillstreakSplashNotify( streakPrompt.splashRef, streakPrompt.needSupportVO );
}

waittillPlayerCanBeBuddy( player, streakPrompt )
{
	if ( isInRemoteTransition() )
		player maps\mp\killstreaks\_killstreaks::playerWaittillRideKillstreakComplete();
	
	waitframe();
		
	if ( isUsingRemote() ) // cannot join if in a killstreak
		 player waittill( "stopped_using_remote" );
}

waittillPromptActivated( streakPrompt )
{
	streakPrompt endon( "streakPromptStopped" );
	
	streakPrompt waittill( "trigger" );
	
	return true;
}

playerTakeStreakSupportInput( streakPrompt ) // self == player
{
	level endon( "game_ended" );
	self endon( "death" );
	self endon( "disconnect" );
	
	while ( true )
	{
		waittillPlayerCanBeBuddy( self );
		
		result = waittillPromptActivated( streakPrompt );
		if ( !IsDefined( result ) )
			return;
		
		if ( !streakPrompt.active )
			return;
		
		if ( IsDefined( self PlayerGetUseEnt() ) && self PlayerGetUseEnt() == streakPrompt && self UseButtonPressed() && self IsOnGround() )
		{
			useTime = self PlayerGetUseTime();
			result = self playerHandleJoining( streakPrompt, useTime );
			if ( result || !streakPrompt.active )
				return;
		}
	}
}

playerGetUseTime()
{
	if ( GetDvarInt( "scr_coop_util_delay", 1 ) == 0 )
	{
		return CONST_COOP_USE_BAR_TIME_MIN;
	}
	else
	{
		// give players with less score a shorter use time
		minScore = self.score;
		maxScore = self.score;
		
		for ( i = 1; i < level.players.size; i++ )
		{
			player = level.players[i];
			
			if ( player.team != self.team )
				continue;
			
			if ( player.score > maxScore )
				maxScore = player.score;
			else if ( player.score < minScore )
				minScore = player.score;
		}
		
		scoreSpread = maxScore - minScore;
		if ( scoreSpread == 0 )
			return CONST_COOP_USE_BAR_TIME_MIN;
		
		playerScorePercent = ( self.score - minScore ) / scoreSpread;
		
		useTimeSpread = CONST_COOP_USE_BAR_TIME_MAX - CONST_COOP_USE_BAR_TIME_MIN;
		useTime = CONST_COOP_USE_BAR_TIME_MIN + ( playerScorePercent * useTimeSpread );
		
		return useTime;
	}
}

handlePrompt( streakPrompt )
{
	streakPrompt makeGloballyUsableByType( "coopStreakPrompt", streakPrompt.joinText, undefined, streakPrompt.team );
	
	streakPrompt waittill( "streakPromptStopped" );
	
	streakPrompt makeGloballyUnusableByType();
}

playerHandleJoining( streakPrompt, useTime ) // self == player
{
	useTimeMS = useTime * 1000;
	if ( streakPrompt useHoldThink( self, useTimeMS, streakPrompt ) )
	{
		level notify( "buddyJoinedStreak", self, streakPrompt.promptID );
		
		self thread maps\mp\_events::killStreakJoinEvent();
		
		if ( IsDefined( streakPrompt.streakPlayer ) && IsAlive( streakPrompt.streakPlayer ) )
		{
			if ( IsDefined( streakPrompt.joinedVO ) )
				self thread leaderDialogOnPlayer( streakPrompt.joinedVO );
			
			if ( IsDefined( streakPrompt.buddyJoinedVO ) )
				streakPrompt.streakPlayer thread leaderDialogOnPlayer( streakPrompt.buddyJoinedVO );

			if( isDefined( streakPrompt.streakPlayer.currentKillStreakIndex ) )
			{
				setMatchData( "killstreaks", streakPrompt.streakPlayer.currentKillStreakIndex, "coopPlayerIndex", self.clientId );
			}
		}
	
		streakPrompt notify( "streakPromptStopped" );
		return true;
	}
	
	return false;
}

useHoldThink( player, useTimeMS, streakPrompt ) 
{
	Assert( IsPlayer( player ) );
	
	player PlayerLinkTo( streakPrompt );
	player PlayerLinkedOffsetEnable();
	player.manuallyJoiningKillStreak = true;
	
	self thread useHoldThinkCleanupOnPlayerDeath( player );
	
    self.curProgress = 0;
    self.inUse = true;
    self.useRate = 0;
	self.useTime = useTimeMS;
	
	if ( IsDefined( player.inWater ) )
	{
		player AllowCrouch( false );
		player AllowProne( false );
	}
    
	player _giveWeapon( "killstreak_remote_turret_mp" );
	player SwitchToWeapon( "killstreak_remote_turret_mp" );
	player DisableWeaponSwitch();
	
	player thread personalUseBar( self, streakPrompt );
   
    result = useHoldThinkLoop( player, streakPrompt );
    
    if ( !IsDefined( result ) )
    	result = false;
    
    if ( isAlive( player ) && !result )
    	player playerResetAfterCoopStreakInternal();
    
    self.inUse = false;
	self.curProgress = 0;
	if ( IsDefined( player ) )
	{
		player.manuallyJoiningKillStreak = false;
		player SetClientOmnvar( "ui_use_bar_text", 0 );
		player SetClientOmnvar( "ui_use_bar_end_time", 0 );
		player SetClientOmnvar( "ui_use_bar_start_time", 0 );
	}
	
	self notify( "coopUtilUseHoldThinkComplete" );

	return result;
}

useHoldThinkCleanupOnPlayerDeath( player )
{
	self endon( "coopUtilUseHoldThinkComplete" );
	
	player waittill_any( "death", "disconnect" );
	
	if ( IsDefined( player ) )
	{
		player playerResetAfterCoopStreakInternal();
		player.manuallyJoiningKillStreak = false;
		player SetClientOmnvar( "ui_use_bar_text", 0 );
		player SetClientOmnvar( "ui_use_bar_end_time", 0 );
		player SetClientOmnvar( "ui_use_bar_start_time", 0 );
	}
}

playerResetAfterCoopStreakInternal()
{
	self maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( "killstreak_predator_missile_mp" );
	self maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( "killstreak_remote_turret_mp" );
	self AllowCrouch( true );
	self AllowProne( true );
	self EnableWeaponSwitch();
	self SwitchToWeapon( self getLastWeapon() );
	self thread playerDelayControl();
	self Unlink();
}

playerSetupCoopStreakInternal( delay )
{
	if ( IsDefined( delay ) )
		wait delay;
	
	self EnableWeaponSwitch();
	self _giveWeapon( "killstreak_predator_missile_mp" );
	self SwitchToWeaponImmediate( "killstreak_predator_missile_mp" );
	self maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( "killstreak_remote_turret_mp" );
	self DisableWeaponSwitch();
}

playerDelayControl() // self == player
{
	self endon( "disconnect" );
	
	self freezeControlsWrapper( true );
	wait( 0.5 );
	self freezeControlsWrapper( false );
}

personalUseBar( object, streakPrompt )
{
    self endon( "disconnect" );
    streakPrompt endon( "streakPromptStopped" );
    
	self SetClientOmnvar( "ui_use_bar_text", 2 );
	self SetClientOmnvar( "ui_use_bar_start_time", int( GetTime() ) );

    lastRate = -1;
    while ( isReallyAlive( self ) && isDefined( object ) && object.inUse && !level.gameEnded )
    {
        if ( lastRate != object.useRate )
        {
            if( object.curProgress > object.useTime)
                object.curProgress = object.useTime;
			if ( object.useRate > 0  )
			{
				now = GetTime();
				current = object.curProgress / object.useTime;
				endTime = now + ( 1 - current ) * ( object.useTime / object.useRate );
				self SetClientOmnvar( "ui_use_bar_end_time", int( endTime ) );
			}
			lastRate = object.useRate;
        }
		wait ( 0.05 );
    }

	self SetClientOmnvar( "ui_use_bar_end_time", 0 );
    
}

useHoldThinkLoop( player, streakPrompt )
{
	streakPrompt endon( "streakPromptStopped" );
	
    while( !level.gameEnded && isDefined( self ) && isReallyAlive( player ) && player useButtonPressed() && self.curProgress < self.useTime )
	{
	        self.curProgress += (50 * self.useRate);
	        
	        if ( isDefined(self.objectiveScaler) )
	        	self.useRate = 1 * self.objectiveScaler;
	        else
	        	self.useRate = 1;
	        
	        if ( self.curProgress >= self.useTime )
	        	return ( isReallyAlive( player ) );
        
        wait 0.05;
	}
	
	return false;
}