package  {
	import flash.display.MovieClip
	import flash.events.MouseEvent;
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import flash.events.Event;
	import flash.display.Sprite;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.display.DisplayObject;
	import flash.text.TextFormatAlign;
	import flash.text.TextFieldAutoSize;
	
	public class MyGame extends MovieClip
	{
		public static const ENEMY_DISTANCE = 180;
		
		private var _player:Sprite;
		private var _enemies:Vector.<Enemy>;
		private var _bullets:Vector.<Bullet>;
		
		private var _numLives:uint;
		private var _score:uint;
		
		//We need these two values so that the game will get harder as we go.
		//As the score increases, the speed will increase, and the time between enemies will decrease.
		private var _speedMultiplier = 1.0;
		private var _enemyTimerValue = 1500;
		
		private var _enemyTimer:Timer;
		
		private var _hudText:TextField = new TextField();		//Text on the screen that displays lives and score
		private var _playText:TextField = new TextField();		//Text on the screen before the game starts

		public function MyGame() 
		{
			//init is called with 'true' as its value for the 'firstGame' parameter,
			//indicating that this is our first game.  We'll call this with false 
			//if we wish to start another game
			init(true);
		}
		
		//All the code in the constructor has been moved to its own init() function.  Now we can call this function
		//whenever we want to start a new game
		public function init(firstGame:Boolean = true)
		{
			var tFormat = new TextFormat();
			tFormat.font = "Arial";
			tFormat.size = 20;
			tFormat.bold = true;
			tFormat.color = 0x000000;
			tFormat.align = TextFormatAlign.CENTER;		//aligns the text in the center, not that we must import
														//flash.text.TextFormatAlign for this
			
			_playText = new TextField();
			_playText.defaultTextFormat = tFormat;
			_playText.autoSize = TextFieldAutoSize.CENTER;		//does something similar, centers the text
			
			
			//if firstGame is false we display score and ask if the player wants to play again, otherwise just
			//display 'Play!'.
			if(!firstGame)_playText.text = "\n Game Over\n Your score is: " +  _score + "!\n Play Again?";
			else _playText.text = "Play!";
			
			_playText.x = stage.stageWidth/2 - _playText.width/2;		//we set the text in the middle of the screen minus half its width
			_playText.y = stage.stageHeight/2 - _playText.height/2;
			
			_playText.selectable = false;
			addChild(_playText);
			
			//this timer just delays for a second so that when a game ends the player doesn't immediately start a new one.
			//Note the timer only triggers once.
			var timer:Timer = new Timer(1000,1);
			timer.start();
			timer.addEventListener(TimerEvent.TIMER, allowNewGame);
		}
		
		public function allowNewGame(e:TimerEvent)
		{
			//When the above timer triggers now we allow the player to start the game by clicking on playText
			_playText.addEventListener(MouseEvent.CLICK, startGame);
		}
		
		public function startGame(e:MouseEvent)
		{
			_bullets = new Vector.<Bullet>;
			_enemies = new Vector.<Enemy>;
			
			//We reset these values every time a new game starts.
			_speedMultiplier = 1.0;
			_enemyTimerValue = 1500;
			
			//remove our playText and its eventListener
			removeChild(_playText);
			_playText.removeEventListener(MouseEvent.CLICK, startGame);
			
			
			_enemyTimer = new Timer(_enemyTimerValue);
			_enemyTimer.start();
			_enemyTimer.addEventListener(TimerEvent.TIMER, addEnemy);
			
			_player = new Sprite();
			_player.x = stage.stageWidth/2;
			_player.y = stage.stageHeight/2;
			_player.mouseEnabled = false;
			addChild(_player);
			drawPlayer();
			
			setHudTextField();
			
			
			//Set the score and numLives back to their original values at the start of a game,
			//note this also sets the hud's text
			score = 0;
			numLives = 3;
			
			stage.addEventListener(MouseEvent.MOUSE_MOVE, rotatePlayer);
			stage.addEventListener(Event.ENTER_FRAME, updateGame);
			stage.addEventListener(MouseEvent.CLICK, fireBullet);
		}
		
		public function setHudTextField()
		{
			var tFormat = new TextFormat();
			tFormat.font = "Arial";
			tFormat.size = 20;
			tFormat.bold = true;
			tFormat.color = 0x000000;
			
			_hudText.defaultTextFormat = tFormat;
			_hudText.width = 500;
			_hudText.selectable = false;
			addChild(_hudText);
		}
		
		//This is called every time the score or number of lives changes using the 
		//set score and set numLives methods.
		public function setHudText()
		{
			_hudText.text = "Lives: " + _numLives.toString() + "		" +
							"Score: " + _score.toString();
		}
		
		public function rotatePlayer(e:MouseEvent)
		{
			//find the angle in radians using trig 
			var angle = Math.atan2(mouseY - _player.y, mouseX - _player.x);
			
			//convert radians to degrees
			angle = angle * 180 / Math.PI;
			
			_player.rotation = angle;
		}
		
		public function drawPlayer()
		{
			_player.graphics.lineStyle(2,0x000000);		
			_player.graphics.moveTo(10,0);				
			_player.graphics.lineTo(-10,5);				
			_player.graphics.lineTo(-10,-5);
			_player.graphics.lineTo(10,0);	
		}
		
		public function addEnemy(e:TimerEvent)
		{
			var randAngle = Math.random() * 2*Math.PI;
			
			var enemy = new Enemy();
			enemy.x = ENEMY_DISTANCE * Math.cos(randAngle) + stage.stageWidth/2;
			enemy.y = ENEMY_DISTANCE * Math.sin(randAngle) + stage.stageHeight/2;
			
			//set enemy so that mouse events don't register for it.
			enemy.mouseEnabled =false;
			
			_enemies.push(enemy);
			addChild(enemy);
			
			var oppAngle = randAngle - Math.PI;
			
			//note that we're multiplying the enemies speed by the speed multiplier value now.
			//Since _speedMultiplier start at 1.0 this has no effect at the beginning.
			enemy.vx = enemy.speed * _speedMultiplier * Math.cos(oppAngle);
			enemy.vy = enemy.speed * _speedMultiplier * Math.sin(oppAngle);
			
		}
		
		public function updateGame(e:Event)
		{
			for( i = 0; i < _bullets.length; i++)
			{
				_bullets[i].update();
				
				//We remove a bullet if its not inBounds.
				//We check this using our function below.
				if(!checkInBounds(_bullets[i]))
				{
					removeChild(_bullets[i]);
					_bullets.splice(i,1);
					i--;
				}
			}
			
			for(var i = 0; i < _enemies.length; i++)
			{
				_enemies[i].update();
				
				if(_enemies[i].hitTestObject(_player))
				{
					numLives--;
					if(numLives == 0)return;
					
					removeChild(_enemies[i]);
					_enemies.splice(i,1);
					i--;
					break;
				}
				
				for(var j = 0; j < _bullets.length; j++)
				{
					if(_enemies[i].hitTestObject(_bullets[j]))
					{
						score++;
						
						removeChild(_enemies[i]);
						_enemies.splice(i,1);
						removeChild(_bullets[j]);
						_bullets.splice(j,1);
						
						i--;
						j--;
						break;
					}
				}
			}
		}
		
		//Note that this function takes any display object so we could call this using a Sprite or enemy
		//or any other displayObject.
		public function checkInBounds(obj:DisplayObject):Boolean
		{
			//if the x or y value is less than 0 or greater than the stage,
			//return false to indicate the object is not inbounds.
			if(obj.x > stage.stageWidth ||
			   obj.x < 0 ||
			   obj.y > stage.stageHeight ||
			   obj.y < 0)
				return false;
			return true;
		}
		
		public function fireBullet(e:Event)
		{
			var bullet = new Bullet();
			bullet.x = stage.stageWidth/2;
			bullet.y = stage.stageHeight/2;
			
			var angle = _player.rotation / 180 * Math.PI;
			bullet.vx = bullet.speed * _speedMultiplier * Math.cos(angle);
			bullet.vy = bullet.speed * _speedMultiplier * Math.sin(angle);
			addChild(bullet);
			_bullets.push(bullet);
		}
		
		//This is a setter function, which allows to run code every time we set score to a value (et. score = 5)
		public function set score(score:uint)
		{
			_score = score;
			
			//The score changes so our hud must reflect that
			setHudText();
			
			
			//This is a modulus operator which gives us the remainder of the score and ten.
			//It will only be 0 if score is evenly divisible by 10 (30,40,50,etc.)
			if(_score%10 == 0)
			{
				//Increase our speedMultiplier by ten percent and decrease our timer value by ten percent
				_speedMultiplier += 0.1 * _speedMultiplier;
				_enemyTimerValue -= 0.1 * _enemyTimerValue;
				
				//Reset the timer so that its now running on the new timer value.
				//All these steps must be done.
				_enemyTimer.removeEventListener(TimerEvent.TIMER, addEnemy);
				_enemyTimer = new Timer(_enemyTimerValue);
				_enemyTimer.start();
				_enemyTimer.addEventListener(TimerEvent.TIMER, addEnemy);
			}
		}
	
		public function set numLives(lives:uint)
		{
			_numLives = lives;
			
			setHudText();
			
			//If we run out of lives we end the game.
			if(_numLives == 0)
				endGame();
		}
		
		//These are getter function.  They don't really do anything right now.
		//We can access the variables using either (_score or score);
		public function get score():uint
		{
			return _score;
		}
		
		public function get numLives():uint
		{
			return _numLives;
		}
		
		//At the end of the game we remove all our bullets and enemies (We don't need to splice because we will be creating
		//a completely new array anyway).  We also remove the player and our head, stop the timer, and remove all our event
		//listeners.  We then call the init function with a value of false indicating that this is not our first game.
		public function endGame()
		{
			for(var i = 0; i < _bullets.length; i++)
			{
				removeChild(_bullets[i]);
			}
			for(i = 0; i < _enemies.length; i++)
			{
				removeChild(_enemies[i]);
			}
			removeChild(_player);
			
			//Remove our hud
			removeChild(_hudText);
			
			_enemyTimer.stop();
			_enemyTimer.removeEventListener(TimerEvent.TIMER, addEnemy);
			
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, rotatePlayer);
			stage.removeEventListener(Event.ENTER_FRAME, updateGame);
			stage.removeEventListener(MouseEvent.CLICK, fireBullet);
			
			//call the initialization function again, but this time this is not our first game,
			//so we provide false as its parameter.
			init(false);
			
		}

	}
	
}
