package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.InteractiveObject;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.ui.Keyboard;
	import flash.utils.Timer;
	import FlDosBox.FlDosBoxSpecialChars;
	import MapObjects.Building;
	import Mobs.Hopter;
	import Mobs.Mob;
	import Mobs.Player;
	
	/**
	 * ...
	 * @author Michael Miriti
	 */
	public class MapManager
	{
		public static var inst:MapManager;
		
		private var _mapData:Array;
		private var back1Data:Array;
		
		private var _shiftX:int = 0;
		private var _shiftY:int = 0;
		private var _mapW:int;
		private var _mapH:int;
		
		private var objects:Vector.<MapObject>;
		private var mobs:Vector.<Mob>;
		
		[Embed(source="../assets/maps/back.png")]
		private static var _backBmp:Class;
		
		private var _player:Player;
		private var pause:Boolean = false;
		
		private var _passedTime:Number;
		
		private var _hintTime:Number = 15000;
		
		private var _skyColor:int;
		static private const CITY_BUILDINGS_CONT:Number = 50;
		
		private var _escapePoint:int;
		
		private var _round:int = ROUND_ESCAPE;
		private var _playerShiftX:int = 5;
		private var _playerShiftY:int = 5;
		private var _lavaPosition:Number;
		private var _lavaSpeed:Number = 0.7;
		
		static public const LAVA_FLOOD_W:int = Building.SEGMENT_WIDTH * 10;
		static public const LAVA_FLOOD_H:int = 200;
		
		static public const ROUND_ESCAPE:int = 0;
		static public const ROUND_LAVA_FLOOD:int = 1;
		
		public function MapManager()
		{
			inst = this;
		}
		
		public function addMob(mob:Mob, ax:int, ay:int):void
		{
			mob.x = ax;
			mob.y = ay;
			mobs.push(mob);
		}
		
		public function delMob(mob:Mob):void
		{
			var i:int = mobs.indexOf(mob);
			if (i != -1)
				mobs.splice(i, 1);
		}
		
		public function canPass(cx:int, cy:int):Boolean
		{
			return (cx < _mapW) && ((_mapData[cx][cy] == null) || (_mapData[cx][cy] is MapCellPassable));
		}
		
		public function getMapCell(atx:int, aty:int):MapCell
		{
			return mapData[atx][aty];
		}
		
		public function update(delta:Number):void
		{
			if (pause)
				return;
			
			Main.i.fillBlock(0, 0, 80, 25, 15, _skyColor, _skyColor);
			
			for (var m:int = 0; m < objects.length; m++)
			{
				objects[m].update(delta);
			}
			
			_player.update(delta);
			
			for (var n:int = 0; n < mobs.length; n++)
			{
				mobs[n].update(delta);
			}
			
			shiftX = _player.x - _playerShiftX;
			shiftY = _player.y - _playerShiftY;
			
			if (back1Data != null)
			{
				var backShiftX:int = Math.floor(_shiftX / 3);
				var backShiftY:int = Math.floor(_shiftY / 3);
				
				for (var k:int = backShiftX; k < backShiftX + 80; k++)
				{
					for (var l:int = backShiftY; l < backShiftY + 25; l++)
					{
						if (back1Data[k][l] != null)
							Main.i.setChar(k - backShiftX, l - backShiftY, back1Data[k][l].char, back1Data[k][l].color, back1Data[k][l].back);
					}
				}
			}
			
			for (var i:int = _shiftX; i < _shiftX + 80; i++)
			{
				for (var j:int = _shiftY; j < _shiftY + 25; j++)
				{
					if (mapData[i][j] != null)
						Main.i.setChar(i - _shiftX, j - _shiftY, (mapData[i][j] as MapCell).char, (mapData[i][j] as MapCell).color, (mapData[i][j] as MapCell).back);
				}
			}
			
			for (var o:int = 0; o < mobs.length; o++)
			{
				if ((mobs[o].x > _shiftX) && (mobs[o].x < _shiftX + 80) && (mobs[o].y > _shiftY) && (mobs[o].y < _shiftY + 25))
					mobs[o].render(delta);
			}
			
			_player.render(delta);
			
			if (_round == ROUND_ESCAPE)
			{
				updateEscape(delta);
			}
			else if (_round == ROUND_LAVA_FLOOD)
			{
				updateLavaFlood(delta);
			}
		}
		
		public function updateLavaFlood(delta:Number):void
		{
			var pre:int = Math.floor(_lavaPosition);
			_lavaPosition -= (delta / 1000) * _lavaSpeed;
			var ne:int = Math.floor(_lavaPosition);
			
			if (ne != pre)
			{
				for (var i:int = 0; i < LAVA_FLOOD_W; i++)
				{
					_mapData[i][ne] = new MapCellLava(FlDosBoxSpecialChars.BLOCK_1, Math.random() > 0.5 ? 14 : 12, Math.random() > 0.5 ? 14 : 12);
				}
			}
			
			if (_lavaSpeed < 3)
			{
				_lavaSpeed += 0.005;
			}
			
			if (ne <= player.y + 2)
			{
				player.dispatchEvent(new Event("death"));
			}
			
			if (player.y <= 5)
			{
				var cs:Catscene = new Catscene("EPISODE ??", "????? ????? ??? ?????", 1500);
				Main.i.showCutscene(cs);
				cs.callback = function():void
				{
					initEscape();
					Main.i.setState(Main.STATE_GAME);
				}
			}
		}
		
		public function updateEscape(delta:Number):void
		{
			if (_player.x >= _escapePoint)
			{
				var cs:Catscene = new Catscene("EPISODE 6", "Flood of Lava", 1500);
				Main.i.showCutscene(cs);
				cs.callback = function():void
				{
					initLavaFlood();
					Main.i.setState(Main.STATE_GAME);
				}
			}
			
			_passedTime -= delta;
			if (_passedTime < 0)
				_passedTime = 0;
			var timeStr:String = formatTime(_passedTime);
			
			Main.i.window(80 - timeStr.length - 2, 0, timeStr.length + 2, 3, 8, 0);
			
			var col:int = 10;
			if (_passedTime < 30000)
				col = 14;
			if (_passedTime < 10000)
				col = 12;
			
			Main.i.setString(80 - timeStr.length - 1, 1, timeStr, col);
			
			if (_hintTime > 0)
			{
				Main.i.window(25, 21, 30, 3, 8, 0);
				if (_hintTime > 10000)
					Main.i.setString(26, 22, "    A,D - Run, W - Jump", 14);
				else if (_hintTime > 5000)
					Main.i.setString(26, 22, "       Mind the gap!", 14);
				else
					Main.i.setString(26, 22, "Escape before time is up ->>", 14);
				_hintTime -= delta;
			}
			
			if (_passedTime == 0)
				playerDeath(null);
		}
		
		private function fillNum(num:Number, len:int):String
		{
			var nstr:String = num.toString();
			if (nstr.length < len)
			{
				var tl:int = len - nstr.length;
				for (var i:int = 0; i < tl; i++)
				{
					nstr = '0' + nstr;
				}
			}
			
			return nstr;
		}
		
		private function formatTime(passedTime:Number):String
		{
			var secs:Number = Math.floor(passedTime / 1000);
			var msec:Number = passedTime % 1000;
			
			var mins:Number = Math.floor(secs / 60);
			secs = secs % 60;
			
			return fillNum(mins, 2) + ':' + fillNum(secs, 2) + "." + fillNum(msec, 3);
		}
		
		private function initData(w:int, h:int, citybg:Boolean = true):void
		{
			objects = new Vector.<MapObject>();
			mobs = new Vector.<Mob>();
			_mapData = new Array(w);
			for (var i:int = 0; i < w; i++)
			{
				_mapData[i] = new Array(h);
				for (var j:int = 0; j < h; j++)
				{
					_mapData[i][j] = null;
				}
			}
			_mapW = w;
			_mapH = h;
			
			if (citybg)
			{
				var _bw:int = Math.floor(w / 2);
				var _bh:int = Math.floor(h / 2);
				var st:int = Math.floor(Math.random() * (_bh / 3));
				back1Data = new Array(_bw);
				for (var k:int = 0; k < _bw; k++)
				{
					back1Data[k] = new Array(_bh);
					
					if (k % 50 != 0)
					{
						for (var l:int = 0; l < _bh; l++)
						{
							if (k % 5 == 0)
								st += -1 + Math.floor(Math.random() * 3);
							
							if (st > _bh)
								st = _bh - 1;
							
							if (l > st)
							{
								back1Data[k][l] = new MapCell(FlDosBoxSpecialChars.BLOCK_2, 8, 8);
							}
							else
								back1Data[k][l] = null;
						}
					}
				}
			}
			
			_player = new Player();
			_player.addEventListener("death", playerDeath);
		}
		
		private function playerDeath(e:Event):void
		{
			pause = true;
			Sounds.getI().sndDeath.play();
			var t:Timer = new Timer(1000, 1);
			t.addEventListener(TimerEvent.TIMER, function(e:TimerEvent):void
				{
					if (_round == ROUND_ESCAPE)
						initEscape();
					if (_round == ROUND_LAVA_FLOOD)
						initLavaFlood();
				});
			t.start();
		}
		
		public function initEscape():void
		{
			initData(3000, 100);
			
			_skyColor = Math.random() > 0.5 ? 0 : 1;
			
			var nx:int = 0;
			var flo:int = 2 + Math.floor(Math.random() * 8);
			for (var i:int = 0; i < CITY_BUILDINGS_CONT; i++)
			{
				var seg:int = 1 + (Math.random() * 4);
				objects.push(new Building(nx, flo, seg, Math.random() > 0.5));
				nx += seg * 14 + (Math.random() * 20);
				
				if (i == CITY_BUILDINGS_CONT - 1)
				{
					_escapePoint = nx + 20;
					for (var j:int = 0; j < _mapH; j++)
					{
						_mapData[_escapePoint][j] = new MapCellPassable("-".charCodeAt(0) - 1, 14, 12);
						_mapData[_escapePoint + 1][j] = new MapCellPassable(">".charCodeAt(0) - 1, 14, 12);
					}
				}
				
				flo += -1 + Math.floor(Math.random() * 3);
				if (flo >= 10)
					flo = 9;
				if (flo < 2)
					flo = 2;
				
			}
			_passedTime = 1000 * 60;
			pause = false;
			_round = ROUND_ESCAPE;
			_playerShiftX = 5;
			_playerShiftY = 5;
		}
		
		public function initLavaFlood():void
		{
			_skyColor = 9;
			
			initData(LAVA_FLOOD_W, LAVA_FLOOD_H, false);
			
			var gap:int = 0;
			var gap2:int;
			var gap3:int;
			for (var k:int = 0; k < (LAVA_FLOOD_H / Building.SEGMENT_HEIGHT) - 3; k++)
			{
				do
				{
					var newgap:int = Math.floor(Math.random() * (LAVA_FLOOD_W / Building.SEGMENT_WIDTH));
				} while (newgap == gap);
				gap = newgap;
				do
				{
					gap2 = Math.floor(Math.random() * (LAVA_FLOOD_W / Building.SEGMENT_WIDTH));
				} while (gap2 == gap);
				
				gap3 = Math.floor(Math.random() * (LAVA_FLOOD_W / Building.SEGMENT_WIDTH));
				
				for (var j:int = 0; j < LAVA_FLOOD_W / Building.SEGMENT_WIDTH; j++)
				{
					if ((j != gap) && (j != gap2) && (j != gap3))
						objects.push(new Building(j * Building.SEGMENT_WIDTH, 2, 1, Math.random() > 0.5, false, (k + 2) * Building.SEGMENT_HEIGHT + Building.SEGMENT_HEIGHT * 2));
				}
			}
			
			// floor
			for (var i:int = 0; i < LAVA_FLOOD_W; i++)
			{
				for (var m:int = 0; m < 5; m++)
				{
					_mapData[i][LAVA_FLOOD_H - m] = new MapCell(15, 8, 8);
				}
			}
			
			// walls
			for (var l:int = 0; l < LAVA_FLOOD_H; l++)
			{
				_mapData[0][l] = new MapCell(15, 0, 0);
				_mapData[LAVA_FLOOD_W - 2][l] = new MapCell(15, 0, 0);
			}
			
			for (var n:int = 0; n < LAVA_FLOOD_W; n++)
			{
				_mapData[n][5] = new MapCellPassable("^".charCodeAt(0) - 1, 12, 14);
				_mapData[n][6] = new MapCellPassable("|".charCodeAt(0) - 1, 12, 14);
			}
			
			_round = ROUND_LAVA_FLOOD;
			
			_playerShiftX = 40;
			_playerShiftY = 10;
			
			player.y = LAVA_FLOOD_H - 10;
			player.x = 48;
			
			_lavaPosition = LAVA_FLOOD_H;
			_lavaSpeed = 0.2;
			
			pause = false;
		}
		
		public function get shiftX():int
		{
			return _shiftX;
		}
		
		public function set shiftX(value:int):void
		{
			if (value < 0)
				value = 0;
			if (value > _mapW - 81)
				value = _mapW - 81;
			
			_shiftX = value;
		}
		
		public function get shiftY():int
		{
			return _shiftY;
		}
		
		public function set shiftY(value:int):void
		{
			if (value < 0)
				value = 0;
			if (value > _mapH - 26)
				value = _mapH - 26;
			_shiftY = value;
		}
		
		public function get mapW():int
		{
			return _mapW;
		}
		
		public function get mapH():int
		{
			return _mapH;
		}
		
		public function get mapData():Array
		{
			return _mapData;
		}
		
		public function get player():Player
		{
			return _player;
		}
	}

}