#include maps\mp\_utility;
#include maps\mp\gametypes\_hud_util;

init()
{
	precacheString(&"PLATFORM_PRESS_TO_SKIP");
	precacheString(&"PLATFORM_PRESS_TO_RESPAWN");
	precacheString(&"PLATFORM_PRESS_TO_COPYCAT");
	precacheShader("specialty_copycat");
	
	level.killcam = maps\mp\gametypes\_tweakables::getTweakableValue( "game", "allowkillcam" );
}

killcam(
	attackerNum, // entity number of the attacker
	killcamentityindex, // entity number of the entity to view (grenade, airstrike, etc)
	killcamentitystarttime, // time at which the killcamentity came into being
	sWeapon, // killing weapon
	predelay, // time between player death and beginning of killcam
	offsetTime, // something to do with how far back in time the killer was seeing the world when he made the kill; latency related, sorta
	timeUntilRespawn, // will the player be allowed to respawn after the killcam?
	maxtime, // time remaining until map ends; the killcam will never last longer than this. undefined = no limit
	attacker, // entity object of attacker
	victim // entity object of the victim
)
{
	// monitors killcam and hides HUD elements during killcam session
	//if ( !level.splitscreen )
	//	self thread killcam_HUD_off();
	
	self endon("disconnect");
	self endon("spawned");
	level endon("game_ended");
	
	if ( attackerNum < 0 )
		return;

	// length from killcam start to killcam end
	if (getdvar("scr_killcam_time") == "") {
		if ( sWeapon == "artillery_mp" || sWeapon == "stealth_bomb_mp" )
			camtime = (gettime() - killcamentitystarttime) / 1000 - predelay - .1;
		else if ( level.showingFinalKillcam	)
			camtime = 4.0;
		else if ( sWeapon == "javelin_mp" )
			camtime = 8;
		else if ( issubstr( sWeapon, "remotemissile_" ) )
			camtime = 5;
		else if ( !timeUntilRespawn || timeUntilRespawn > 5.0 ) // if we're not going to respawn, we can take more time to watch what happened
			camtime = 5.0;
		else if ( sWeapon == "frag_grenade_mp" || sWeapon == "frag_grenade_short_mp" || sWeapon == "semtex_mp" )
			camtime = 4.25; // show long enough to see grenade thrown
		else
			camtime = 2.5;
	}
	else
		camtime = getdvarfloat("scr_killcam_time");

	if (isdefined(maxtime)) {
		if (camtime > maxtime)
			camtime = maxtime;
		if (camtime < .05)
			camtime = .05;
	}
	
	// time after player death that killcam continues for
	if (getdvar("scr_killcam_posttime") == "")
		postdelay = 2;
	else {
		postdelay = getdvarfloat("scr_killcam_posttime");
		if (postdelay < 0.05)
			postdelay = 0.05;
	}
	
	/* timeline:
	
	|        camtime       |      postdelay      |
	|                      |   predelay    |
	
	^ killcam start        ^ player death        ^ killcam end
	                                       ^ player starts watching killcam
	
	*/
	
	killcamlength = camtime + postdelay;
	
	// don't let the killcam last past the end of the round.
	if (isdefined(maxtime) && killcamlength > maxtime)
	{
		// first trim postdelay down to a minimum of 1 second.
		// if that doesn't make it short enough, trim camtime down to a minimum of 1 second.
		// if that's still not short enough, cancel the killcam.
		if ( maxtime < 2 )
			return;

		if (maxtime - camtime >= 1) {
			// reduce postdelay so killcam ends at end of match
			postdelay = maxtime - camtime;
		}
		else {
			// distribute remaining time over postdelay and camtime
			postdelay = 1;
			camtime = maxtime - 1;
		}
		
		// recalc killcamlength
		killcamlength = camtime + postdelay;
	}

	killcamoffset = camtime + predelay;
	
	startTime = getTime();
	self notify ( "begin_killcam", startTime );
	
	self.sessionstate = "spectator";
	self.forcespectatorclient = attackerNum;
	self.killcamentity = -1;
	if ( killcamentityindex >= 0 )
		self thread setKillCamEntity( killcamentityindex, killcamoffset, killcamentitystarttime );
	self.archivetime = killcamoffset;
	self.killcamlength = killcamlength;
	self.psoffsettime = offsetTime;
	
	// ignore spectate permissions
	self allowSpectateTeam("allies", true);
	self allowSpectateTeam("axis", true);
	self allowSpectateTeam("freelook", true);
	self allowSpectateTeam("none", true);

	if ( isDefined( attacker ) && level.showingFinalKillcam ) // attacker may have disconnected
	{
		self openMenu( "killedby_card_display" );
		self SetCardDisplaySlot( attacker, 7 );
	}
	
	self thread endedKillcamCleanup();

	// wait till the next server frame to allow code a chance to update archivetime if it needs trimming
	wait 0.05;	
	
	assertex( self.archivetime <= killcamoffset + 0.0001, "archivetime: " + self.archivetime + ", killcamoffset: " + killcamoffset );
	if ( self.archivetime < killcamoffset )
		println( "WARNING: Code trimmed killcam time by " + (killcamoffset - self.archivetime) + " seconds because it doesn't have enough game time recorded!" );
	
	camtime = self.archivetime - .05 - predelay;
	killcamlength = camtime + postdelay;
	self.killcamlength = killcamlength;
	
	if ( camtime <= 0 ) // if we're not looking back in time far enough to even see the death, cancel
	{
		println( "Cancelling killcam because we don't even have enough recorded to show the death." );
		
		self.sessionstate = "dead";
		self.forcespectatorclient = -1;
		self.killcamentity = -1;
		self.archivetime = 0;
		self.psoffsettime = 0;
		
		self notify ( "killcam_ended" );
		
		return;
	}
	
	if ( level.showingFinalKillcam )
		thread doFinalKillCamFX( camtime );
	
	self.killcam = true;

	self initKCElements();

	if ( !level.splitscreen )
	{	
		self.kc_timer.alpha = 1;
		self.kc_timer setTenthsTimer(camtime);
	}

	if ( timeUntilRespawn && !level.gameEnded )
	{
		if ( timeUntilRespawn > 0 )
			setLowerMessage( "kc_info", game["strings"]["waiting_to_spawn"], timeUntilRespawn );
		else
			setLowerMessage( "kc_info", &"PLATFORM_PRESS_TO_SKIP" );
	}
	else if ( !level.gameEnded )
	{
		setLowerMessage( "kc_info", &"PLATFORM_PRESS_TO_RESPAWN" );
	}
	
	if ( !level.showingFinalKillcam )
		self.kc_skiptext.alpha = 1;
	else
		self.kc_skiptext.alpha = 0;

	self.kc_othertext.alpha = 0;
	self.kc_icon.alpha = 0;

	self thread spawnedKillcamCleanup();

	if ( self == victim && victim _hasPerk( "specialty_copycat" ) && isDefined( victim.pers["copyCatLoadout"] ) )
		self thread waitKCCopyCatButton( attacker );

	if ( !level.showingFinalKillcam )
		self thread waitSkipKillcamButton( timeUntilRespawn );
	else
		self notify ( "showing_final_killcam" );
	
	self thread endKillcamIfNothingToShow();
	
	self waittillKillcamOver();
	
	if ( level.showingFinalKillcam )
	{
		self thread maps\mp\gametypes\_playerlogic::spawnEndOfGame();
		return;
	}
	
	self thread calculateKillCamTime( startTime );
	
	self thread killcamCleanup( true );
}


doFinalKillCamFX( camTime )
{
	if ( isDefined( level.doingFinalKillcamFx ) )
		return;
	level.doingFinalKillcamFx = true;
	
	intoSlowMoTime = camTime;
	if ( intoSlowMoTime > 1.0 )
	{
		intoSlowMoTime = 1.0;
		wait( camTime - 1.0 );
	}
	
	setSlowMotion( 1.0, 0.25, intoSlowMoTime ); // start timescale, end timescale, lerp duration
	wait( intoSlowMoTime + .5 );
	setSlowMotion( 0.25, 1, 1.0 );
	
	level.doingFinalKillcamFx = undefined;
}


calculateKillCamTime( startTime )
{
	watchedTime = int(getTime() - startTime);
	self incPlayerStat( "killcamtimewatched", watchedTime );
}

waittillKillcamOver()
{
	self endon("abort_killcam");
	
	wait(self.killcamlength - 0.05);
}

setKillCamEntity( killcamentityindex, killcamoffset, starttime )
{
	self endon("disconnect");
	self endon("killcam_ended");
	
	killcamtime = (gettime() - killcamoffset * 1000);
	
	if ( starttime > killcamtime )
	{
		wait .05;
		// code may have trimmed archivetime after the first frame if we couldn't go back in time as far as requested.
		killcamoffset = self.archivetime;
		killcamtime = (gettime() - killcamoffset * 1000);
		
		if ( starttime > killcamtime )
			wait (starttime - killcamtime) / 1000;
	}
	self.killcamentity = killcamentityindex;
}

waitSkipKillcamButton( timeUntilRespawn )
{
	self endon("disconnect");
	self endon("killcam_ended");

	while(self useButtonPressed())
		wait .05;

	while(!(self useButtonPressed()))
		wait .05;
		
	if ( !matchMakingGame() )
		self incPlayerStat( "killcamskipped", 1 );

	if ( timeUntilRespawn <= 0 )
		clearLowerMessage( "kc_info" );
	
	self notify("abort_killcam");
}

waitKCCopyCatButton( attacker )
{
	self endon("disconnect");
	self endon("killcam_ended");

	self waitCopyCatButton( attacker );

	self notify("abort_killcam");
}

waitDeathCopyCatButton( attacker )
{
	self endon ( "disconnect" );

	self initKCElements();
	
	usedCopycat = self waitCopyCatButton( attacker );

	if ( !isDefined( usedCopycat ) )
	{
		self.kc_icon.alpha = 0;
		self.kc_othertext.alpha = 0;
	}
}

waitCopyCatButton( attacker )
{
	self endon ( "spawned_player" );
	self endon ( "death_delay_finished" );
	self.kc_icon setShader( "specialty_copycat", 48, 48 );
	self.kc_othertext setText( &"PLATFORM_PRESS_TO_COPYCAT" );
	self.kc_othertext.alpha = 1;
	self.kc_icon.alpha = 1;
	
	self notifyOnPlayerCommand( "use_copycat", "weapnext" );
	
	self waittill( "use_copycat" );
	
	self.pers["copyCatLoadout"]["inUse"] = true;
	self.pers["copyCatLoadout"]["owner"] = attacker;

	self.kc_othertext fadeOverTime( 0.5 );
	self.kc_othertext.alpha = 0;

	self.kc_icon fadeOverTime( 0.25 );
	self.kc_icon scaleOverTime( 0.25, 512, 512 );
	self.kc_icon.alpha = 0;
	
	if ( isDefined( attacker ) )
		attacker thread maps\mp\gametypes\_hud_message::playerCardSplashNotify( "copied", self );

	self playLocalSound( "copycat_steal_class" );
		
	return true;
}

waitSkipKillcamSafeSpawnButton()
{
	self endon("disconnect");
	self endon("killcam_ended");
	
	if ( !self maps\mp\gametypes\_playerlogic::maySpawn() )
		return;
	
	while(self fragButtonPressed())
		wait .05;

	while(!(self fragButtonPressed()))
		wait .05;
	
	self.wantSafeSpawn = true;

	self notify("abort_killcam");
}

endKillcamIfNothingToShow()
{
	self endon("disconnect");
	self endon("killcam_ended");
	
	while(1)
	{
		// code may trim our archivetime to zero if there is nothing "recorded" to show.
		// this can happen when the person we're watching in our killcam goes into killcam himself.
		// in this case, end the killcam.
		if ( self.archivetime <= 0 )
			break;
		wait .05;
	}
	
	self notify("abort_killcam");
}

spawnedKillcamCleanup()
{
	self endon("disconnect");
	self endon("killcam_ended");
	
	self waittill("spawned");
	self thread killcamCleanup( false );
}

endedKillcamCleanup()
{
	self endon("disconnect");
	self endon("killcam_ended");
	
	level waittill("game_ended");

	self thread killcamCleanup( true );
}

killcamCleanup( clearState )
{
	if(isDefined(self.kc_skiptext))
		self.kc_skiptext.alpha = 0;

	if(isDefined(self.kc_timer))
		self.kc_timer.alpha = 0;

	if(isDefined(self.kc_icon))
		self.kc_icon.alpha = 0;

	if(isDefined(self.kc_othertext))
		self.kc_othertext.alpha = 0;

	self.killcam = undefined;

	if ( !level.gameEnded )
		self clearLowerMessage( "kc_info" );
	
	self thread maps\mp\gametypes\_spectating::setSpectatePermissions();
	
	self notify("killcam_ended"); // do this last, in case this function was called from a thread ending on it

	if ( !clearState )
		return;
			
	self.sessionstate = "dead";
	self ClearKillcamState();
}



cancelKillCamOnUse()
{
	self.cancelKillcam = false;
	self thread cancelKillCamOnUse_specificButton( ::cancelKillCamUseButton, ::cancelKillCamCallback );
	//self thread cancelKillCamOnUse_specificButton( ::cancelKillCamSafeSpawnButton, ::cancelKillCamSafeSpawnCallback );
}

cancelKillCamUseButton()
{
	return self useButtonPressed();
}
cancelKillCamSafeSpawnButton()
{
	return self fragButtonPressed();
}
cancelKillCamCallback()
{
	self.cancelKillcam = true;
}
cancelKillCamSafeSpawnCallback()
{
	self.cancelKillcam = true;
	self.wantSafeSpawn = true;
}

cancelKillCamOnUse_specificButton( pressingButtonFunc, finishedFunc )
{
	self endon ( "death_delay_finished" );
	self endon ( "disconnect" );
	level endon ( "game_ended" );
	
	for ( ;; )
	{
		if ( !self [[pressingButtonFunc]]() )
		{
			wait ( 0.05 );
			continue;
		}
		
		buttonTime = 0;
		while( self [[pressingButtonFunc]]() )
		{
			buttonTime += 0.05;
			wait ( 0.05 );
		}
		
		if ( buttonTime >= 0.5 )
			continue;
		
		buttonTime = 0;
		
		while ( !self [[pressingButtonFunc]]() && buttonTime < 0.5 )
		{
			buttonTime += 0.05;
			wait ( 0.05 );
		}
		
		if ( buttonTime >= 0.5 )
			continue;
			
		self [[finishedFunc]]();
		return;
	}	
}

initKCElements()
{
	if ( !isDefined( self.kc_skiptext ) )
	{
		self.kc_skiptext = newClientHudElem(self);
		self.kc_skiptext.archived = false;
		self.kc_skiptext.x = 0;
		self.kc_skiptext.alignX = "center";
		self.kc_skiptext.alignY = "top";
		self.kc_skiptext.horzAlign = "center_adjustable";
		self.kc_skiptext.vertAlign = "top_adjustable";
		self.kc_skiptext.sort = 1; // force to draw after the bars
		self.kc_skiptext.font = "default";
		self.kc_skiptext.foreground = true;
		self.kc_skiptext.hideWhenInMenu = true;
		
		if ( level.splitscreen )
		{
			self.kc_skiptext.y = 20;
			self.kc_skiptext.fontscale = 1.2; // 1.8/1.5
		}
		else
		{
			self.kc_skiptext.y = 32;
			self.kc_skiptext.fontscale = 1.8;
		}
	}
	
	if ( !isDefined( self.kc_othertext ) )
	{
		self.kc_othertext = newClientHudElem(self);
		self.kc_othertext.archived = false;		
		self.kc_othertext.y = 18;
		self.kc_othertext.alignX = "left";
		self.kc_othertext.alignY = "top";
		self.kc_othertext.horzAlign = "center";
		self.kc_othertext.vertAlign = "middle";
		self.kc_othertext.sort = 10; // force to draw after the bars
		self.kc_othertext.font = "small";
		self.kc_othertext.foreground = true;
		self.kc_othertext.hideWhenInMenu = true;
		
		if ( level.splitscreen )
		{
			self.kc_othertext.x = 16;
			self.kc_othertext.fontscale = 1.2;
		}
		else
		{
			self.kc_othertext.x = 62;
			self.kc_othertext.fontscale = 1.6;
		}
	}

	if ( !isDefined( self.kc_icon ) )
	{
		self.kc_icon = newClientHudElem(self);
		self.kc_icon.archived = false;
		self.kc_icon.x = 16;
		self.kc_icon.y = 16;
		self.kc_icon.alignX = "left";
		self.kc_icon.alignY = "top";
		self.kc_icon.horzAlign = "center";
		self.kc_icon.vertAlign = "middle";
		self.kc_icon.sort = 1; // force to draw after the bars
		self.kc_icon.foreground = true;
		self.kc_icon.hideWhenInMenu = true;		
	}

	if ( !level.splitscreen )
	{
		if ( !isdefined( self.kc_timer ) )
		{
			self.kc_timer = createFontString( "hudbig", 1.0 );
			self.kc_timer.archived = false;
			self.kc_timer.x = 0;
			self.kc_timer.alignX = "center";
			self.kc_timer.alignY = "middle";
			self.kc_timer.horzAlign = "center_safearea";
			self.kc_timer.vertAlign = "top_adjustable";
			self.kc_timer.y = 42;
			self.kc_timer.sort = 1; // force to draw after the bars
			self.kc_timer.font = "hudbig";
			self.kc_timer.foreground = true;
			self.kc_timer.color = (0.85,0.85,0.85);
			self.kc_timer.hideWhenInMenu = true;
		}
	}
}

