class GameProfile extends Object
	native;

var()	string	  PackageName;
var()	int		 ManifestIndex;


enum EPlayerPos			
{
	POS_Auto,
	POS_Defense,
	POS_Offense,
	POS_Roam,
	/*POS_Captain,  Not handy for dropdown display */
	POS_Supporting
};
const NUM_POSITIONS = 5;
var localized string PositionName[5];		// text names of these positions

var EPlayerPos PlayerPositions[7];			// only need positions for AI team, so 7 max
const TEAM_SIZE = 7;
var()	array<string> PlayerTeam;		   // Player team members
var		int			PlayerLineup[4];		// Lineup for current match.  Stores index into PlayerTeam array.
const LINEUP_SIZE = 4;
var		string		EnemyTeam;				// Opponent team name for pending/current match
var		string		TeamName;				// Player team name
var		string		TeamSymbolName;			// name of team symbol

var()	float		BaseDifficulty;		// configured at start of single player
var()	float	   Difficulty;

var		int			SalaryCap;			// allowable salary cap for team roster

// stored here, but also passed separately on URL
var		string		PlayerName;
var		string		PlayerCharacter;

// player's stats - individual experience
var()  int			Kills;
var()  int			Goals;
var()  int			Deaths;
var()  int			Wins;
var()  int			Matches;

// Ladders:  -1 = Locked
var int LadderRung[6];
const NUM_LADDERS = 6;

var string SpecialEvent;
var string GameLadderName;
var class<LadderInfo> GameLadder;

// current match
var int CurrentLadder;
var transient int CurrentMenuRung;		// set by menu system, used for starting a match, in LadderInfo.  if -1, use next match in order
var transient Object NextMatchObject;	// Used by GUI SP Pages for holding the Button for Next Match
var transient Object ChampBorderObject;	// Used by GUI SP Pages for holding the border for Championship.  
										// Sad hack, but easiest way to communicate GUI objects between Ladder and Qual tabs
var bool bInLadderGame;		// Used to see if we should return to the SP menu after the match has finished, 
													// also used to check if we should use the LoadingClass vignette
var bool bWonMatch;


// constructor:  set up the GameLadder
function Initialize(GameInfo currentGame, string pn)
{
	local Controller C;

	if (GameLadder == none) 
	{
		GameLadder = class<LadderInfo>(DynamicLoadObject(GameLadderName, class'Class'));
	}
	PackageName=pn;
	PlayerName=pn;

	// set character, player in current game
	for ( C=currentGame.Level.ControllerList; C!=None; C=C.NextController ) 
	{
		if ( PlayerController(C) != None ) 
		{
			currentGame.ChangeName (PlayerController(C), PlayerName, false);	
			break;		
		}
	}
	NextMatchObject=None;
	ChampBorderObject=None;
}

// skip this rung of the ladder without saving
function CheatSkipMatch(GameInfo CurrentGame)
{
	SpecialEvent = GameLadder.static.UpdateLadders(self,CurrentLadder); // updates LadderRungs appropriately
	ContinueSinglePlayerGame(CurrentGame.Level);
}

// skip directly to a certain ladder/rung
// takes a single number (54) and splits into ladder 5, match 4
function CheatJumpMatch(GameInfo currentGame, int param) {
	local Controller C;
	local int newladder, newrung;

	newladder = param/10;
	newrung = param-(newladder*10);
	if (newladder < 0 || newladder >= NUM_LADDERS || newrung < 0) 
		return;
	bInLadderGame=true;
	CurrentLadder = newladder;
	LadderRung[CurrentLadder] = newrung;
	CurrentMenuRung=newrung;

	// open game
	for ( C=currentGame.Level.ControllerList; C!=None; C=C.NextController ) 
	{
		if ( PlayerController(C) != None ) 
		{
			PlayerController(C).ConsoleCommand("START"@GameLadder.static.MakeURLFor(self));
			break;		
		}
	}
}

// robust checks are in LadderInfo, returns none if not found
function MatchInfo GetMatchInfo(int ladder, int rung) {
	if ( GameLadder != none ) 
	{
		return GameLadder.static.GetMatchInfo(ladder,rung);
	} 
	else 
	{
		return none;
	}
}

// After a game is completed, this function should be called to 
// record the player's statistics and update the ladder.
// Currently called from Deathmatch 'MatchOver' state
function RegisterGame(GameInfo currentGame, PlayerReplicationInfo PRI)
{
	Log("SINGLEPLAYER GameProfile::RegisterGame for profile"@self.packagename);
	Kills += PRI.Kills;
	Goals += PRI.GoalsScored;
	Deaths += PRI.Deaths;
	Matches++;
	if ( bWonMatch ) {
		//Log("SINGLEPLAYER GameProfile::RegisterGame player won the match.");	
		SpecialEvent = GameLadder.static.UpdateLadders(self,CurrentLadder); // updates LadderRungs appropriately
		Wins++;
	}
	bWonMatch = false;
}

// Send the player to the next match in the given ladder
function StartNewMatch(int PickedLadder, LevelInfo CurrentLevel)
{
	local Controller C;

	bWonMatch = false;
	bInLadderGame=true;
	CurrentLadder = PickedLadder;
	CurrentLevel.Game.SavePackage(PackageName);
	
	// open game
	for ( C=currentLevel.ControllerList; C!=None; C=C.NextController ) 
	{
		if ( PlayerController(C) != None ) 
		{
			PlayerController(C).ConsoleCommand("START"@GameLadder.static.MakeURLFor(self));
			break;		
		}
	}
}

// Handy helper function to find the player's first unfinished ladder
// If no ladders are unfinished, return 0 for DM ladder
function int FindFirstUnfinishedLadder() {
	local int i;
	
	for (i=0; i<6; i++) 
	{		
		// 6 is magic from number of ladders, declared above
		if (LadderRung[i] < GameLadder.static.LengthOfLadder(i)) {
			return i;		
		}
	}
	return 0;
}

// override in subclasses!
function ContinueSinglePlayerGame(LevelInfo level, optional bool bReplace)
{
	Level.Game.SavePackage(PackageName);

	// the direct call to startnewmatch is to avoid using a game-specific menu system
	StartNewMatch(FindFirstUnfinishedLadder(), level);
}

// Used in menus:  this is the gametype info for the next match
function string GetMatchDescription()
{
	return GameLadder.static.GetMatchDescription(self);
}

// accessor
function static int GetNumPositions()
{
	return NUM_POSITIONS;
}

// return number of teammates needed for currently selected match
// assumes player team always gets an odd player
function int GetNumTeammatesForMatch()
{
	local MatchInfo M;

	M = GameLadder.static.GetCurrentMatchInfo(self);

	if ( M.GameType ~= "xGame.xDeathmatch" || M.GameType ~= "xGame.BossDM" ) 
		return 0;
	else
		return M.NumBots / 2;
}


function static string TextPositionDescription(int posnval) 
{
	local string retval;

	if (posnval < 0 || posnval > NUM_POSITIONS)	// magic number based on team size of 7
		return "Error";

	switch (posnval) {
		case EPlayerPos.POS_Auto:
			retval = default.PositionName[0];
			break;
		case EPlayerPos.POS_Defense:
			retval = default.PositionName[1];
			break;
		case EPlayerPos.POS_Offense:
			retval = default.PositionName[2];
			break;
		case EPlayerPos.POS_Roam:
			retval = default.PositionName[3];
			break;
/*		case EPlayerPos.POS_Captain:
			retval = "CAPTAIN";
			break;*/
		case EPlayerPos.POS_Supporting:
			retval = default.PositionName[4];
			break;
	}

	return retval;
}

function static EPlayerPos EnumPositionDescription(string posnval) 
{	
	local EPlayerPos retval;

	if (posnval == default.PositionName[0]) {
		retval = EPlayerPos.POS_Auto;
	} else if (posnval == default.PositionName[1]) {
		retval = EPlayerPos.POS_Defense;
	} else if (posnval == default.PositionName[2]) {
		retval = EPlayerPos.POS_Offense;
	} else if (posnval == default.PositionName[3]) {
		retval = EPlayerPos.POS_Roam;
	} else if (posnval == default.PositionName[4]) {
		retval = EPlayerPos.POS_Supporting;
	} else 
		retval = EPlayerPos.POS_Auto;

	return retval;
}

function string GetPositionDescription(int playernum) 
{
	if (playernum < 0 || playernum >= TEAM_SIZE)	
		return "Error";
	return TextPositionDescription(PlayerPositions[playernum]);
}

// takes a lineup position (1-4) and sets that player's game position
function SetPosition (int lineupnum, string posn)
{
	if ((lineupnum >= 0) && (lineupnum < 4))
		PlayerPositions[PlayerLineup[lineupnum]] = EnumPositionDescription(posn);
}

// called when adjusting the lineup
// takes lineup position (1-4) and team position (1-7) and makes it all work
function SetLineup (int lineuppos, int teampos)
{
	local int oldlineuppos, oldteammate, i;

	// check bounds
	if ( lineuppos < 0 || lineuppos > LINEUP_SIZE ) 
		return;
	if ( teampos < 0 || teampos >= TEAM_SIZE )
		return;
	if ( PlayerLineup[lineuppos] == teampos )	// no-op
		return;

	// check to see if player 'teampos' was already in the lineup 
	oldlineuppos=-1;
	for ( i=0; i<LINEUP_SIZE; i++ )
	{
		if ( PlayerLineup[i] == teampos ) 
		{
			oldlineuppos = i;
			break;
		}
	}
	if ( oldlineuppos >= 0 ) {
		oldteammate = PlayerLineup[lineuppos];
	}
	PlayerLineup[lineuppos] = teampos;
	if ( oldlineuppos >= 0 ) {
		PlayerLineup[oldlineuppos] = oldteammate;
	}
}

// add teammate to the next available position on the team
// return false if not added because already on team or no room
// assumes it's a legal player record
function bool AddTeammate(string botname) 
{
	local int i;
	
	if ( botname == "" ) 
	{
		return false;
	}

	for ( i=0; i<TEAM_SIZE; i++ ) 
	{
		if ( i >= PlayerTeam.Length || PlayerTeam[i] == "" ) 
		{
			Playerteam[i] = botname;
			return true;
		}
		if ( PlayerTeam[i] ~= botname ) 
		{
			// already on team
			return false;
		}
	}

	return false;  // never found space
}

// remove teammate from the team
// return false if not removed because not on team 
function bool ReleaseTeammate(string botname) 
{
	local int i, j;
	
	if ( botname == "" ) 
	{
		return false;
	}

	for ( i=0; i<PlayerTeam.Length; i++ ) 
	{
		if ( PlayerTeam[i] ~= botname ) 
		{
			// player is on team, shuffle list 
			for ( j=i; j<PlayerTeam.Length-1; j++ ) 
			{
				PlayerTeam[j] = PlayerTeam[j+1];
			}
			PlayerTeam[PlayerTeam.Length-1] = "";
			return true;
		}
	}

	return false;  // never found botname
}

function ClearTeammates() 
{
	local int i;
	for ( i=0; i<PlayerTeam.Length; i++ ) 
	{
		PlayerTeam[i] = "";
	}
}

function ReportCheat(PlayerController Cheater, string cheat);

function bool CanChangeTeam(Controller Other, int NewTeam)
{
	return true;
}

defaultproperties
{
	GameLadderName="Engine.LadderInfo"
	LadderRung(0)=0
	LadderRung(1)=-1
	LadderRung(2)=-1
	LadderRung(3)=-1
	LadderRung(4)=-1
	LadderRung(5)=-1
	
	PositionName(0)="AUTO-ASSIGN"
	PositionName(1)="DEFENSE"
	PositionName(2)="OFFENSE"
	PositionName(3)="ROAM"
	PositionName(4)="SUPPORT"

	PlayerLineup(0)=0
	PlayerLineup(1)=1
	PlayerLineup(2)=2
	PlayerLineup(3)=3

	BaseDifficulty=1
	PlayerName="Name"
	PlayerCharacter="Roc"
	PlayerTeam="TeamName"
	PackageName="Default"
	Kills=0
	Goals=0
	Deaths=0
	bInLadderGame=false
}
