package  
{
	import net.flashpunk.masks.Hitbox;
	import net.flashpunk.World;
	import net.flashpunk.utils.Draw;
	import net.flashpunk.utils.Input;
	import net.flashpunk.FP;
	import net.flashpunk.utils.Key;
	
	/**
	 * ...
	 * @author Benn
	 */
	public class GameWorld extends World 
	{
		
		//levels
		[Embed(source = '../assets/levels/level01.xml', mimeType = 'application/octet-stream')] private static const LEVEL01:Class;
		[Embed(source = '../assets/levels/level02.xml', mimeType = 'application/octet-stream')] private static const LEVEL02:Class;
		[Embed(source = '../assets/levels/level03.xml', mimeType = 'application/octet-stream')] private static const LEVEL03:Class;
		[Embed(source = '../assets/levels/level04.xml', mimeType = 'application/octet-stream')] private static const LEVEL04:Class;
		[Embed(source = '../assets/levels/level05.xml', mimeType = 'application/octet-stream')] private static const LEVEL05:Class;
		[Embed(source = '../assets/levels/level06.xml', mimeType = 'application/octet-stream')] private static const LEVEL06:Class;
		[Embed(source = '../assets/levels/level07.xml', mimeType = 'application/octet-stream')] private static const LEVEL07:Class;
		[Embed(source = '../assets/levels/level08.xml', mimeType = 'application/octet-stream')] private static const LEVEL08:Class;
		[Embed(source = '../assets/levels/level09.xml', mimeType = 'application/octet-stream')] private static const LEVEL09:Class;
		[Embed(source = '../assets/levels/level10.xml', mimeType = 'application/octet-stream')] private static const LEVEL10:Class;
		
		//things that we want to render
		private var RenderArray:Array = [];
		
		//player
		private var GamePlayer:Player;
		
		//platforms
		private var PlatformArray:Array = [];
		
		//hostiles
		private var HostileArray:Array = [];
		
		//level stuff
		private var CurrentLevel:int = 0;
		private var LevelsArray:Array = [];
		private var LevelWidth:int = 0;
		private var LevelHeight:int = 0;
		
		//level editing
		private var CanEditLevel:Boolean = false;
		private var MouseHeld:Boolean = false;
		private var DragPlatform:Platform;
		
		//level state
		private var InLevelIntro:Boolean = false;
		private var InLevelIntroTimer:Number = 2.0;
		private var InLevelWin:Boolean = false;
		private var InLevelWinTimer:Number = 2.0;
		
		//game state flags
		private var OnFrontPage:Boolean = true;
		private var OnEnding:Boolean = false;
		
		//text
		private var FrontPageText:HudText;
		private var FrontPageCredits:HudText;
		private var FrontPageSpace:HudText;
		private var TutorialWASD:HudText;
		private var TutorialJump:HudText;
		
		public function GameWorld() 
		{
			
			GamePlayer = new Player;
			GamePlayer.SetFilled(true);
			GamePlayer.height = 500;
			add(GamePlayer);
			RenderArray.push(GamePlayer);
			
			FrontPageText = new HudText("10");
			FrontPageText.SetSize(1000);
			FrontPageText.x = 235;
			FrontPageText.y = 120;
			FrontPageText.FadeIn();
			add(FrontPageText);
			
			FrontPageCredits = new HudText("BY BENN POWELL");
			FrontPageCredits.SetSize(20);
			FrontPageCredits.x = 215;
			FrontPageCredits.y = 300;
			FrontPageCredits.FadeIn();
			add(FrontPageCredits);
			
			FrontPageSpace = new HudText("PRESS SPACE TO BEGIN");
			FrontPageSpace.SetSize(20);
			FrontPageSpace.x = 180;
			FrontPageSpace.y = 330;
			FrontPageSpace.FadeIn();
			add(FrontPageSpace);
			
			LoadLevelsFromXML();
			
			LoadCurrentLevel();
			
		}
		
		private function CleanUpLevel():void
		{
			RenderArray.length = 0;
			RenderArray.push(GamePlayer);
			
			for each (var P:Platform in PlatformArray)
			{
				remove(P);
			}
			
			PlatformArray.length = 0;
			
			for each (var H:Hostile in HostileArray)
			{
				remove(H);
			}
			
			HostileArray.length = 0;
			
		}
		
		private function LoadLevelsFromXML():void
		{
			LevelsArray.push(new Level(LEVEL01));
			LevelsArray.push(new Level(LEVEL02));
			LevelsArray.push(new Level(LEVEL03));	
			LevelsArray.push(new Level(LEVEL04));
			LevelsArray.push(new Level(LEVEL05));
			LevelsArray.push(new Level(LEVEL06));
			LevelsArray.push(new Level(LEVEL07));
			LevelsArray.push(new Level(LEVEL08));
			LevelsArray.push(new Level(LEVEL09));
			LevelsArray.push(new Level(LEVEL10));
		}
		
		private function LoadCurrentLevel():void
		{
			
			var levelData:Level = LevelsArray[CurrentLevel];
			
			var dataList:XMLList;
			var dataElement:XML;
			
			dataList = levelData.LevelData.levelinfo.leveldata;
			for each (dataElement in dataList)
			{
				LevelWidth = int(dataElement.@width);
				LevelHeight = int(dataElement.@height);
			}
			
			dataList = levelData.LevelData.playerinfo.player;
			for each (dataElement in dataList)
			{
				
				GamePlayer.x = int(dataElement.@x);
				GamePlayer.y = int(dataElement.@y);
				GamePlayer.SetInIntro(Number(dataElement.@height));
				GamePlayer.SetResetInfo(GamePlayer.x, GamePlayer.y, Number(dataElement.@height));
				
			}
			
			var NewPlatform:Platform;
			
			dataList = levelData.LevelData.platforms.platform;
			for each (dataElement in dataList)
			{
				NewPlatform = new Platform;
			
				NewPlatform.x = int(dataElement.@x);
				NewPlatform.y = int(dataElement.@y);
				NewPlatform.SetHeight(Number(dataElement.@height));
				NewPlatform.SetSize(Number(dataElement.@size));
				NewPlatform.SetAlpha(Number(dataElement.@alpha));
				NewPlatform.SetFilled(Boolean(int(dataElement.@filled)));
				NewPlatform.SetColour(uint(dataElement.@colour));
				NewPlatform.SetThickness(Number(dataElement.@thickness));
				NewPlatform.SetEnd(Boolean(int(dataElement.@end)));
				
				add(NewPlatform);
				PlatformArray.push(NewPlatform);
				RenderArray.push(NewPlatform);
				
			}
			
			dataList = levelData.LevelData.elevatorplatforms.platform;
			for each (dataElement in dataList)
			{
				var targetPlatform:int = int(dataElement.@value);
				var topPosition:Number = Number(dataElement.@top);
				var bottomPosition:Number = Number(dataElement.@bottom);
				var platformSpeed:Number = Number(dataElement.@speed);
				var topColour:uint = uint(dataElement.@topcolour);
				var bottomColour:uint = uint(dataElement.@bottomcolour);
				var startDirection:Boolean = Boolean(int(dataElement.@startdirection));
				var startPosition:Number = Number(dataElement.@startposition);
				
				PlatformArray[targetPlatform].SetElevator(topPosition, bottomPosition, platformSpeed, topColour, bottomColour, startDirection, startPosition);
				
			}
			
			dataList = levelData.LevelData.orbitplatforms.platform;
			for each (dataElement in dataList)
			{
				var platformtarget:int = int(dataElement.@value);
				var centreX:int = int(dataElement.@centerx);
				var centreY:int = int(dataElement.@centery);
				var radius:int = int(dataElement.@radius);
				var startangle:Number = Number(dataElement.@startangle);
				var clockwise:Boolean = Boolean(int(dataElement.@clockwise));
				var speed:Number = Number(dataElement.@speed);
				
				PlatformArray[platformtarget].SetOrbiting(centreX, centreY, radius, startangle, clockwise, speed);
				
			}
			
			dataList = levelData.LevelData.hostiles.hostile;
			for each (dataElement in dataList)
			{
				var newHostile:Hostile = new Hostile;
				
				newHostile.x = int(dataElement.@x);
				newHostile.y = int(dataElement.@y);
				newHostile.SetHeight(Number(dataElement.@height));
				newHostile.SetFilled(true);
				newHostile.SetColour(uint(dataElement.@colour));
				newHostile.SetFireVelocity(Number(dataElement.@firevelocity));
				newHostile.SetProjectileSpeed(Number(dataElement.@projectilespeed));
				newHostile.SetRotateSpeed(Number(dataElement.@rotatespeed));
				newHostile.SetFireRate(Number(dataElement.@firerate));
				newHostile.SetClockwise(Boolean(int(dataElement.@clockwise)));
				newHostile.SetCurrentRotation(Number(dataElement.@startangle));
				
				add(newHostile);
				RenderArray.push(newHostile);
				HostileArray.push(newHostile);
				
			}
			
		}
		
		private function UpdateLevelEdit():void
		{
			if (Input.mousePressed)
			{
				for each (var P:Platform in PlatformArray)
				{
					var distance:Number = FP.distance(Input.mouseX + FP.camera.x, Input.mouseY + FP.camera.y, P.x, P.y);
					
					if (distance < P.GetHeight() * P.GetSize()) //mouse inside circle
					{
						MouseHeld = true;
						DragPlatform = P;
					}
				}
			}
			else if (Input.mouseDown)
			{
				if (DragPlatform)
				{
					DragPlatform.x = Input.mouseX + FP.camera.x;
					DragPlatform.y = Input.mouseY + FP.camera.y;
				}
			}
			else if (Input.mouseReleased)
			{
				MouseHeld = false;
				DragPlatform = null;
			}
		}
		
		private function UpdateGameCamera():void
		{
			var playerX:int = GamePlayer.x;
			var playerY:int = GamePlayer.y;
			
			if (playerX > 320 && playerX < LevelWidth - 320)
			{
				FP.camera.x = playerX - 320;
			}
			else if (playerX <= 320)
			{
				FP.camera.x = 0;
			}
			else if (playerX >= LevelWidth - 320)
			{
				FP.camera.x = LevelWidth - 640;
			}
			
			if (playerY > 240 && playerY < LevelHeight - 240)
			{
				FP.camera.y = playerY - 240;
			}
			else if (playerY <= 240)
			{
				FP.camera.y = 0;
			}
			else if (playerY >= LevelHeight - 240)
			{
				FP.camera.y = LevelHeight - 480;
			}
		}
		
		private function UpdateInGame():void
		{
			
			if (GamePlayer.GetWon())
			{
				if (CurrentLevel == 0)
				{
					TutorialJump.FadeOut();
					TutorialWASD.FadeOut();
				}
				
				InLevelWin = true;
			}
			
			UpdateGameCamera();
			
			for each (var H:Hostile in HostileArray)
			{
				if (H.IsReadyToFire())
				{
					var newHostileProjectile:HostileProjectile = new HostileProjectile;
					
					//make the projectile
					newHostileProjectile.x = H.x;
					newHostileProjectile.y = H.y;
					newHostileProjectile.SetHeight(H.GetHeight());
					newHostileProjectile.SetSize(H.GetSize());
					newHostileProjectile.SetFilled(true);
					newHostileProjectile.SetColour(H.GetColour());
					
					//position the projectile
					
					var angle:Number = H.GetCurrentRotation();
					
					var directionX:Number = Math.sin(angle * Math.PI / 180);
					var directionY:Number = Math.cos(angle * Math.PI / 180);
					
					newHostileProjectile.SetVelocity(H.GetFireVelocity());
					newHostileProjectile.SetSpeed(H.GetProjectileSpeed());
					newHostileProjectile.SetDirection(directionX, directionY);
					
					add(newHostileProjectile);					
					RenderArray.push(newHostileProjectile);
					
					H.SetFired();
					
				}
			}
			
		}
		
		private function UpdateLevelIntro():void
		{
			if (!GamePlayer.GetInIntro())
			{
				InLevelIntro = false;
			}
		}
		
		private function CleanRenderer():void
		{
			
			var index:int = 0;
			
			for each (var R:Renderable in RenderArray)
			{
				if (R.GetFlagRemove())
				{
					RenderArray.splice(index, 1);
				}
				
				++index;
			}
		}
		
		private function UpdateFrontPage():void
		{
			
			if (Input.released(Key.SPACE))
			{
				FrontPageCredits.FadeOut();
				FrontPageSpace.FadeOut();
				FrontPageText.FadeOut();
				
				TutorialWASD = new HudText("WASD / ARROWS");
				TutorialWASD.x = 350;
				TutorialWASD.y = 285;
				TutorialWASD.SetSize(12);
				TutorialWASD.SetColour(0xFFFFFF);
				TutorialWASD.FadeIn();
				add(TutorialWASD);
				
				TutorialJump = new HudText("SPACE - JUMP");
				TutorialJump.x = 165;
				TutorialJump.y = 75;
				TutorialJump.SetSize(12);
				TutorialJump.SetColour(0xFFFFFF);
				TutorialJump.FadeIn();
				add(TutorialJump);	
				
				GamePlayer.SetDead(false);
				OnFrontPage = false;
			}
			
		}
		
		override public function update():void 
		{
			
			CleanRenderer();
			
			if (OnEnding)
			{
				
			}
			else if (OnFrontPage)
			{
				UpdateFrontPage();
			}
			else if (InLevelIntro)
			{
				UpdateLevelIntro();
			}
			else if (InLevelWin)
			{
				InLevelWinTimer -= FP.elapsed;
				
				if (InLevelWinTimer <= 0)
				{
					InLevelWinTimer = 2.0;
					InLevelWin = false;
					GamePlayer.MoveToNextLevel();
					CleanUpLevel();
					CurrentLevel++;
					
					if (CurrentLevel == 10)
					{
						OnEnding = true;
						GamePlayer.SetDead(true);
						GamePlayer.x = 0;
						GamePlayer.y = 0;
						FrontPageText.FadeIn();
						FrontPageCredits.FadeIn();
						FP.camera.x = 0;
						FP.camera.y = 0;
						return;
					}
					
					LoadCurrentLevel();
				}
				
			}
			else
			{
				
				UpdateInGame();
				
				if (CanEditLevel)
				{
					UpdateLevelEdit();
				}
			}
			
			super.update();
		}
		
		private function RenderSort(a:Renderable, b:Renderable):int
		{
			
			var valueA:Number = a.GetHeight();
			var valueB:Number = b.GetHeight();
			
			if (valueA > valueB)
			{
				return 1;
			}
			else if (valueB > valueA)
			{
				return -1;
			}
			else
			{
				return 0;
			}
			
		}
		
		override public function render():void 
		{
			
			RenderArray.sort(RenderSort);
			
			for each (var R:Renderable in RenderArray)
			{
				Draw.circlePlus(R.x, R.y, R.GetHeight() * R.GetSize(), R.GetColour(), R.GetAlpha(), R.GetFilled(), R.GetThickness());
			}
			
			super.render();
		}
		
	}

}