//Copyright (C) Ben Parbury - All Rights Reserved. Unauthorized copying of this file, via any medium is strictly prohibited. Written by Ben Parbury <ben@gridlockinteractive.com> 2014
var gRecordingSystem = new RecordingSystem();
var gGhostKeyOffset = 256;

//upload recordings when user is navigating away
if(!gEditor)
{
	window.onbeforeunload = function (e)
	{
		gStatsSystem.Clear();	//Save stats...
		gTelemetry.PostStat("unload");
		gRecordingSystem.Upload();
	};
}

function RecordingSystem()
{
	//For recording only
	this.isRecording = false;
	this.recordingCounter = undefined;
	this.records = undefined;
	
	//For playback only
	this.isPlayback = false;
	this.isLoading = false;
	this.ghost = false;
	this.playbackCounter = undefined;
	this.playRecords = undefined;
	
	//Called on level loads...
	this.Init = function()
	{
		if(this.isPlayback)
		{
			this.FinishPlayback();
		}
		
		var urlVars = getUrlVars();
		if(urlVars["replay"])
		{
			var filename = "data/levels/" + urlVars["set"] + "/" + urlVars["replay"] + ".xml";
			this.RequestLoad(filename);
		}
		else if(urlVars["replayURL"])
		{
			var filename = urlVars["replayURL"];
			this.RequestLoad(filename);
		}
		else if(urlVars["ghost"])
		{
			this.ghost = true;
			var filename = "data/levels/" + urlVars["set"] + "/" + urlVars["ghost"] + ".xml";
			this.RequestLoad(filename);
			this.Record();
		}
		else
		{
			this.Record();
		}
	}
	
	this.ToggleGhost = function()
	{
		if(this.isPlayback)
		{
			this.FinishPlayback();
		}
		else
		{
			this.ghost = true;
			this.RequestLoad("data/levels/" + gLevelSystem.GetLevelSet() + "/" + gLevelSystem.GetLevelName() + "_ghost.xml");
		}
	}
	
	this.RequestLoad = function(filename)
	{
		this.isLoading = true;
		OpenXML(filename,
			function(response)
			{
				gRecordingSystem.Load(response);
			});			
	}
	
	this.Load = function(xmlString)
	{
		this.playbackCounter = 0;
		this.isPlayback = false;
		this.playRecords = new Array();
		
		xmlDoc = ParseXMLString(xmlString);
		this.LoadHeader(xmlDoc.getElementsByTagName("Replay"));
		this.LoadRecords(xmlDoc.getElementsByTagName("record"));
		
		this.Playback();
		
		this.isLoading = false;
	}
	
	this.LoadHeader = function(replayArray)
	{
		gConsole.Log(3, "[RS] Loading Header");
		if(replayArray.length == 1)
		{
			var previousSet = replayArray[0].getAttribute('previousSet');
			var previousLevel = replayArray[0].getAttribute('previousLevel');
			if(previousSet && previousLevel)
			{
				gLevelSystem.previousLevelSet = previousSet;
				gLevelSystem.previousLevelName = previousLevel;
			}
		}
	}

	this.LoadRecords = function(recordsArray)
	{
		gConsole.Log(3, "[RS] Loading " + recordsArray.length + " Records");
		for(var i = 0; i < recordsArray.length; i++)
		{
			var type = recordsArray[i].getAttribute("type");
			this.LoadRecord(type, recordsArray[i]);
		}
	}
	
	this.LoadRecord = function(typename, xml)
	{
		var record = undefined;
		switch(typename)
		{
			case RecordKeyUp.GetTypename():
			{
				record = new RecordKeyUp();
			}
			break;
			case RecordKeyDown.GetTypename():
			{
				record = new RecordKeyDown();
			}
			break;
		}
		
		if(record != undefined)
		{
			record.LoadXml(xml, this.ghost);
			this.playRecords.push(record);
		}
		else
		{
			gConsole.Assert("Unhandled data");
		}
	}
	
	this.Record = function()
	{
		if(gIni.Get("game", "replays", 0) == 1)
		{
			gConsole.Log(6, "[RS] Recording...");
			gLevelSystem.SetRandomSeed();
			this.recordingCounter = 0;
			this.records = new Array();
			this.isRecording = true;
		}
	}
	
	this.Playback = function()
	{
		if(this.playRecords.length > 0 && this.isPlayback == false)
		{
			gConsole.Log(6, "[RS] Playback" + this.ghost ? " Ghost" : "");
			gLevelSystem.SetRandomSeed();
			this.playback = this.playRecords.slice();
			if(this.ghost == false)
			{
				this.isRecording = false;
			}
			else
			{
				gPlayerSystem.AddGhost();
			}
			this.isPlayback = true;
			this.playbackCounter = this.playRecords[0].frame;
		}
	}
	
	this.Update = function()
	{
		if(this.isPlayback)
		{
			this.UpdatePlayback();
			this.playbackCounter++;
		}
		this.recordingCounter++;
	}
	
	this.UpdatePlayback = function()
	{
		while(this.playback[0].ShouldReplay(this.playbackCounter))
		{
			this.playback[0].Replay();
			
			this.playback.splice(0, 1);
			
			if(this.playback.length == 0)
			{
				this.FinishPlayback();
				return;
			}			
		}
	}
	
	this.FinishPlayback = function()
	{
		if(gEditor || this.ghost)
		{
			gConsole.Log(6, "[RS] Finished Playback");
			this.isPlayback = false;
			gPlayerSystem.RemoveGhost();
			this.ghost = false;
		}
		else
		{
			gConsole.Log(6, "[RS] Loading admin page");
			window.location.href = "/admin/handler/document/";
		}
	}
	
	this.Debug = function()
	{
		if(this.isPlayback)
		{
			gDraw.Watch("Recording system playback");
			gDraw.Watch("    Counter " + this.playbackCounter);
			gDraw.Watch("    Records " + this.playback.length + "/" + this.playRecords.length);
			for(var i = 0; i < Math.min(this.playback.length, 5); i++)
			{
				this.playback[i].Debug();
			}
		}
		
		if(this.isRecording)
		{
			gDraw.Watch("Recording: Records " + this.records.length + " Counter " + this.recordingCounter);
		}
	}
	
	this.RecordKeyDown = function(key)
	{
		if(this.isRecording)
		{
			var record = new RecordKeyDown;
			record.key = key;
			record.frame = this.recordingCounter;
			this.records.push(record);
		}
	}

	this.RecordKeyUp = function(key)
	{
		if(this.isRecording)
		{
			var record = new RecordKeyUp;
			record.key = key;
			record.frame = this.recordingCounter;
			this.records.push(record);
		}
	}
	
	this.Write = function()
	{
		var xml = '<?xml version="1.0" encoding="UTF-8" ?>\r<!--Bopadoodee Replay-->\r';
		xml += '<Replay' + this.WriteHeader() + '>\r';
		for(var i = 0; i < this.records.length; i++)
		{
			xml += this.records[i].WriteXml() + '\r';
		}
		xml += '</Replay>\r';
		return xml;
	}
	
	this.WriteHeader = function()
	{
		var previousSet = gLevelSystem.GetPreviouslevelSet();
		var previousLevel = gLevelSystem.GetPreviouslevelName();
		if(previousSet && previousLevel)
		{
			return ' previousSet="' + previousSet + '" previousLevel="' + previousLevel + '" ';
		}
		else
		{
			return '';
		}
	}
	
	this.ShouldUpload = function()
	{
		return (!gEditor) && this.records && this.records.length > 0 && this.isRecording;
	}
	
	this.Restart = function()
	{
		if(this.isPlayback)
		{
			this.FinishPlayback();
		}
		this.Upload();	//Upload attempt before restart
	}
	
	this.Upload = function()
	{
		if(this.ShouldUpload())
		{
			var urlVars = getUrlVars();
			if(urlVars["disablepost"])
			{
				return;
			}

			var levelName = gLevelSystem.GetLevelName().toLowerCase();
			var levelSet = gLevelSystem.GetLevelSet().toLowerCase();
			
			gConsole.Log(5, "[RS] Uploading log file for " + levelSet + " " + levelName);
			
			var xml = gRecordingSystem.Write();
			var blob = new Blob([xml], {type: 'text/xml'});
			
			var complete = !gRulesSystem.HasComplete() || gRulesSystem.IsComplete();
			var stars = gRulesSystem.GetCompleteStars();
			var score = gStatsSystem.GetStat(EStatsType.Score);
			var time = gStatsSystem.GetStat(EStatsType.Time);
			var pushes = gStatsSystem.GetStat(EStatsType.Pushes);
			var blocks = gStatsSystem.GetStat(EStatsType.DeletedBlocks);
			
			if(gIni.Get("game", "replays", 0) == 1)
			{
				var formData = new FormData();
				formData.append("docfile", blob, levelSet + "_" + levelName + ".xml");
				formData.append("levelset", levelSet)
				formData.append("level", levelName)
				formData.append("complete", complete ? 'true' : 'false')
				formData.append("stars", stars)
				formData.append("restarting", gLevelSystem.restarting ? 'true' : 'false')
				formData.append("score", score);
				formData.append("time", time);
				formData.append("pushes", pushes);
				formData.append("blocks", blocks);
				
				var xmlhttp = GetXMLRequest();
				
				xmlhttp.open("POST", "http://polyology.webfactional.com/list/", true);
				xmlhttp.send(formData);
			}
			
			if(gLevelSystem.GetLevelName() != "hub")
			{
				if(complete)
				{
					gTelemetry.PostGlobalStat("complete", 1)
					if(stars > 0)
					{
						gTelemetry.PostGlobalStat("stars", stars)
					}
				}
			}
			gTelemetry.PostGlobalStat("time", time);
			gTelemetry.PostGlobalStat("pushes", pushes);
			gTelemetry.PostGlobalStat("blocks", blocks);
		}
	}
	
	this.IsPlaying = function()
	{
		return this.isLoading || this.isPlayback;
	}
}

function Record()
{
	this.frame = undefined;
	
	this.ShouldReplay = function(frame)
	{
		return this.frame == frame;
	}
	
	this.LoadXml = function(xml, ghost)
	{
		this.frame = Number(xml.getAttribute("frame"));
	}
	
	this.Debug = function()
	{
		gDraw.Watch(this.WriteXml());
	}
	
	this.WriteXml = function()
	{
		return "    <record type=\"" + this.GetTypename() + "\" frame=\"" + this.frame + "\" " + this.WriteAdditionalXml() + " />";
	}
}

function RecordKey()
{
	this.__proto__ = new Record;
	this.key = undefined;
	
	this.LoadXml = function(xml, ghost)
	{
		this.__proto__.LoadXml(xml, ghost)
		var key = Number(xml.getAttribute("key"));
		this.key = ghost ? key + gGhostKeyOffset : key;
	}
	
	this.WriteAdditionalXml = function()
	{
		return "key=\"" + this.key + "\"";
	}
}
function RecordKeyDown()
{
	this.__proto__ = new RecordKey;
	
	this.Replay = function()
	{
		var evt = { keyCode : this.key };
		gKeyboard.OnKeyDown(evt);
	}
	
	this.GetTypename = function()
	{
		return RecordKeyDown.GetTypename();
	}
}

RecordKeyDown.GetTypename = function()
{
	return "KeyDown";
}

function RecordKeyUp()
{
	this.__proto__ = new RecordKey;
	
	this.Replay = function()
	{
		var evt = { keyCode : this.key };
		gKeyboard.OnKeyUp(evt);
	}

	this.GetTypename = function()
	{
		return RecordKeyUp.GetTypename();
	}
}

RecordKeyUp.GetTypename = function()
{
	return "KeyUp";
}
