#include maps\mp\_utility;

init()
{
	level.persistentDataInfo = [];

	maps\mp\gametypes\_class::init();
	maps\mp\gametypes\_missions::init();
	maps\mp\gametypes\_playercards::init();

	maps\mp\gametypes\_rank::init();
	
	if ( GetDvarInt( "virtualLobbyActive", 0 ) > 0 )
	{	// no game stats while in the vl
		return;
	}
	level thread updateBufferedStats();
	
	level thread uploadGlobalStatCounters();
}


initBufferedStats()
{
	println( "Init Buffered Stats for " + self.name );

	self.bufferedStats = [];

	if ( self rankingEnabled() )
	{
		self.bufferedStats[ "totalShots" ] = self getRankedPlayerData( "totalShots" );	
		self.bufferedStats[ "accuracy" ] = self getRankedPlayerData( "accuracy" );	
		self.bufferedStats[ "misses" ] = self getRankedPlayerData( "misses" );	
		self.bufferedStats[ "hits" ] = self getRankedPlayerData( "hits" );	
		self.bufferedStats[ "timePlayedAllies" ] = self getRankedPlayerData( "timePlayedAllies" );	
		self.bufferedStats[ "timePlayedOpfor" ] = self getRankedPlayerData( "timePlayedOpfor" );	
		self.bufferedStats[ "timePlayedOther" ] = self getRankedPlayerData( "timePlayedOther" );	
		self.bufferedStats[ "timePlayedTotal" ] = self getRankedPlayerData( "timePlayedTotal" );

		println( "timePlayedAllies " + self.bufferedStats[ "timePlayedAllies" ] );
		println( "timePlayedOpfor " + self.bufferedStats[ "timePlayedOpfor" ] );
		println( "timePlayedOther " + self.bufferedStats[ "timePlayedOther" ] );
		println( "timePlayedTotal " + self.bufferedStats[ "timePlayedTotal" ] );
	}

	
	self.bufferedChildStats = [];
	self.bufferedChildStats[ "round" ] = [];
	self.bufferedChildStats[ "round" ][ "timePlayed" ] = self getCommonPlayerData( "round", "timePlayed" );
}


// ==========================================
// Script persistent data functions
// These are made for convenience, so persistent data can be tracked by strings.
// They make use of code functions which are prototyped below.

/*
=============
statGet

Returns the value of the named stat
=============
*/
statGet( dataName )
{
	assert( !isDefined( self.bufferedStats[ dataName ] ) ); // should use statGetBuffered consistently with statSetBuffered
	return self getRankedPlayerData( dataName );
}

/*
=============
statSet

Sets the value of the named stat
=============
*/
statSet( dataName, value )
{
	if ( !self rankingEnabled() || practiceRoundGame() )
		return;
	
	assert( !isDefined( self.bufferedStats[ dataName ] ) ); // should use statGetBuffered consistently with statSetBuffered

	self setRankedPlayerData( dataName, value );
}

/*
=============
statAdd

Adds the passed value to the value of the named stat
=============
*/
statAdd( dataName, value, optionalArrayInd )
{	
	if ( !self rankingEnabled() || practiceRoundGame() )
		return;
	
	assert( !isDefined( self.bufferedStats[ dataName ] ) ); // should use statGetBuffered consistently with statSetBuffered		

	if ( isDefined( optionalArrayInd ) )
	{
		curValue = self getRankedPlayerData( dataName, optionalArrayInd );
		self setRankedPlayerData( dataName, optionalArrayInd, value + curValue );
	}
	else
	{
		curValue = self getRankedPlayerData( dataName );
		self setRankedPlayerData( dataName, value + curValue );
	}
}


statGetChild( parent, child )
{
	if ( parent == "round" )
		return self getCommonPlayerData( parent, child );
	else
		return self getRankedPlayerData( parent, child );
}


statSetChild( parent, child, value )
{
	if ( IsAgent(self) )
		return;
	
	if ( inVirtualLobby() )
		return;
	
	if ( parent == "round" )
	{
		self setCommonPlayerData( parent, child, value );
	}
	else
	{
		if ( !self rankingEnabled() || practiceRoundGame() )
			return;
		
		self setRankedPlayerData( parent, child, value );
	}
}


statAddChild( parent, child, value )
{
	if ( !self rankingEnabled() || practiceRoundGame() )
		return;
	
	assert( isDefined( self.bufferedChildStats[ parent ][ child ] ) );

	curValue = self getRankedPlayerData( parent, child );
	self setRankedPlayerData( parent, child, curValue + value );
}


statGetChildBuffered( parent, child )
{
	if ( !self rankingEnabled() )
		return 0;

	assert( isDefined( self.bufferedChildStats[ parent ][ child ] ) );
	
	return self.bufferedChildStats[ parent ][ child ];
}


statSetChildBuffered( parent, child, value )
{
	if ( !self rankingEnabled() )
		return;

	assert( isDefined( self.bufferedChildStats[ parent ][ child ] ) );

	self.bufferedChildStats[ parent ][ child ] = value;
}


statAddChildBuffered( parent, child, value )
{
	if ( !self rankingEnabled() )
		return;
	
	assert( isDefined( self.bufferedChildStats[ parent ][ child ] ) );

	curValue = statGetChildBuffered( parent, child );
	statSetChildBuffered( parent, child, curValue + value );
}


statAddBufferedWithMax( stat, value, max )
{
	if ( !self rankingEnabled() )
		return;
	
	assert( isDefined( self.bufferedStats[ stat ] ) );

	newValue = statGetBuffered( stat ) + value;
	
	if ( newValue > max )
		newValue = max;
		
	if ( newValue < statGetBuffered( stat ) )	// has wrapped so keep at the max
		newValue = max;
		
	statSetBuffered( stat, newValue );
}


statAddChildBufferedWithMax( parent, child, value, max )
{
	if ( !self rankingEnabled() )
		return;
	
	assert( isDefined( self.bufferedChildStats[ parent ][ child ] ) );

	newValue = statGetChildBuffered( parent, child ) + value;
	
	if ( newValue > max )
		newValue = max;
		
	if ( newValue < statGetChildBuffered( parent, child ) )	// has wrapped so keep at the max
		newValue = max;
		
	statSetChildBuffered( parent, child, newValue );
}


/*
=============
statGetBuffered

Returns the value of the named stat
=============
*/
statGetBuffered( dataName )
{
	if ( !self rankingEnabled() )
		return 0;

	assert( isDefined( self.bufferedStats[ dataName ] ) );
	
	return self.bufferedStats[ dataName ];
}

/*
=============
statSet

Sets the value of the named stat
=============
*/
statSetBuffered( dataName, value )
{
	if ( !self rankingEnabled() )
		return;
	
	assert( isDefined( self.bufferedStats[ dataName ] ) );

	self.bufferedStats[ dataName ] = value;
}

/*
=============
statAdd

Adds the passed value to the value of the named stat
=============
*/
statAddBuffered( dataName, value )
{	
	if ( !self rankingEnabled() )
		return;
	
	assert( isDefined( self.bufferedStats[ dataName ] ) );
	assert( value >= 0 );

	curValue = statGetBuffered( dataName );
	statSetBuffered( dataName, curValue + value );
}


updateBufferedStats()
{
	// give the first player time to connect
	wait ( 0.15 );
	
	nextToUpdate = 0;
	while ( !level.gameEnded )
	{
		maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();

		nextToUpdate++;
		if ( nextToUpdate >= level.players.size )
			nextToUpdate = 0;

		if ( isDefined( level.players[nextToUpdate] ) )
		{
			level.players[nextToUpdate] writeBufferedStats();
			level.players[nextToUpdate] updateWeaponBufferedStats();
		}

		wait ( 2.0 );
	}
	
	foreach ( player in level.players )
	{
		player writeBufferedStats();
		player updateWeaponBufferedStats();
	}
			
}


writeBufferedStats()
{
	rankingEnabled = ( self rankingEnabled() && !practiceRoundGame() );
	if ( rankingEnabled )
	{
		foreach ( statName, statVal in self.bufferedStats )
		{
			self setRankedPlayerData( statName, statVal );
		}
	}

	foreach ( statName, statVal in self.bufferedChildStats )
	{
		foreach ( childStatName, childStatVal in statVal )
		{
			if ( statName == "round" )
				self setCommonPlayerData( statName, childStatName, childStatVal );
			else if ( rankingEnabled )
				self setRankedPlayerData( statName, childStatName, childStatVal );
		}
	}
}

incrementWeaponStat( weaponName, stat, incValue )
{
	if( isKillstreakWeapon( weaponName ) )
		return;
	
	if( IsDefined(level.disableWeaponStats) && level.disableWeaponStats )
		return;

	if ( self rankingEnabled() && !practiceRoundGame() )
	{
		oldval = self getRankedPlayerData( "weaponStats", weaponName, stat );			
		self setRankedPlayerData( "weaponStats", weaponName, stat, oldval+incValue );
	}
}

incrementAttachmentStat( attachmentName, stat, incValue )
{
	if ( self rankingEnabled() && !practiceRoundGame() )
	{
		oldval = self getRankedPlayerData( "attachmentsStats", attachmentName, stat );			
		self setRankedPlayerData( "attachmentsStats", attachmentName, stat, oldval+incValue );
	}
}


updateWeaponBufferedStats()
{
	if( !isdefined( self.trackingWeaponName ) )
		return;
		
	if( self.trackingWeaponName == "" || self.trackingWeaponName == "none" )
		return;
	
	tmp = self.trackingWeaponName;
	
	//if( isSubStr( self.trackingWeaponName, "iw5_" ) )
	//tmp = GetSubStr( self.trackingWeaponName, 4, self.trackingWeaponName.size );
	
	// check for killstreak and environment before we start tokenizing the weapon name
	if( isKillstreakWeapon( tmp ) || isEnvironmentWeapon( tmp ) )
		return;
	
	/*stripping out akimbo and adding it in as it were an attachment for tracking
	if ( isSubStr( tmp, "akimbo" ) )
	{
		tmp = fixAkimboString( tmp );
	}
	*/
		
	tokens = getWeaponNameTokens( tmp );
	
	//updating for IW5 weapons
	if ( tokens[0] == "iw5" )// || isLootWeapon(tokens) )
		tokens[0] = tokens[0] + "_" + tokens[1];
	
	if ( tokens[0] == "alt" )
	{
		foreach( token in tokens )
		{
			if ( token == "gl" || token == "gp25" || token == "m320" )
			{
				tokens[0] = "gl";
				break;	
			}
			if ( token == "shotgun" )
			{
				tokens[0] = "shotgun";
				break;	
			}
		}
		
		//For Hybrid Scopes
		if ( tokens[0] == "alt" )
		{
			tokens[0] = tokens[1] + "_" + tokens[2];
		}
		
	}
	
	//when you hit someone or kill someone with the gl it will come in as the first token
	if( tokens[0] == "gl" || tokens[0] == "shotgun" )
	{
		//println(" player is " + self.name);
		//println( "weapon is  " + tokens[0]);
		if( self.trackingWeaponShots > 0 )
		{
			self incrementAttachmentStat( tokens[0], "shots", self.trackingWeaponShots ); 
			//println("wrote shots");
		}
		
		if( self.trackingWeaponKills > 0 )
		{
			self incrementAttachmentStat( tokens[0], "kills", self.trackingWeaponKills ); 
			//println("wrote kills");
		}
		
		if(	self.trackingWeaponHits > 0 )
		{
			self incrementAttachmentStat( tokens[0], "hits", self.trackingWeaponHits ); 
			//println("wrote hits");
		}
		
		if( self.trackingWeaponHeadShots > 0 )
		{
			self incrementAttachmentStat( tokens[0], "headShots", self.trackingWeaponHeadShots ); 
			//println("wrote headshots");
		}
			
		if( self.trackingWeaponDeaths > 0 )
		{
			self incrementAttachmentStat( tokens[0], "deaths", self.trackingWeaponDeaths ); 
			//println("wrote deaths");
		}
		
		if( self.trackingWeaponHipFireKills > 0 )
		{
			self incrementAttachmentStat( tokens[0], "hipfirekills", self.trackingWeaponHipFireKills ); 
			//println("wrote hipfire kills");
		}
		
		if( self.trackingWeaponUseTime > 0 )
		{
			self incrementAttachmentStat( tokens[0], "timeInUse", self.trackingWeaponUseTime ); 
			//println("wrote hipfire kills");
		}
		
		
		self.trackingWeaponName = "none";
		self.trackingWeaponShots = 0;
		self.trackingWeaponKills = 0;
		self.trackingWeaponHits = 0;
		self.trackingWeaponHeadShots = 0;
		self.trackingWeaponDeaths = 0;
		self.trackingWeaponHipFireKills = 0;
		self.trackingWeaponUseTime = 0;
		return;
	}
	
	if ( !isCACPrimaryWeapon( tokens[0] ) && !isCACSecondaryWeapon( tokens[0] ) )
		return;
	
	//log the weapon
	//println(" player is " + self.name);
	//println( "weapon is  " + tokens[0]);
	if( self.trackingWeaponShots > 0 )
	{
		self incrementWeaponStat( tokens[0], "shots", self.trackingWeaponShots );
		self maps\mp\_matchdata::logWeaponStat( tokens[0], "shots", self.trackingWeaponShots );
		//println("wrote shots");
	}
	
	if( self.trackingWeaponKills > 0 )
	{
		self incrementWeaponStat( tokens[0], "kills", self.trackingWeaponKills );
		self maps\mp\_matchdata::logWeaponStat( tokens[0], "kills", self.trackingWeaponKills );
		//println("wrote kills");
	}
	
	if(	self.trackingWeaponHits > 0 )
	{
		self incrementWeaponStat( tokens[0], "hits", self.trackingWeaponHits );
		self maps\mp\_matchdata::logWeaponStat( tokens[0], "hits", self.trackingWeaponHits );
		//println("wrote hits");
	}
	
	if( self.trackingWeaponHeadShots > 0 )
	{
		self incrementWeaponStat( tokens[0], "headShots", self.trackingWeaponHeadShots );
		self maps\mp\_matchdata::logWeaponStat( tokens[0], "headShots", self.trackingWeaponHeadShots );
		//println("wrote headshots");
	}
	
	if( self.trackingWeaponDeaths > 0 )
	{
		self incrementWeaponStat( tokens[0], "deaths", self.trackingWeaponDeaths );
		self maps\mp\_matchdata::logWeaponStat( tokens[0], "deaths", self.trackingWeaponDeaths );
		//println("wrote deaths");
	}
	
	if( self.trackingWeaponHipFireKills > 0 )
	{
		self incrementWeaponStat( tokens[0], "hipfirekills", self.trackingWeaponHipFireKills ); 
		self maps\mp\_matchdata::logWeaponStat( tokens[0], "hipfirekills", self.trackingWeaponHipFireKills );
		//println("wrote hipfire kills");
	}
	
	if( self.trackingWeaponUseTime > 0 )
	{
		self incrementWeaponStat( tokens[0], "timeInUse", self.trackingWeaponUseTime ); 
		self maps\mp\_matchdata::logWeaponStat( tokens[0], "timeInUse", self.trackingWeaponUseTime );
		//println("wrote time in use");
	}
	
	attachments = GetWeaponAttachments( tmp );
	foreach ( attachmentName in attachments )
	{
		//turns alternate silencers and gls into correct stat track
		attachmentBase = attachmentMap_toBase( attachmentName );
		if( attachmentBase == "gl" || attachmentBase == "shotgun" )
			continue;
			
		//println( "weapon is  " + tokens[i]);
		if( self.trackingWeaponShots > 0 )
		{
			if ( attachmentBase != "tactical" )
			{
				self incrementAttachmentStat( attachmentBase, "shots", self.trackingWeaponShots ); 
			}
			//println("wrote shots");
		}
	
		if( self.trackingWeaponKills > 0 )
		{
			if ( attachmentBase != "tactical" )
			{
				self incrementAttachmentStat( attachmentBase, "kills", self.trackingWeaponKills ); 
				//println("wrote kills");
			}
		}
	
		if(	self.trackingWeaponHits > 0 )
		{
			if ( attachmentBase != "tactical" )
			{
				self incrementAttachmentStat( attachmentBase, "hits", self.trackingWeaponHits ); 
				//println("wrote hits");
			}
		}
	
		if( self.trackingWeaponHeadShots > 0 )
		{
			if ( attachmentBase != "tactical" )
			{
				self incrementAttachmentStat( attachmentBase, "headShots", self.trackingWeaponHeadShots ); 
				//println("wrote headshots");
			}
		}
			
		if( self.trackingWeaponHipFireKills > 0 )
		{
			if ( attachmentBase != "tactical" )
			{
				self incrementAttachmentStat( attachmentBase, "hipfirekills", self.trackingWeaponHipFireKills ); 
				//println("wrote hipfire kills");
			}
		}
		
		if( self.trackingWeaponUseTime > 0 )
		{
			if ( attachmentBase != "tactical" )
			{
				self incrementAttachmentStat( attachmentBase, "timeInUse", self.trackingWeaponUseTime ); 
				//println("wrote time in use");
			}
		}
	
		if( self.trackingWeaponDeaths > 0 )
		{
			self incrementAttachmentStat( attachmentBase, "deaths", self.trackingWeaponDeaths ); 
			//println("wrote deaths");
		}
	}
	
	self.trackingWeaponName = "none";
	self.trackingWeaponShots = 0;
	self.trackingWeaponKills = 0;
	self.trackingWeaponHits = 0;
	self.trackingWeaponHeadShots = 0;
	self.trackingWeaponDeaths = 0;
	self.trackingWeaponHipFireKills = 0;
	self.trackingWeaponUseTime = 0;
}

uploadGlobalStatCounters()
{
	level waittill( "game_ended" );
	
	if( !matchMakingGame() )
		return;
		
	totalKills = 0;
	totalDeaths = 0;
	totalAssists = 0;
	totalHeadshots = 0;
	totalSuicides = 0;
	totalTimePlayed = 0;
	
	foreach ( player in level.players ) 
	{
		totalTimePlayed += player.timePlayed["total"];
	}

	incrementCounter( "global_minutes", int( totalTimePlayed / 60 ) );

	if ( !wasLastRound() )
		return;

	wait( 0.05 );
	
	foreach ( player in level.players ) 
	{
		totalKills += player.kills;
		totalDeaths += player.deaths;
		totalAssists += player.assists;
		totalHeadshots += player.headshots;	
		totalSuicides += player.suicides;
	}

	incrementCounter( "global_kills", totalKills );
	incrementCounter( "global_deaths", totalDeaths );
	incrementCounter( "global_assists", totalAssists );
	incrementCounter( "global_headshots", totalHeadshots );
	incrementCounter( "global_suicides", totalSuicides );
	incrementCounter( "global_games", 1 );
}
