class ServerPopulation extends Mutator config(ServerPopulation);

var bool init;
var bool postInit;

var string packages, actors;

var config int lastKnownHour;
var config int lastDay;

var const int HOUR_COUNT;
var const int DAY_COUNT;
var const int WEEK_COUNT;

var config struct HourStat
{
	var int sum;
		
	var int Day, Month, Year;
	
	var float avg24Hours;
} hours[24];


var config struct DayStat
{
	var int sum;
		
	//date
	var int Year, Month, Day, DayOfWeek;
	
	var float avg7Days;
} days[50];


function PreBeginPlay()
{
	if(init)
		return;
		
	Log(">>ServerPopulation mod started");
	init = True;
	
	SetTimer(4, False);//loop
	
	NewHour_R_Day(True);
}

function PostBeginPlay()
{
	if(postInit)
		return;
		
	postInit = True;
	
	if(MutatorMissing(Self.class))
	{
		Log("ServerPopulation adds itself to the mutator chain");
		Level.Game.BaseMutator.AddMutator(self);
	}
}


function NewHour_R_Day(bool fillHoursSkipped)
{
	local bool bSave;
	local int i, sum, k;
	
	
	//check if we have a new hour
	if(lastKnownHour != Level.Hour)
	{
		//Log("new hour");
		if(fillHoursSkipped)
		{
			i = Abs(lastKnownHour - Level.Hour); 
			if(i > 1 && i < 23)//skipped some hours (down)
			{
				//Log("delta: " $ i);
				//all hours after lastKnown hour before current hour
				if(Level.Hour > lastKnownHour)
					k = Level.Hour;
				else
					k = Level.Hour + 24;
				for(i = lastKnownHour + 1; i < k; i++)
				{
					if(i > 23)
						hours[i-24].sum = 0;//@todo avg24h=0 too <-> self-add remove?
					else
						hours[i].sum = 0;
				}
			}
		}

		
		sum = 0;
		//last 24 hours
		for(i = 0;i<24;i++)
		{
			sum += hours[i].sum;
			//Log("24h calc: - " $ hours[i].sum $ " // " $ hours[i].count);
		}
		hours[lastKnownHour].avg24Hours = float(sum)/DAY_COUNT;
			
		//reference new hour
		lastKnownHour = Level.Hour;
		hours[lastKnownHour].Year = Level.Year;
		hours[lastKnownHour].Month = Level.Month;
		hours[lastKnownHour].Day = Level.Day;
		
		hours[lastKnownHour].sum = 0;
		hours[lastKnownHour].avg24hours = 0;
				
		bSave = True;
	}
	else if(fillHoursSkipped && 
		(hours[lastKnownHour].Day != Level.Day || 
		hours[lastKnownHour].Month != Level.Month ||
		hours[lastKnownHour].Year != Level.Year)//booting @ same hour but different date
		)
		{
			//new date
			hours[lastKnownHour].Year = Level.Year;
			hours[lastKnownHour].Month = Level.Month;
			hours[lastKnownHour].Day = Level.Day;

			//clear all old hour-data
			for(i=0;i<24;i++)
				hours[i].sum = 0;
						
			bSave = True;
		}
	
	//new day?
	if(days[lastDay].Day != Level.Day || 
		days[lastDay].Month != Level.Month || 
		days[lastDay].Year != Level.Year)
	{
		
		sum = 0;
		//last 7 days (may be wrong)
		for(i = lastDay + 50;i>lastDay + 43;i--)
		{
			if(i >= 50)
				k = i - 50;
			else
				k = i;
				
			sum += days[k].sum;
		}
		
		days[lastDay].avg7Days = float(sum)/WEEK_COUNT;

		//next entry -> INIT
		lastDay ++;
		if(lastDay >= 50)
			lastDay -= 50;
			
		days[lastDay].Year = Level.Year;
		days[lastDay].Month = Level.Month;
		days[lastDay].Day = Level.Day;
		days[lastDay].DayOfWeek = Level.DayOfWeek;
		
		days[lastDay].sum = 0;
		days[lastDay].avg7Days = 0;
				
		bSave = True;
	}
	
	if(bSave)
		SaveConfig();//make sure new data is not lost
}


function Timer()
{
	NewHour_R_Day(False);	
	
	//add data
	//current hour
	hours[lastKnownHour].sum += Level.Game.NumPlayers;
	
	//current day
	days[lastDay].sum += Level.Game.NumPlayers;
	
	//Log("Added: " $ Level.Game.NumPlayers);
	
	if(Level.Game.bGameEnded || Level.NextURL != "")//game ends soon
	{
		//SetTimer(0.0, False);
		//BroadcastMessage("game ends now");
		SaveConfig(); // SAVE THE DATA
		//Self.Destroy();
	}
	
	if(Self.TimerRate == 0)
		SetTimer(15, True);
}

function Mutate(string MutateString, PlayerPawn Sender)
{
	if ( NextMutator != None )
		NextMutator.Mutate(MutateString, Sender);
		
		
	if(MutateString ~= "pop days")
	{
		PrintDays(Sender);
	}
	else if(MutateString ~= "pop hours")
	{
		PrintHours(Sender);
	}
	/*else if(MutateString ~= "saveconfig")
	{
		Self.SaveConfig();
	}*/	
	else if(MutateString ~= "actorsandpackages")
	{
		if(packages == "")
			packages = ConsoleCommand("get ini:Engine.Engine.GameEngine ServerPackages");
		if(actors == "")
			actors = ConsoleCommand("get ini:Engine.Engine.GameEngine ServerActors");
		
		Sender.ClientMessage("PCKS: " $ packages);
		Sender.ClientMessage("ACTS: " $ actors);
	}
}

function PrintHours(Pawn p)
{
	local int i, k, sum;
	local bool bLastWasEmpty;
	
	bLastWasEmpty = True;
	
	for(i = lastKnownHour + 1; i < lastKnownHour + 25;i++)
	{
		if(i >= 24)
			k = i - 24;
		else
			k = i;

		if(hours[k].Year == 0 ||
			hours[k].Month == 0 ||
			hours[k].Day == 0 ||
			hours[k].sum == 0)
			{
				if(!bLastWasEmpty)
					p.ClientMessage("---");//no data
				bLastWasEmpty = True;
				continue;
			}
			
			bLastWasEmpty = False;
				
		if(k != lastKnownHour)
			p.ClientMessage("[" $hours[k].Year $ "-" $ TwoDigitString(hours[k].Month) $ "-" $ TwoDigitString(hours[k].Day)
				$ " | hour " $ TwoDigitString(k) $ " - " $ TwoDigitString(k+1) $ "] avg = " $ float(hours[k].sum)/HOUR_COUNT $ "; last 24 hours " $ hours[k].avg24Hours);
		else
			p.ClientMessage("[" $hours[k].Year $ "-" $ TwoDigitString(hours[k].Month) $ "-" $ TwoDigitString(hours[k].Day)
				$ " | hour " $ TwoDigitString(k) $ " - " $ TwoDigitString(k+1) $ "] avg = " $ float(hours[k].sum)/HOUR_COUNT);
	}
	
	p.ClientMessage("current hour is not complete!");
}


function PrintDays(Pawn p)
{
	local int i, k;
	
	for(i = lastDay + 1;i < lastDay + 51;i++)
	{
		if(i >= 50)
			k = i - 50;
		else
			k = i;
		
		if(days[k].Day == 0 ||
			days[k].Year == 0 ||
			days[k].Month == 0 ||
			days[k].sum == 0)
				continue;
		
		if(k != lastDay)
			p.ClientMessage("[" $ days[k].Year $ "-" $ TwoDigitString(days[k].Month) $ "-" $ TwoDigitString(days[k].Day) @ DayOfWeekToString(days[k].DayOfWeek) $ "] " $
				float(days[k].sum)/DAY_COUNT $ "; last 7 days: " $ days[k].avg7Days);
		else
			p.ClientMessage("[" $ days[k].Year $ "-" $ TwoDigitString(days[k].Month) $ "-" $ TwoDigitString(days[k].Day) @ DayOfWeekToString(days[k].DayOfWeek) $ "] " $
				float(days[k].sum)/DAY_COUNT);
	}
	
	p.ClientMessage("current day is not complete!");
}

function string DayOfWeekToString(int d)
{
	switch(d)
	{
		case 0: return "SUN";
		case 1: return "MON";
		case 2: return "TUE";
		case 3: return "WED";
		case 4: return "THU";
		case 5: return "FRI";
		case 6: return "SAT";
		default: return "   ";
	}
}

function string TwoDigitString(int Num)
{
    if ( Num < 10 )
        return "0"$Num;
    else
        return string(Num);
}

function bool MutatorMissing(class c)
{
	local Mutator m;
	
	for(m = Level.Game.BaseMutator;m != None;m = m.NextMutator)
	{
		if(m.class == c)
			return False;
	}
	return True;
}

function AddMutator(Mutator Other) 
{
if (Other != None && Other.Class == Self.Class) 
{
	if (Other != Self) 
		Other.Destroy();
} 
else 
	Super.AddMutator(Other);

}

defaultproperties
{
HOUR_COUNT=260
DAY_COUNT=6240
WEEK_COUNT=43680
}
