/*
    AdjustingMapcycle version 1.2b
    Copyright (C) 2011 n0nexe
    
    This program is free software; you can redistribute and/or modify
    it under the terms of the Open Unreal Mod License version 1.1.
*/

class AMC extends Mutator
	config(AdjustingMapcycle);

var enum eTimerState
{
	TS_Message,
	TS_Checks,
	TS_End_wait_I,
	TS_End_wait_II,
} TimerState;
	
struct MapConfig
{
	var string mapname;
	var int minPlayers;
	var int maxPlayers;
	var int skipCount;
};	

var config MapConfig mapcycle[50];
var config int currentIndex;
var bool bPreInit;
var bool bNextMapChosen;

var Pawn skipVotes[32];
var config float skipPercentage;
var config bool bMapvoteCoop;
var config bool bNoTimeEdit;
var bool bUncycledMap;

var config int END_WAIT_I;
var config int END_WAIT_II;

var int emptyCount;

var string Reason;

var string ASC_leftMsg;
var string lastMsg;
var int leftMsgLength;



function PreBeginPlay() 
{
	local string currMap;
	local int i;
	
	if(bPreInit)
		return;

	bPreInit = True;
	if(Level.Game.class != class'TeamGamePlus')
	{
		Log("This is not TDM.");
		Self.Destroy();
		bPreInit = True;
		return;
	}
	
	Level.Game.RegisterMessageMutator(Self);
	leftMsgLength = Len(ASC_leftMsg);//Len(Level.Game.LeftMessage);

	//find map in list/move to next map on the list
	currMap = GetLevelName();
		
	if(InStr(mapcycle[currentIndex].mapname, currMap) == -1)
		FindMapIndex(currMap $ ".unr");
	
	TimerState = TS_Message;
	SetTimer(FMax(0.25, TeamGamePlus(Level.Game).NetWait - Level.TimeSeconds + 0.2), False);
}

function FindMapIndex(string currMap)
{
	local int i;
	
	for(i = 0;i<50;i++)
	{
		if(mapcycle[i].mapname ~= currMap)
		{
			currentIndex = i;
			
			SaveConfig();
			return;
		}
	}
	
	if(bMapvoteCoop)//whatever map is on
	{
		bUncycledMap = True;//map is not on the cycle
		return;
	}
		
	Self.BroadcastMessage("Invalid map -> changing map now!", True);
	
	NextMap(True);
	InitWaitII();
}

function InitWaitII()
{
	TimerState = TS_End_wait_II;
	SetTimer(END_WAIT_II, False);
	bNextMapChosen = True;
}

function Timer()
{
	local int oldIndex;
	
	switch(TimerState)
	{
		case TS_Message: 
			Self.BroadcastMessage("If you want to play the next map -> say !skip.");
			Self.BroadcastMessage(int(100 * skipPercentage) $ "% !skip-votes needed.");
				
			if(!bUncycledMap)
			{
				Self.BroadcastMessage(Left(mapcycle[currentIndex].mapName, Len(mapcycle[currentIndex].mapname) - 4) $ " is used for " $ 
					mapcycle[currentIndex].minPlayers $ " - " $ mapcycle[currentIndex].maxPlayers $
					" players. Check the !maplist.");
			}
			
			TimerState = TS_Checks;
			SetTimer(44, True);
			break;
			
		case TS_Checks: 
			
			if(!bUncycledMap && Level.Game.NumPlayers < mapcycle[currentIndex].minPlayers)
				Self.BroadcastMessage("Map is too big now? - Then say !skip.");
			
			if(Level.Game.NumPlayers == 0)
				emptyCount ++;
			else
				emptyCount = 0;
			
			//empty server: unknown map or not suitable -> change
			if(emptyCount >= 4 && (bUncycledMap || mapcycle[currentIndex].minPlayers > 0))
			{
				oldIndex = currentIndex;
				
				if(NextMap())
				{
					emptyCount = 0;
					Self.BroadcastMessage("Auto-Skipping this map", True, 'CriticalEvent');
					EndThisGameForSkipping(oldIndex, True);
					InitWaitII();
				}
			}
			break;
			
		case TS_End_wait_I:
			
			NextMap(True);
			
			InitWaitII();
			break;
			
		case TS_End_wait_II: 
			if(!bNoTimeEdit)
				Level.NextSwitchCountdown = 1;//?fast mapchange(UTdelay)
			Level.ServerTravel(mapcycle[currentIndex].mapname $ "?game=Botpack.TeamGamePlus", False);
			break;
	}
}

function bool MutatorBroadcastMessage( Actor Sender, Pawn Receiver, out coerce string Msg, optional bool bBeep, out optional name Type )
{
	if(Type == 'Event' && Msg != lastMsg)
	{
		if(Right(Msg, LeftMsgLength) == ASC_leftMsg)//Level.Game.LeftMessage)
			ProcessSkipVote(None, Left(Msg, Len(Msg) - LeftMsgLength));
			
		lastMsg = Msg;
	}
	
	//spectators may see the maplist too
	if(Type == 'Event' && Sender == Receiver && 
		Receiver.PlayerReplicationInfo.bIsSpectator && Mid(Msg, Len(Receiver.PlayerReplicationInfo.PlayerName)) ~= ":!maplist")
	{
		PrintMaplist(Receiver);
	}

	if ( NextMessageMutator != None )
		return NextMessageMutator.MutatorBroadcastMessage( Sender, Receiver, Msg, bBeep, Type );
	else
		return true;
}

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

function PrintMaplist(Pawn Receiver)
{
	local int i, k;
	k = 1;
	for(i = 0;i < 50;i++)
	{
		if(mapcycle[i].mapname != "")
		{
			if(i == currentIndex)
			{
				if(bUncycledMap)
					Receiver.ClientMessage(k++ $ ".) " $ Left(mapcycle[i].mapname, Len(mapcycle[i].mapname) - 4) $ " -----  for " $ 
						mapcycle[i].minPlayers $ " - " $ mapcycle[i].maxPlayers $ " players / received " $ mapcycle[i].skipcount $ " skips. <<< LAST CYCLED");
				else
				{
					if(!bNextMapChosen)
						Receiver.ClientMessage(k++ $ ".) " $ Left(mapcycle[i].mapname, Len(mapcycle[i].mapname) - 4) $ " -----  for " $ 
							mapcycle[i].minPlayers $ " - " $ mapcycle[i].maxPlayers $ " players / received " $ mapcycle[i].skipcount $ " skips. <<< NOW");
					else
						Receiver.ClientMessage(k++ $ ".) " $ Left(mapcycle[i].mapname, Len(mapcycle[i].mapname) - 4) $ " -----  for " $ 
							mapcycle[i].minPlayers $ " - " $ mapcycle[i].maxPlayers $ " players / received " $ mapcycle[i].skipcount $ " skips. <<< NEXT");
				}
			}
			else
				Receiver.ClientMessage(k++ $ ".) " $ Left(mapcycle[i].mapname, Len(mapcycle[i].mapname) - 4) $ " -----  for " $ 
					mapcycle[i].minPlayers $ " - " $ mapcycle[i].maxPlayers $ " players / received " $ mapcycle[i].skipcount $ " skips.");
		}
	}
}

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

	if(p != None)
	{
		for(i = 0; i < 32; i++)
		{
			if(p == skipVotes[i])
			{
				p.ClientMessage("Already got your vote!");
				return;
			}
		}
	}
	
	
	for(i = 0; i < 32; i++)
	{
		if(skipVotes[i] == None)
		{
			if(p != None && !bDone)
			{
				skipVotes[i] = p;
				k++;
				bDone = True;
			}
		}
		else
		{
			if(noVote == "" || skipVotes[i].PlayerReplicationInfo.PlayerName != noVote)
				k++;
			else //remove vote
			{
				skipVotes[i] = None;
			}
		}
	}
	
	Self.BroadcastMessage(k $ " valid skip-votes atm.");
	i = currentIndex;
	if(k > 0 && k >= skipPercentage * Level.Game.NumPlayers)
	{
		if(!NextMap())
		{
			BroadcastMessage("No other map matching current population.", True, 'CriticalEvent');
		}
		else
		{
			Self.BroadcastMessage("Skipping this map!", True, 'CriticalEvent');
			EndThisGameForSkipping(i, False);
			InitWaitII();
		}
	}
}

function bool Nextmap(optional bool noReturn)
{
	local int oldIndex;

	oldIndex = currentIndex;

	while(mapcycle[++currentIndex].mapname == "" || !VerifyMap(currentIndex) ||
		mapcycle[currentIndex].minPlayers > Level.Game.NumPlayers ||
		mapcycle[currentIndex].maxPlayers < Level.Game.NumPlayers)
		{
			if(currentIndex == oldIndex)
				break;
				
			if(currentIndex == 49)
				currentIndex = -1;
		}
	
	
	if(!noReturn && currentIndex == oldIndex)
		return False;

	SaveConfig();
		
	DeathMatchPlus(Level.Game).bDontRestart = True;
	
	return True;
}


function bool HandleEndGame()
{
	local TeamGamePlus tgp;
	
	if(!bMapvoteCoop)
		Super.HandleEndGame();
	
	tgp = TeamGamePlus(Level.Game);
	
	
	if(tgp != None && 
		tgp.teams[0].Score != tgp.teams[1].Score)
	{
		TimerState = TS_End_wait_I;
		SetTimer(END_WAIT_I, False);
				
		tgp.SetEndCams("teamscorelimit");
		
		return True;
	}
	else
		return Super.HandleEndGame();
}

function EndThisGameForSkipping(int oldIndex, bool bAuto)
{
	local Actor A;
	local pawn aPawn;

	for ( aPawn=Level.PawnList; aPawn!=None; aPawn=aPawn.NextPawn )
		if ( aPawn.bIsPlayer )
		{
			aPawn.GotoState('GameEnded');
			aPawn.ClientGameEnded();
		}	
	
	if(!bAuto && !bUncycledMap)
	{
		mapCycle[oldIndex].skipCount ++;
		SaveConfig();
	}

	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, empty;
	local string temp;
	
	if(MutateString ~= "maplist")
	{
		PrintMaplist(Sender);
	}
	else if(Sender.bAdmin)
	{
		if(MutateString ~= "resetskipcounts")
		{
			for(i = 0;i < 50;i++)
				mapcycle[i].skipCount = 0;
				
			Sender.ClientMessage("Done.");
			SaveConfig();
		}
		else if(Left(MutateString, 10) ~= "removemap ")
		{
			MutateString = Mid(MutateString, 10);
			
			if(MutateString ~= (GetLevelName() $ ".unr"))
			{
				Sender.ClientMessage("Removing map just played is not allowed.");
				return;
			}
			
			for(i = 0;i < 50;i++)
			{
				if(MutateString ~= mapcycle[i].mapname)
				{
					mapcycle[i].mapname = "";
					mapcycle[i].minPlayers = 0;
					mapcycle[i].maxPlayers = 0;
					mapcycle[i].skipCount = 0;
					SaveConfig();
					
					Sender.ClientMessage(MutateString $ " has been removed.");
					return;
				}
			}
		}
		else if(Left(MutateString, 7) ~= "addmap ")
		{
			
			MutateString = Mid(MutateString, 7);
			i = InStr(MutateString, "/");
			if(i == -1)
				return;
			
			temp = Left(MutateString, i);
			
			MutateString = Mid(MutateString, i+1);
		
			empty = -1;	
			
			for(i = 0; i < 50; i ++)
			{
				if(empty == -1 && mapcycle[i].mapname == "")
					empty = i;
				
				
				if(mapcycle[i].mapname ~= temp)
				{
					SetMapEntry(i, temp, MutateString, Sender, True);
					return;
				}
			}
			
			if(empty != -1)
				SetMapEntry(empty, temp, MutateString, Sender, False);
			else
				Sender.ClientMessage("Cycle if full.");
		}
	}

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

function SetMapEntry(int index, string mapname, string rest, Pawn Sender, bool bEdit)
{
	local int i;
	local string temp;

	if(!(Right(mapname, 4) ~= ".unr"))
	{
		Sender.ClientMessage("MAPNAME.UNR/MIN/MAX plz");
		return;
	}
	
	mapcycle[index].mapname = mapname;
	
	
	i = InStr(rest, "/");
	temp = Left(rest, i);
	rest = Mid(rest, i+1);
	
	mapcycle[index].minPlayers = int(temp);
	
	mapcycle[index].maxPlayers = int(rest);
	if(!bEdit)
		mapcycle[index].skipCount = 0;
	
	Sender.ClientMessage("new/edited entry: " $ mapcycle[index].mapname $ " for " $ 
		mapcycle[index].minPlayers $ " - " $ mapcycle[index].maxPlayers);
		
	SaveConfig();
}

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;
}

function bool VerifyMap(int index)
{
	if(Self.GetMapName("DM-", mapcycle[index].mapName, 0) != mapcycle[index].mapName)
	{
		LOG("MAP IS NOT ON THE SERVER [ " $ mapcycle[index].mapName $ "] - REMOVED from AdjustingMapcycle");
		mapcycle[index].mapName = "";
		mapcycle[index].minPlayers = 0;
		mapcycle[index].maxPlayers = 0;
		mapcycle[index].skipCount = 0;
		return False;
	}
	else
		return True;
}


defaultproperties
{
	skipPercentage=0.6
	Reason="MAP_SKIPPED"
	TimerState=TS_Message
	END_WAIT_I=12
	END_WAIT_II=3
	ASC_leftMsg=" has disconnected from the server"
}