//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 gLevelSystem = new LevelSystem();

function LevelSystem()
{
	this.loading = false;
	this.restarting = false;
	this.restarted = false;
	
	this.loadingTransition = false;
	this.transitionTimer = 0;
	this.kTransitionTime = 30;
	this.transitionX = 0;
	this.transitionY = 0;
	this.transitionSound = gSounds.Get("transition");
	
	this.time = 0;

	this.levelName = undefined;
	this.levelSet = undefined;
	
	this.previousLevelName = undefined;
	this.previousLevelSet = undefined;

	this.script = null;
	this.scriptFilename = null;
	
	this.levelXml = undefined;
	
	this.Init = function()
	{
		var level = gDemo ? "demo" : "title";
		var set = "menu";
		var urlVars = getUrlVars();
		if(urlVars["set"] && urlVars["level"])
		{
			set = urlVars["set"];
			level = urlVars["level"];
		}
		this.LoadLevel(set, level);
	}
	
	this.LoadLevel = function(set, level)
	{
		if(!this.loading)
		{
			this.loading = true;

			gConsole.Log(2, "[LVL] LoadLevel " + set + " " + level);

			gPause.Pause(EPauseReason.Loading, true);
			gRecordingSystem.Upload();	//Upload previous levels recording (if there was any)
			
			set = set.toLowerCase();
			level = level.toLowerCase();
			
			this.previousLevelSet = this.levelSet;
			this.previousLevelName = this.levelName;
	
			this.levelSet = set;
			this.levelName = level;
	
			if (!this.IsArcade() && (!this.IsMenu() || this.IsLevelSelectMenu()))
			{
				gStorage.setItem("levelSystem", "previousSet", this.previousLevelSet);
				gStorage.setItem("levelSystem", "previousName", this.previousLevelName);
				gStorage.setItem("levelSystem", "levelSet", this.levelSet);
				gStorage.setItem("levelSystem", "levelName", this.levelName);
			}
			
			if(this.previousLevelName == "hub" && this.levelName != "hub")
			{
				gStorage.setItem("levelSystem", "previousHubSet", this.previousLevelSet);
			}
	
			OpenXML("data/levels/" + set + "/" + level + ".xml",
				function(response)
				{
					gLevelSystem.Load(response);
				});
		}
		else
		{
			gConsole.Log(7, "[LVL] Already loading so ignoring LoadLevel call");
		}
	}

	
	this.Load = function(xmlString)
	{
		if(xmlString == "")
		{
			gConsole.Log(1, "[LVL] Failed to get level response...")
			gLevelSystem.loading = false;
			gLevelSystem.LoadLevel("green", "hub");
			return;
		}
		gLevelSystem.SetRandomSeed();
		gRulesSystem.Clear();
		gStatsSystem.Clear();
		gBlockSystem.Clear();
		gParticleSystem.Clear();
		gBackground.Clear();
		gTileSystem.Clear();
		gStyles.Clear();
		gScoreOverlay.Clear();
		gCompleteOverlay.Clear();
		ClearCanvas();
		ClearPlayer();
		ClearOverlay();
		ClearHubs();
		ClearMenu();
		this.UnloadScript();
		
		this.levelXml = xmlString;
		this.restarted = false;
		
		xmlDoc = ParseXMLString(xmlString);
		
		var set = gLevelSystem.GetLevelSet();
		var parentSet = GetParentHub(set);
		if(IsHubSet(set))
		{
			this.SetTheme(set);
		}
		else if(parentSet && IsHubSet(parentSet))
		{
			this.SetTheme(parentSet);
		}

		this.LoadStars(xmlDoc.getElementsByTagName("Star"));
		this.LoadTiles(xmlDoc.getElementsByTagName("Tile"));
		this.LoadStyle(xmlDoc.getElementsByTagName("Style"));
		this.LoadBlocks(xmlDoc.getElementsByTagName("Block"));
		this.LoadPlayers(xmlDoc.getElementsByTagName("Player"));
		this.LoadLights(xmlDoc.getElementsByTagName("Light"));
		this.LoadHubs(xmlDoc.getElementsByTagName("Hub"));
			
		gRecordingSystem.Init();
		
		//Script is loaded after the recording system has inited so hub replays work
		this.LoadScript(xmlDoc.getElementsByTagName("Script"));
		
		//LoadScript calls FinishedLoading when it's loaded the script (or straight away if there isn't one)
		
		gPerformanceOverlay.IgnorePerformance();
	}
	
	this.SetTheme = function(theme)
	{
		if(IsHubSet(theme))
		{
			switch(theme)
			{
				case "red":
				{
					//gBlockRenderer.overlay = gBlockRenderer.DrawOverlayDiamond;
					gTileStyle = 2;
					gTileBGStyle = 2;
					gBackground.style = 2;
				}
				break;
				case "blue":
				{
					//gBlockRenderer.overlay = gBlockRenderer.DrawOverlayDetailCircles
					gTileStyle = 1;
					gTileBGStyle = 1;
					gBackground.style = 1;
				}
				break;
				case "green":
				{
					//gBlockRenderer.overlay = gBlockRenderer.DrawOverlayDetailText;
					gTileStyle = 0;
					gTileBGStyle = 0;
					gBackground.style = 0;
				}
				break;
			}
		}
	}
	
	this.FinishedLoading = function()
	{
		gConsole.Log(5, "[LVL] Finished loading");

		//post loaded after recoding system init encase it's a replay
		gTelemetry.PostStat("loaded");
		
		gStorage.setItem(gLevelSystem.GetLevelSet(), gLevelSystem.GetLevelName(), "loaded", true);

		this.loading = false;
		gPause.Pause(EPauseReason.Loading, false);
		this.time = 0;
		
		if(!gEditor)
		{
			this.StartLoadingTransition();
		}
		
		if(this.LoadedCallback)
		{
			this.LoadedCallback();
		}
		 //Draw the level and then display the objectives
		if(gRulesSystem.HasComplete())
		{
			gDraw.Draw();
			gObjectivesOverlay.Enable();
			gScoreOverlay.SetMode(EOverlayMode.None);
		}
		else
		{
			if(this.IsArcade())
			{
				gScoreOverlay.SetMode(EOverlayMode.None);
			}
			else if(this.levelName == "hub" || this.IsLevelSelectMenu())
			{
				gScoreOverlay.SetMode(EOverlayMode.Completed);
			}
			else
			{
				gScoreOverlay.SetMode(EOverlayMode.None);
			}
			gObjectivesOverlay.Disable();
		}
		
		gIconsOverlay.Init();
		gHintsOverlay.Init();
		gControlsOverlay.Init();
		
		gUndoSystem.Enable();
		
		gPerformanceOverlay.IgnorePerformance();
	}
	
	this.IsLevelSelectMenu = function()
	{
		return this.levelSet == "menu" && this.levelName == "menu";
	}
	
	this.IsMenu = function()
	{
		return this.levelSet == "menu";
	}

	this.IsArcade = function()
	{
		return this.levelSet == "arcade";
	}
	
	this.StartLoadingTransition = function()
	{
		gConsole.Log(5, "[LVL] StartLoadingTransition");
		//start from player...
		var player = gPlayerSystem.players[0];
		if(player)
		{
			this.loadingTransition = true;
			this.transitionTimer = 0;
			this.transitionX = player.GetX();
			this.transitionY = player.GetY();
			
			this.transitionSound.Play();

			gStyles.StartTransition();
			gTileSystem.StartTransition();
			gBlockSystem.StartTransition();
			if(gLevelScript)
			{
				if(gLevelScript.StartTransition)
				{
					gLevelScript.StartTransition();
				}
			}
			gPlayerSystem.Draw();
			
			gPerformanceOverlay.IgnorePerformance();
		}
		else
		{
			this.CompleteLoadingTransition();
		}
	}
	
	this.CompleteLoadingTransition = function()
	{
		this.loadingTransition = false;
		this.restarting = false;
		gConsole.Log(5, "[LVL] CompleteLoadingTransition");
	}
	
	this.Update = function()
	{
		if(gPause.IsPaused(EPauseReason.Player))
		{
			return;
		}
		
		if(this.loadingTransition)
		{
			if(this.transitionTimer == this.kTransitionTime)
			{
				this.CompleteLoadingTransition();
			}
			else
			{
				this.transitionTimer++;
			}
		}
		
		if(!gUndoSystem.IsUndoing())
		{
			this.time++;
		}
	}
	
	this.DrawTransition = function()
	{
		if(this.loadingTransition)
		{
			var fraction = this.transitionTimer/this.kTransitionTime + 0.01;
			gStyles.DrawTransition(fraction);
			gTileSystem.DrawTransition(fraction);
			gBlockSystem.DrawTransition(fraction);
			
			if(gLevelScript)
			{
				if(gLevelScript.DrawTransition)
				{
					gLevelScript.DrawTransition(fraction);
				}
			}
		}
	}
	
	this.SetLoadedCallback = function(callback)
	{
		this.LoadedCallback = callback;
	}
	
	this.Restart = function()
	{
		if(this.restarting == false)
		{
			this.restarting = true;
			gTelemetry.PostStat("restart");
			gRecordingSystem.Restart();
			gConsole.AssertMsg(this.levelXml, "Unable to restart, level hasn't been loaded");
			gPause.Pause(EPauseReason.Loading, true);
			this.Load(this.levelXml);
			this.restarted = true;
		}
	}
	
	this.Hub = function()
	{
		gTelemetry.PostStat("hub");
		
		var set = GetHubSet();
		this.LoadLevel(set, "Hub");
	}
	
	this.Write = function()
	{
		var xml = '<?xml version="1.0" encoding="UTF-8" ?>\r<!--Bopadoodee Level-->\r<Level>\r';
		xml += gLevelSystem.WriteScript();
		xml += gLevelSystem.WriteStars();
		xml += gLevelSystem.WriteStyle();
		xml += gLevelSystem.WriteLights();
		xml += gLevelSystem.WritePlayers();
		xml += gLevelSystem.WriteHubs();
		xml += gLevelSystem.WriteBlocks();
		xml += gLevelSystem.WriteTiles();
		xml += '</Level>\r';
		return xml;
	}
	
	this.LoadTiles = function(tileArray)
	{
		gConsole.Log(3, "[LVL] Loading " + tileArray.length + " Tiles");
		for(var i = 0; i < tileArray.length; i++)
		{
			var type = tileArray[i].getAttribute("type");
			var subType = tileArray[i].getAttribute("subType");
			var x = Number(tileArray[i].getAttribute("x"));
			var y = Number(tileArray[i].getAttribute("y"));
			var blockMove = tileArray[i].hasAttribute("blockMove") ? tileArray[i].getAttribute("blockMove") == 1 : true;
			var playerMove = tileArray[i].hasAttribute("playerMove") ? tileArray[i].getAttribute("playerMove") == 1 : true;
			var hintText = tileArray[i].hasAttribute("text") ? tileArray[i].getAttribute("text") : null;
			gTileSystem.AddTileWithParam(type, subType, x, y, blockMove, playerMove, hintText);
		}
	}
	
	this.WriteTiles = function()
	{
		var xml = '\t<Tiles>\r';
		var count = gTileSystem.tiles.length;
		for(var i = 0; i < count; i++)
		{
			var tile = gTileSystem.tiles[i];
			var params = '';
			if(!tile.CanMoveBlockTo())
			{
				params += 'blockMove="0" ';
			}
			if(!tile.CanMovePlayerTo())
			{
				params += 'playerMove="0" ';
			}
			if(tile.hintText)
			{
				params += 'text="' + tile.hintText + '" ';
			}
			if(tile.subType)
			{
				xml += '\t\t<Tile type="' + tile.type + '" subType="' + tile.subType + '" x="' + tile.x + '" y="' + tile.y + '" ' + params + '/>\r';
			}
			else
			{
				xml += '\t\t<Tile type="' + tile.type + '" x="' + tile.x + '" y="' + tile.y + '" ' + params + '/>\r';
			}
		}
		
		xml += '\t</Tiles>\r';
		
		return xml;
	}
	
	this.LoadBlocks = function(blockArray)
	{
		gConsole.Log(3, "[LVL] Loading " + blockArray.length + " Blocks");
		for(var i = 0; i < blockArray.length; i++)
		{
			var block = blockArray[i];
			var type = block.getAttribute("type");
			var subType = block.getAttribute("subType");
			var value = Number(block.getAttribute("value"));
			var x = Number(block.getAttribute("x"));
			var y = Number(block.getAttribute("y"));
			var rot = Number(block.getAttribute("rot"));
			var switchPower = (block.getAttribute("switch") == 1);
			var connected = (block.getAttribute("connected") == 1);
			var fading = (block.getAttribute("fading") == 1);
			gBlockSystem.AddBlock(type, subType, rot, value, x, y, switchPower, connected, fading);
		}		
	}
	
	this.WriteBlocks = function()
	{
		var xml = '\t<Blocks>\r';
		var count = gBlockSystem.blocks.length;
		for(var i = 0; i < count; i++)
		{
			var block = gBlockSystem.blocks[i];
			var params = '';
			if(block.switchPower)
			{
				params += 'switch="1" ';
			}
			
			if(block.connected)
			{
				params += 'connected="1" ';
			}

			if(block.fading)
			{
				params += 'fading="1" ';
			}

			xml += '\t\t<Block value="' + block.value + '" type="' + block.type + '" subType="' + block.subType + '" rot="' + block.rotation + '" x="' + block.x + '" y="' + block.y + '" ' + params + '/>\r';
		}
		
		xml += '\t</Blocks>\r';
		
		return xml;
	}

	this.LoadPlayers = function(playerArray)
	{
		gPlayerSystem.LoadPlayers(playerArray);
	}
	
	this.WritePlayers = function()
	{
		var xml = '\t<Players>\r';
		var count = gPlayerSystem.players.length;
		for(var i = 0; i < count; i++)
		{
			var player = gPlayerSystem.players[i];
			xml += gPlayerSystem.players[i].Write();
		}
		
		xml += '\t</Players>\r';
		
		return xml;
	}
	
	this.LoadStars = function(starArray)
	{
		gConsole.Log(3, "[LVL] Loading stars");
		gRulesSystem.LoadStars(starArray);
	}
	
	this.WriteStars = function()
	{
		return gRulesSystem.WriteStars();
	}
	
	this.LoadStyle = function(styleArray)
	{
		var setInfo = gSetManager.Get(this.levelSet.toLowerCase());
		gStyles.SetColor(setInfo.r, setInfo.g, setInfo.b);
	}

	this.WriteStyle = function()
	{
		var xml = '\t<Style'
		xml += ' r="' + gStyles.baseColor.r+ '"';
		xml += ' g="' + gStyles.baseColor.g+ '"';
		xml += ' b="' + gStyles.baseColor.b+ '"';
		xml += ' />\r';
		return xml;
	}
	
	this.LoadLights = function(lightArray)
	{
		if(lightArray.length > 0)
		{
			gConsole.Log(3, "[LVL] Loading " + lightArray.length + " Lights");
			for(var i = 0; i < lightArray.length; i++)
			{
				var light = lightArray[i];
				var x = light.getAttribute("x");
				var y = light.getAttribute("y");
				var range = light.getAttribute("range");
				var strength = light.getAttribute("strength");
				gStyles.AddLightSource(x, y, range, strength);
			}
		}
		else
		{
			gStyles.AddLightSource(3, 4, 5, 20);
			gStyles.AddLightSource(11, 9, 5, 20);
		}
		
		gStyles.InitLights();
	}
	
	this.WriteLights = function()
	{
		var xml = '\t<Lights>\r';
		var count = gStyles.lightSources.length;
		for(var i = 0; i < count; i++)
		{
			var light = gStyles.lightSources[i];
			if(light.IsValid())
			{
				xml += '\t\t<Light x="' + light.x + '" y="' + light.y + '" range="' + light.range + '" strength="' + light.strength + '" />\r';
			}
		}
		
		xml += '\t</Lights>\r';
		
		return xml;
	}
	
	this.LoadHubs = function(hubsArray)
	{
		if(hubsArray.length > 0)
		{
			gConsole.Log(3, "[LVL] Loading " + hubsArray.length + " Hubs");
			gLevelScript = new BaseHub();
			for(var i = 0; i < hubsArray.length; i++)
			{
				var hub = hubsArray[i];
				var x = parseInt(hub.getAttribute("x"));
				var y = parseInt(hub.getAttribute("y"));
				var set = hub.getAttribute("set");
				var level = hub.getAttribute("level");
				gLevelScript.hubDef.AddLevel(set, level, x, y);
			}
			gRulesSystem.ScriptLoaded();
		}
	}
	
	this.WriteHubs = function()
	{
		var xml = '\t<Hubs>\r';
		if(gLevelScript && gLevelScript.hubDef && gLevelScript.hubDef.levelDefs)
		{
			var count = gLevelScript.hubDef.levelDefs.length;
			for(var i = 0; i < count; i++)
			{
				var hub = gLevelScript.hubDef.levelDefs[i];
				xml += '\t\t<Hub set="' + hub.set + '" level="' + hub.level + '" x="' + hub.x + '" y="' + hub.y + '" />\r';
			}
		}
		xml += '\t</Hubs>\r';
		
		return xml;
	}

	this.LoadScript = function(scriptArray)
	{
		if(scriptArray.length > 0)
		{
			gConsole.AssertMsg(scriptArray.length <= 1, "Only going to load at most 1 script for a level");
			var filename = scriptArray[0].getAttribute("filename");
			this.LoadScriptFile(filename);
		}
		else
		{
			this.scriptFilename = null;
			this.FinishedLoading();
		}
	}
	
	this.WriteScript = function()
	{
		var xml = '';
		if(this.scriptFilename)
		{
			var filename = this.scriptFilename.replace(gLevelSystem.GetLevelPath(), "");
			xml += '\t<Script filename="' + filename + '" />\r';
		}
		return xml;
	}
	
	this.LoadScriptFile = function(filename)
	{
		filename = this.GetLevelPath() + filename;
		
		this.UnloadScript();
		
		this.scriptFilename = filename;
		
		gConsole.AssertMsg(filename, "expected valid filename");
		
		var script = document.createElement("script");
		script.setAttribute("type","text/javascript");
		
		Math.seedrandom(); //Set a random seed so we don't cache...
		filename = filename + "?nocache=" + Math.floor(Math.random() * Math.pow(2, 32));
		gLevelSystem.SetRandomSeed();

		script.setAttribute("src", filename);
		
		this.script = filename;
		
		script.onload = function()
		{
			gLevelScript = new LevelScript();
			gConsole.Log(5, "[LVL] Loaded script");
			gRulesSystem.ScriptLoaded();
			
			gLevelSystem.FinishedLoading();
		}
		document.getElementsByTagName("head")[0].appendChild(script);
		
		gConsole.Log(5, "[LVL] Loading script " + filename);
	}
	
	this.UnloadScript = function()
	{
		if(this.script || gLevelScript)
		{
			gConsole.Log(5, "[LVL] Unloading script");

			if(gLevelScript && gLevelScript.Unload)
			{
				gLevelScript.Unload();
			}
			gLevelScript = null;	//clear reference
			this.script = null;		
		}
	}
	
	this.GetLevelName = function()
	{
		return this.levelName;
	}
	
	this.GetLevelSet = function()
	{
		return this.levelSet;
	}
	
	this.GetLevelPath = function()
	{
		return "data/levels/" + this.levelSet + "/";
	}
	
	this.GetPreviouslevelName = function()
	{
		return this.previousLevelName;
	}
	
	this.GetPreviouslevelSet = function()
	{
		return this.previousLevelSet;
	}
	
	this.SetRandomSeed = function()
	{
		Math.seedrandom('polyology');
	}
}
