package{
	import Game.*;
	
	import flash.display.*;
	import flash.events.*;
	import flash.utils.*;
	import flash.text.*;
	
	import fl.transitions.Tween;
	import fl.transitions.easing.*;
	import fl.transitions.TweenEvent;
	
	/**
	 * Класс игры
	 * Здесь происходят весь основной игровой процесс
	 */
	public class Game extends MovieClip {
		/** Все существующие лабиринты */
		var Mazes:Array = new Array(
			Game.DefaultMap,
			Game.MrsPacManMap,
			Game.Level3Map
		);
		
		/** Объект уровня **/
		public var Lvl:Level = new Level();
		
		/** Объект главного героя **/
		public var ThePacMan:PacMan = new PacMan();
		
		/** Массив для 4х врагов **/
		public var Ghosts:Array = new Array(4);
		
		/** Объект менеджера звуков **/
		public var soundsInGame:GameSounds = new GameSounds();
		
		/** Режим движения объектов **/
		// 0 = Пауза
		// 1 = Игра
		// 2 = Смерть
		public var MoveMode:Number = 0;
		
		/** GAME VARS **/
		public var HighScore:Number = 0; // Максимальные очки
		
		var FoodEaten:Number = 0; // Количество съеденных точек
		var Lifes:Number = 3; // Количество жизней
		var Score:Number = 0; // Количество очков		
		var EatenPerEnergi:Number = 0; // Количество съеденных крабов во время берсерка пакмана.
		
		var CurrentLevelCycle:Number = 0;
		var CurrentCrabMode:Number = 2;
		
		var MazeNum:Number = 0;
		var LevelNum:Number = 1;
		
		var FrightenedTime:Number = 7000;
		var SpeedScale:Number = 1.0;
		
		/** Массив буленов, в котором каждый элемент указывает нажата ли там или и иная клавиша **/
		var Keysdown:Array = new Array(255);
		
		/** MC **/
		var ReadyLabel:GameReadyLabel = new GameReadyLabel();
		var EpicWinMc:EpicWin = new EpicWin();
		
		/** TIMERS **/
		var setChaseTimer:Number;
		var setBlinkTimer:Number;
		var gotoMenuAfterGameOverTimer:Number;
		var switchLevelCycleTimer:Number;
		
		/** TWEENS **/
		var Tweens:Array = new Array();
		var LevelBlink:Tween;
		
		function switchModeToAllCrabs(Mode:Number):void {
			for (var i:Number = 0; i < 4; i++) {
				if ((Ghosts[i].Mode != 3) && (Ghosts[i].Mode != 0) && (Ghosts[i].Mode != 4) && (Ghosts[i].Mode != 5)) {					
					Ghosts[i].SwitchMode(Mode);
				}
			}
		}
		
		function switchLevelCycle():void {
			switch(CurrentLevelCycle) {
				case 0:
					CurrentCrabMode = 1;
					switchModeToAllCrabs(CurrentCrabMode);
					switchLevelCycleTimer = setTimeout(switchLevelCycle, 20000);
					CurrentLevelCycle++;
				break;
				
				case 1:
					CurrentCrabMode = 2;
					switchModeToAllCrabs(CurrentCrabMode);
					switchLevelCycleTimer = setTimeout(switchLevelCycle, 7000);
					CurrentLevelCycle++;
				break;
				
				case 2:
					CurrentCrabMode = 1;
					switchModeToAllCrabs(CurrentCrabMode);
					switchLevelCycleTimer = setTimeout(switchLevelCycle, 20000);
					CurrentLevelCycle++;
				break;
				
				case 3:
					CurrentCrabMode = 2;
					switchModeToAllCrabs(CurrentCrabMode);
					switchLevelCycleTimer = setTimeout(switchLevelCycle, 5000);
					CurrentLevelCycle++;
				break;
				
				case 4:
					CurrentCrabMode = 1;
					switchModeToAllCrabs(CurrentCrabMode);
					switchLevelCycleTimer = setTimeout(switchLevelCycle, 20000);
					CurrentLevelCycle++;
				break;
				
				case 5:
					CurrentCrabMode = 2;
					switchModeToAllCrabs(CurrentCrabMode);
					switchLevelCycleTimer = setTimeout(switchLevelCycle, 5000);
					CurrentLevelCycle++;
				break;
				
				case 6:
					switchModeToAllCrabs(1);
				break;
			}
		}
		
		function getRandomMazeNum():Number {
			return Math.floor( Math.random() * Mazes.length );
		}
		
		function getRandomMaze():Game.LevelMap {
			return new Mazes[ getRandomMazeNum() ];
		}
		
		/** смена режима игры **/
		function SwitchMoveMode(mod:Number):void {
			if (MoveMode == mod) {
				return;
			}
			switch(mod){
				case 0:
					MoveMode = mod;
					break;

				case 1:
					MoveMode = mod;					
					removeChild(ReadyLabel);
				break;

				case 2:
					MoveMode = mod;
					ThePacMan.SwitchMode(2);
				break;
			}
		}
		
		/** Изменить видимость привидений **/
		function ShowGhosts(vis:Boolean = true):void{
			for(var i:Number = 0; i < 4; i++){
				Ghosts[i].visible = vis;
			}
		}
		
		/** Событие отпускания клавиши **/
		public function onKeyUp(e:KeyboardEvent):void{
			Keysdown[e.keyCode] = false;
		}
		
		/** События нажатия клавиш **/
		public function onKeyDown(e:KeyboardEvent):void {			
			if (MoveMode != 1) { return; }			
			Keysdown[e.keyCode] = true;
			
			switch (e.keyCode)
			{
				case 37: // left
					ThePacMan.ChangeDir(1);
				break;
				
				case 39: // right
					ThePacMan.ChangeDir(2);
				break;
				
				case 38: // up
					ThePacMan.ChangeDir(3);
				break;
				
				case 40: // down
					ThePacMan.ChangeDir(4);
				break;
			}
		}
		
		/** Событие смены кадра **/
		function onEnterFrame(e:Event):void{
			switch(MoveMode){
				case 1:
					if(Keysdown[37]){
						ThePacMan.ChangeDir(1);
					}					
					if(Keysdown[39]){
						ThePacMan.ChangeDir(2);
					}
					if(Keysdown[38]){
						ThePacMan.ChangeDir(3);
					}
					if(Keysdown[40]){
						ThePacMan.ChangeDir(4);
					}
					
					ThePacMan.Move();
					for(var i:Number = 0; i<4; i++){
						Ghosts[i].Move();
					}
				break;
			}
		}
		
		/** Событие: клик мыши **/
		function onMouseDown(e:MouseEvent):void{
			//stage.focus = this;
		}
		
		/** Вызывается по таймеру, после смерти пакмана **/
		function restartAfterPause():void{
			setScenee(); 
			runRound(); 
			SwitchMoveMode(1);
		}
		
		function afterDeathPause():void{			
			ShowGhosts(false);
			SwitchMoveMode(2);
			Lifes--;

			if (Lifes > 0) {
				soundsInGame.pacmanDead();
				addChild(ReadyLabel);
				setTimeout(restartAfterPause, 3000);
				soundsInGame.readyGo();
			}else{
				onGameOver();
			}
		}
		
		/** Callback вызывается когда пакман умирает **/
		function onPacManDeath():void {
			clearTimeout(switchLevelCycleTimer);
			MoveMode = 0;
			ThePacMan.SwitchMode(0);
			soundsInGame.stopAll();
			oLifesHud.DecLifes();
			
			clearInterval(setChaseTimer);
			clearInterval(setBlinkTimer);
			
			setTimeout(afterDeathPause, 1000);
		}
		
		/** Считаем съеденную еду **/
		function incFood():void{
			FoodEaten++;
			
			if(FoodEaten == 30){
				Ghosts[2].SwitchMode(5);
			}
			
			if(FoodEaten == Math.floor(Lvl.FoodNum / 3))
			{
				Ghosts[3].SwitchMode(5);
			}
			
			if(FoodEaten == Lvl.FoodNum){
				onEpicWin();
			}
		}
		
		/** Прибавить к текущим очкам значение val **/
		function addScore(val:Number):void {		
			if (( Score < 10000 ) && ( Score+val >= 10000 ) )
			{
				Lifes++;
				oLifesHud.IncLifes();
			}

			if (( Score < 25000 ) && ( Score+val >= 25000 ) )
			{
				Lifes++;
				oLifesHud.IncLifes();
			}
			
			if (( Score < 50000 ) && ( Score+val >= 50000 ) )
			{
				Lifes++;
				oLifesHud.IncLifes();
			}
			
			if (( Score < 100000 ) && ( Score+val >= 100000 ) )
			{
				Lifes++;
				oLifesHud.IncLifes();
			}
			
			Score += val;
			if (Score > HighScore) {
				HighScore = Score;
			}
			
			putScoreInTextField();
			putHighScoreInTextField();			
		}
		
		/** Переход в меню после гейм овера **/
		function gotoMenuAfterGameOver():void {
			soundsInGame.stopAll();
			(parent as Main).menuMode();
		}
		
		/** Клик на Game Over **/
		function mcGameOverClick(e:MouseEvent):void {
			clearTimeout(gotoMenuAfterGameOverTimer);
			soundsInGame.stopAll();
			(parent as Main).menuMode();
		}
		
		/** GAME OVER **/
		function onGameOver():void{						
			var Gm:GameOverMc = new GameOverMc();
			Gm.addEventListener(MouseEvent.CLICK, mcGameOverClick);			
			Gm.alpha = 0;
			addChild(Gm);
			
			soundsInGame.gameOver();
			
			var gmt:Tween = new Tween(Gm, 'alpha', Strong.easeIn, 0, 100, 2, true);
			gmt.addEventListener(TweenEvent.MOTION_FINISH, function (e:TweenEvent) {
				var ti:Number = Tweens.indexOf(e.target);
				Tweens[ti] = Tweens[Tweens.length - 1];
				Tweens.pop();
			});
			
			gotoMenuAfterGameOverTimer = setTimeout(gotoMenuAfterGameOver, 22000);
			SwitchMoveMode(0);
		}
		
		/** Переход на следующий уровень **/
		function StartNexLevel():void {
			clearTimeout(switchLevelCycleTimer);
			FoodEaten = 0;
			parent.removeChild(EpicWinMc);
			Lvl.clearTiles();
			
			MazeNum++;
			LevelNum++;
			
			txtLevelNum.text = 'Level ' + LevelNum.toString();
			
			if (LevelNum < 21) {
				SpeedScale = 1.0 + (LevelNum / 20);
				ThePacMan.speed = 1.5 * SpeedScale;
				
				for (var i:Number = 0; i < 4; i++) {
					Ghosts[i].initSpeed = 1.5 * SpeedScale;
				}
			}			
			
			if (LevelNum < 19) {
				FrightenedTime =  Math.floor( 2000 + (6000 / 19) * (20-LevelNum) );
			}			
			
			if (MazeNum == Mazes.length) {
				MazeNum = 0;
			}
			
			Lvl.InitLevel(new Mazes[ MazeNum ]);
			
			addChild(ReadyLabel);
			setTimeout(restartAfterPause, 2000);
		}
		
		/** EPIC WIN - Уровень пройден **/
		function onEpicWin():void {
			clearTimeout(switchLevelCycleTimer);
			soundsInGame.stopAll();
			soundsInGame.youWin();
			ShowGhosts(false);
			ThePacMan.visible = false;
			
			SwitchMoveMode(0);
			EpicWinMc.y = 300;
			parent.addChild(EpicWinMc);
			setTimeout(StartNexLevel, 3000);
		}
		
		/** Callback вызывается когда пакман съедает еду **/
		function onFood():void {
			soundsInGame.simpleFood();
			addScore(10);
			incFood();			
		}
		
		/** Callback вызывается когда пакаман съел энерго еду **/
		function onEnergyFood():void {
			if(LevelNum < 19){
				soundsInGame.EnergyFood();
				
				for(var i:Number = 0; i < 4; i++){
					Ghosts[i].SwitchMode(3);
					if (Ghosts[i].Mode == 3) {						
						Ghosts[i].gotoAndStop('Spook');
					}
				}
				
				ThePacMan.SwitchMode(3);
			
				clearInterval(setChaseTimer);
				clearInterval(setBlinkTimer);
			
				setChaseTimer = setTimeout(setChase, FrightenedTime);
				setBlinkTimer = setTimeout(setBlink, FrightenedTime - 2000);
			}else {
				soundsInGame.simpleFood();
			}
			
			addScore(50);
			incFood();			
		}
		
		/** Исчезновение бонуса **/
		function onBonusDisapeare(e:TweenEvent):void {
			var tobject:MovieClip = ((e.target as Tween).obj as MovieClip);
			tobject.parent.removeChild(tobject);
			
			var ti:Number = Tweens.indexOf(e.target);
			Tweens[ti] = Tweens[Tweens.length - 1];
			Tweens.pop();
		}
		
		/** Callback вызывается когда пакман съедает врага **/
		function onEnemyEaten():void {
			
			var BonusLbl:GameBonus = new GameBonus();
			
			soundsInGame.killCrab();
			
			/** Каноничный пример быдлокода **/
			switch(EatenPerEnergi) {
				case 0:
					addScore(200);
					BonusLbl.txtValue.text = '200';
				break;
				
				case 1:
					addScore(400);
					BonusLbl.txtValue.text = '400';
				break;
				
				case 2:
					addScore(800);
					BonusLbl.txtValue.text = '800';
				break;
				
				case 3:
					addScore(1600);
					BonusLbl.txtValue.text = '1600';
					EatenPerEnergi = -1;
				break;
			}
			
			BonusLbl.x = ThePacMan.x;
			BonusLbl.y = ThePacMan.y;
			
			Lvl.addChild(BonusLbl);
			
			var tw:Tween = new Tween(BonusLbl, 'width', Strong.easeIn, BonusLbl.width, 0, 1, true);
			tw.addEventListener(TweenEvent.MOTION_FINISH, onBonusDisapeare);
			
			Tweens[Tweens.length] = tw;
			
			var th:Tween = new Tween(BonusLbl, 'height', Strong.easeIn, BonusLbl.height, 0, 1, true);
			tw.addEventListener(TweenEvent.MOTION_FINISH, function (e:TweenEvent) {
				var ti:Number = Tweens.indexOf(e.target);
				Tweens[ti] = Tweens[Tweens.length - 1];
				Tweens.pop();
			} );
			
			Tweens[Tweens.length] = tw;
			
			EatenPerEnergi++;
		}
		
		/** Начать мигание приведений **/
		function setBlink():void {
			for(var i:Number = 0; i < 4; i++){
				if(Ghosts[i].Mode == 3){
					Ghosts[i].gotoAndStop('Blink');
				}
			}
		}
		
		/** Начать приследование **/
		function setChase():void{
			soundsInGame.EnergyFoodEnds();
			EatenPerEnergi = 0;
			ThePacMan.SwitchMode(1);
			for(var i:Number = 0; i < 4; i++){
				if(Ghosts[i].Mode == 3){
					Ghosts[i].SwitchMode(CurrentCrabMode);
				}
			}
		}
		
		/** Начать новый раунд **/
		function runRound():void {
			/** Выгоняем из дома второго краба **/
			setTimeout(function (){	Ghosts[1].SwitchMode(5); }, 2000);
			
			/** Если съедено больше 30 точек, то и третьего **/
			if(FoodEaten >= 30){
				setTimeout(function (){	Ghosts[2].SwitchMode(5); }, 4000);
			}
			
			/** Съедено больше трети точек, то выгоняем и четвертого **/
			if(FoodEaten >= Math.floor(Lvl.FoodNum / 3)) {
				setTimeout(function (){	Ghosts[3].SwitchMode(5); }, 6000);
			}			
						
			//setChaseTimer = setTimeout(setChase, 3000);
			
			
			switchLevelCycleTimer = setTimeout(switchLevelCycle, 7000);
			
			soundsInGame.bgSound();
			
			ShowGhosts(true);
			ThePacMan.visible = true;
			
			SwitchMoveMode(1);
		}
		
		/**
		 * Расстановка всего по местам
		 * 
		 * Вызывается в начале игры и перед началом раунда, например после смерти пакмана
		 */
		function setScenee():void{
			ThePacMan.x = Lvl.Map.InitPoint.x * 8;
			ThePacMan.y = Lvl.Map.InitPoint.y * 8 + 4;
			ThePacMan.SwitchMode(0);
			ThePacMan.Direction = 0;
			
			Ghosts[0].x = (Lvl.Map.GhostHouse.x - 1) * 8 + 4;
			Ghosts[0].y = (Lvl.Map.GhostHouse.y - 4) * 8 + 4;
			Ghosts[0].Direction = 1;
			Ghosts[0].CanPassHome = false;
			Ghosts[0].SwitchMode(2);					
			
			for(var i:Number = 1; i<4; i++){
				Ghosts[i].x = Lvl.Map.GhostHouse.x * 8 + (i-2) * 16;
				Ghosts[i].y = Lvl.Map.GhostHouse.y * 8;
				Ghosts[i].CanPassHome = false;
				Ghosts[i].NextMode = 2;
				if(i % 2 == 0)
				{
					Ghosts[i].Direction = 3;
				}else{
					Ghosts[i].Direction = 4;
				}
				Ghosts[i].SwitchMode(0);				
			}
		}	
		
		/** Добавляем Score в textField **/
		function putScoreInTextField():void {
			gameScore.text = 'GAME SCORE: '+Score;
		}
		
		/** Добавляем HighScore в textField **/
		public function putHighScoreInTextField():void {
			highScore.text = 'HIGH SCORE: '+HighScore;
		}
		
		/**
		 * КОНСТРУКТОР
		 *
		 * Тут создаются все объекты игры, такие как ГГ, враги и уровень
		 *
		 */
		function Game(){						
			/** Загружаем уровень **/
			Lvl.InitLevel(new Mazes[ MazeNum ]);
			Lvl.scaleX = 2;
			Lvl.scaleY = 2;
			
			Lvl.x = width / 2 - Lvl.width / 2;
			Lvl.y = height / 2 - Lvl.height / 2;
			
			/** Регистрируем callback функции для пакмана **/
			ThePacMan.onFoodTake = onFood;
			ThePacMan.onEnergyFood = onEnergyFood;	
			
			/** Создаем врагов **/
			Ghosts[0] = new GhostShadow();
			Ghosts[1] = new GhostSpeedy();
			Ghosts[2] = new GhostBashful();
			Ghosts[3] = new GhostPokey();
			
			/** Регистрируем callback функции для врагов и даем им ссылку на ГГ **/
			for(var i:Number = 0; i<4; i++){
				Ghosts[i].onEnemyEaten = onEnemyEaten;
				Ghosts[i].onPacManDeath = onPacManDeath;
				Ghosts[i].pm = ThePacMan;
				Lvl.addChild(Ghosts[i]);
			}									
			
			/** Кладем на уровень пакмана **/
			Lvl.addChild(ThePacMan);
			
			/** Показываем уровень **/
			addChild(Lvl);			

			/** Регистрируем нажатия клавиш **/
			addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
			
			/** Регистрируем.. хм.. отпускание клавиш **/
			addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
			
			/** Регистрируем нажатие мышью **/
			addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			
			/** Регистрируем смену кадров **/
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
			
			ReadyLabel.x = width / 2;
			ReadyLabel.y = height / 2 - ReadyLabel.height;
			
			addChild(ReadyLabel);
			soundsInGame.readyGo();
			
			oLifesHud.SetLifes(Lifes);
			
			/** Расставляем объекты по уровню **/
			setScenee();
			
			setTimeout(runRound, 2000);			
		}		
	}
}