class AMC extends Mutator
	config(AdjustingMapcycle);

struct MapConfig
{
	var string mapname;
	var int minAvgPlayers;
	var int maxAvgPlayers;
	var int skipCount;
};	

var config MapConfig mapcycle[32];
var config int currentIndex;
var bool bPreInit;
var bool bMsgShown;
//////////////////////
var Pawn skipVotes[16];
var config float skipPercentage;
//////////////////////
var config float avgPopulation;
//////////////////////
var bool bNotEnded;

var string Reason;

function PreBeginPlay() 
{
	local int i;
	local string currMap;
	
	if(bPreInit)
		return;
	
	bPreInit = True;
	Level.Game.RegisterMessageMutator(Self);
	
	SetTimer(FMax(0.25, TeamGamePlus(Level.Game).NetWait - Level.TimeSeconds - 2), False);
	
	for(i = 0;i<32;i++)//@debug
	{
		if(mapcycle[i].mapname != "")
			Log(mapcycle[i].mapname $
				" ||| " $ mapcycle[i].minAvgPlayers $
				" - " $ mapcycle[i].maxAvgPlayers);
	}

	currMap = GetLevelName();
	Log("map is " $ currMap $ " should be " $ mapcycle[currentIndex].mapname);
	
	if(InStr(mapcycle[currentIndex].mapname, currMap) == -1)
		FindMapIndex(currMap);
	
}

function FindMapIndex(string currMap)
{
	local int i;
	
	for(i = 0;i<32;i++)
	{
		if(InStr(mapcycle[i].mapname, currMap) != -1)//found
		{
			currentIndex = i;
			Log("map is matching with " $ i);
			return;
		}
	}
	bMsgShown = True;
	//not found@Todo CONFIG timer booleans for change!!!!...
	NextMap(False);//???not changing - without bots (C-S)
}

function Timer()
{
	if(!bMsgShown)
	{
		Self.BroadcastMessage("If you want to see this map skipped say !skip.");
		Self.BroadcastMessage(int(100 * skipPercentage) $ "% !skip-votes needed.");
		Self.BroadcastMessage(Left(mapcycle[currentIndex].mapName, Len(mapcycle[currentIndex].mapname) - 4) $ " used for " $ 
			mapcycle[currentIndex].minAvgPlayers $ " - " $ mapcycle[currentIndex].maxAvgPlayers $
			" players.");
		
		//next calc avg population every minute
		bMsgShown = True;
		
		bNotEnded = True;
		SetTimer(60 * Level.TimeDilation, True);
		return;
	}

	if(bNotEnded)
	{
		avgPopulation += (Level.Game.NumPlayers - avgPopulation) / 4;
		Log(Level.Game.NumPlayers $ " -> " $ avgPopulation);
		
		//@todo current map: avg pop going < 0.2 but minavgplayers is > 0 -> nextmap!
		if(avgPopulation < 0.2 && mapcycle[currentIndex].minAvgPlayers > 0)
		{
			if(NextMap(True))//can find a better map?
			{
				Self.BroadcastMessage("Auto-Skipping this map", True, 'CriticalEvent');
				EndGameForSkipping();
			}
		}
			
	}
	else //delay after we ended game
	{
		Level.ServerTravel(mapcycle[currentIndex].mapname, False);
		//Level.Game.ProcessServerTravel(mapcycle[currentIndex].mapname, False);
	}
}

function bool MutatorTeamMessage(Actor Sender, Pawn Receiver, PlayerReplicationInfo PRI, coerce string Msg, name Type, optional bool bBeep) 
{
	if(Sender == Receiver) 
	{ 
		if(Left(Msg, 5) ~= "!skip") 
		{ 
			if(Level.TimeSeconds < 35)
			{
				Receiver.ClientMessage("Wait for " $ int(1 + (35 - Level.TimeSeconds)/Level.TimeDilation) $ " seconds then say it again.");
			}
			else
				ProcessSkipVote(Receiver);
		}
	}
  
	if(NextMessageMutator != None)
		return NextMessageMutator.MutatorTeamMessage( Sender, Receiver, PRI, Msg, Type, bBeep );
	else
		return true;
}

function ProcessSkipVote(Pawn p)
{
	local int i, k;
	local bool bDone;

	for(i = 0; i < 16; i++)
	{
		if(p == skipVotes[i])
			return;//already voted
	}
	
	//clean
	for(i = 0; i < 16; i++)
	{
		if(skipVotes[i] == None)
		{
			if(!bDone)
			{
				skipVotes[i] = p;
				k++;
				bDone = True;
			}
		}
		else //count
			k++;
	}
	
	Self.BroadcastMessage(k $ " valid skip-votes atm.");
	
	if(k >= skipPercentage * Level.Game.NumPlayers)
	{
		if(!NextMap(True))
		{
			BroadcastMessage("No other map matching current population", True, 'CriticalEvent');
		}
		else
		{
			Self.BroadcastMessage("Skipping this map", True, 'CriticalEvent');
			EndGameForSkipping();
		}
	}
}

function bool Nextmap(bool skipMap)
{
	local int oldIndex;
	
	oldIndex = currentIndex;

	Log(currentIndex $ " old");
	//find a matching map -> maybe little offset due to slow convergence in the end
	while(mapcycle[++currentIndex].mapname == "" || 
		mapcycle[currentIndex].minAvgPlayers > avgPopulation + 0.2 ||
		mapcycle[currentIndex].maxAvgPlayers < avgPopulation - 0.2)
		{
			if(currentIndex == oldIndex)//no els
				break;
				
			if(currentIndex == 31)//wrap
				currentIndex = -1;
		}
	Log(currentIndex $ " new");
	
	if(currentIndex == oldIndex)//ending on the same map!
		return False;

	SaveConfig();
	
	//prepare
	DeathMatchPlus(Level.Game).bDontRestart = True;
	
	//last calculation
	avgPopulation += (Level.Game.NumPlayers - avgPopulation) / 4;
	SaveConfig();//save now
	Log(Level.Game.NumPlayers $ " ->NM " $ avgPopulation);
	
	//delay
	bNotEnded = False;//delay mode in timer()
	if(skipMap)
		SetTimer(10, False);
	else
		SetTimer(15, False);
		
	return True;
}


function bool HandleEndGame()
{
	Super.HandleEndGame();
	
	
	if(True)//tgp.teams[0].Score != tgp.teams[1].Score)@todo -> determine ?TDM only
	{
		//find next suitable map - and start end-game timer
		NextMap(False);
		//show winner
		Level.Game.SetEndCams("teamscorelimit");
		
		return True;
	}
	else
		return False;
}

function EndGameForSkipping()
{
	local Actor A;
	
	mapCycle[currentIndex].skipCount ++;
				
	Level.Game.bGameEnded = true;
	foreach AllActors(class'Actor', A, 'EndGame')
		A.trigger(Level.Game, none);

	if (Level.Game.LocalLog != None)
	{
		Level.Game.LocalLog.LogGameEnd(Reason);
		Level.Game.LocalLog.StopLog();
		if (Level.Game.bBatchLocal)
			Level.Game.LocalLog.ExecuteSilentLogBatcher();
		Level.Game.LocalLog.Destroy();
		Level.Game.LocalLog = None;
	}
	if (Level.Game.WorldLog != None)
	{
		Level.Game.WorldLog.LogGameEnd(Reason);
		Level.Game.WorldLog.StopLog();
		Level.Game.WorldLog.ExecuteWorldLogBatcher();
		Level.Game.WorldLog.Destroy();
		Level.Game.WorldLog = None;
	}
}

function Mutate(string MutateString, PlayerPawn Sender)
{
	local int i;
	if(MutateString ~= "showskips")
	{
		for(i = 0;i < 32;i++)
		{
			if(mapcycle[i].mapname != "")
				Sender.ClientMessage(mapcycle[i].skipCount $ " skips for " $ Left(mapcycle[i].mapname, Len(mapcycle[i].mapname) - 4));
		}
	}
	else if(Sender.bAdmin && MutateString ~= "resetskipcounts")
	{
		for(i = 0;i < 32;i++)
			mapcycle[i].skipCount = 0;
	}

	if ( NextMutator != None )
		NextMutator.Mutate(MutateString, Sender);
}

function string GetLevelName()
{
	local string Str;
	local int Pos;

	Str = string(Level);
	Pos = InStr(Str, ".");
	if(Pos != -1)
		return Left(Str, Pos);
	else
		return Str;
}



defaultproperties
{
	skipPercentage=0.6
	Reason="Players want to skip this map."
}