package  
{
	import com.cheezeworld.utils.Input;
	import com.cheezeworld.utils.KeyCode;
	import events.GridObjectEvent;
	import events.ModelEvent;
	import events.StringEvent;
	import events.ViewEvent;
	import factories.PathGridObjectFactory;
	import factories.ProjectileFlamethrowerFactory;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;
	import game.build.BuildManager;
	import game.enemies.Enemy;
	import game.enemies.EnemyManager;
	import game.menu.BuyMenu;
	import game.projectiles.Projectile;
	import game.projectiles.ProjectileFlamethrower;
	import game.projectiles.ProjectileHeavy;
	import game.projectiles.ProjectileManager;
	import game.projectiles.ProjectileSlow;
	import game.towers.Tower;
	import game.towers.TowerFlamethrower;
	import game.towers.TowerHeavy;
	import game.towers.TowerManager;
	import game.towers.TowerSlow;
	import game.UpgradeManager;
	import game.waves.WaveManager;
	import grid.Grid;
	import grid.path.PathFollowUnit;
	import hud.HUD;
	import views.EnemyView;
	import views.PathGridObjectView;
	import views.PathView;
	import views.ProjectileFlamethowerView;
	import views.ProjectileHeavyView;
	import views.ProjectileSlowView;
	import views.ProjectileView;
	import views.TowerFlamethrowerView;
	import views.TowerHeavyView;
	import views.TowerSlowView;
	import views.TowerView;
	import views.View;
	import grid.path.PathGrid;
	import grid.path.PathGridObject;
	import pathfinder.INode;
	import pathfinder.Pathfinder;
	import flash.system.System;
	
	/**
	 * ...
	 * @author 
	 */
	public class Game extends Sprite {
		static public const BUILD_PHASE:String = "buildPhase";
		static public const PLAY_PHASE:String = "playPhase";
		
		private var pathGrid:PathGrid;
		private var pathView:PathView;
		private var towerManager:TowerManager;
		private var enemyManager:EnemyManager;
		private var route:Array;
		private var projectileManager:ProjectileManager;
		private var waveManager:WaveManager;
		private var upgradeManager:UpgradeManager;
		
		private var buildManager:BuildManager;
		private var buildPhase:Boolean;
		
		private var placingTower:Boolean;
		public var placingTowerName:String;
		
		private var hudD:HUD;
		private var buyMenu:BuyMenu;
		
		//still a simple variable, might make a class instead
		private var health:int;
		
		public function Game() {
			trace("Hello world!");
			
			pathView = new PathView();
			addChild(pathView);
			pathView.addEventListener(PathView.VIEW_ADD, onViewAdd);
			
			pathGrid = new PathGrid();
			pathGrid.addEventListener(Grid.OBJECT_ADD, onObjectAdd);
			pathGrid.startGrid(11, 11);
			
			towerManager = new TowerManager();
			towerManager.addEventListener(TowerManager.TOWER_ADD, onTowerAdd);
			towerManager.addEventListener(TowerManager.TOWER_FIRE, onTowerFire);
			//towerManager.addEventListener(TowerManager.TOWER_REMOVE, onTowerRemove);
			
			enemyManager = new EnemyManager();
			enemyManager.addEventListener(EnemyManager.ENEMY_ADD, onEnemyAdd);
			enemyManager.addEventListener(EnemyManager.ENEMY_REMOVE, onEnemyRemove);
			enemyManager.addEventListener(PathFollowUnit.END_PATH, onEnemyEndPath);
			
			projectileManager = new ProjectileManager();
			projectileManager.addEventListener(ProjectileManager.PROJECTILE_ADDED, onProjectileAdded);
			//projectileManager.addEventListener(ProjectileManager.ON_PROJECTILE_HIT, onProjectileHit);
			
			waveManager = new WaveManager();
			waveManager.addEventListener(WaveManager.SPAWN_ENEMY, onSpawnEnemy);
			
			buildManager = new BuildManager();
			
			buyMenu = new BuyMenu();
			buyMenu.addEventListener(BuyMenu.TOWER_SELECTED, onTowerSelected);
			addChild(buyMenu);
			
			hudD = new HUD();
			addChild(hudD);
			hudD.addEventListener(HUD.SELL_TOWER, onSellTower);
			hudD.addEventListener(HUD.UPGRADE_TOWER, onUpgradeTower);
			
			
			addEventListener(Event.ENTER_FRAME, update);
			addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			
			findRoute();
			
			health = 15;
		}
		
		private function onUpgradeTower(e:ModelEvent):void {
			var tower:Tower = e.model as Tower;
			
			if ( tower.upgradeCost <= buildManager.moneyCount() ) {
				buildManager.removeMoney(tower.upgradeCost);
				tower.upgrade();
			}
			dispatchEvent( new ModelEvent(Tower.TOWER_UPGRADE, tower) );
			
			//upgradeManager.upgrade(tower);
			//System.exit(0);
		}
		
		private function onSellTower(e:ModelEvent):void {
			var tower:Tower = e.model as Tower;
			
			buildManager.refundTowerByTower(tower);
			towerManager.removeTowerbyPathGridObject(tower.location);
		}
		
		private function update(e:Event):void {
			hudD.update( buildManager.moneyCount(), waveManager.currentWaveID(), health );
			
			waveManager.update();
			
			pathView.update();
			
			highlightRoute(route);
			towerManager.updateTowerTargets(enemyManager.enemies);
			
			enemyManager.update();
			
			projectileManager.update();
			
			if (Input.instance.isKeyDown(KeyCode.SPACEBAR) ) waveManager.startNewWave();
		}
		
		private function onTowerSelected(e:StringEvent):void {
			placingTower = !placingTower;
			placingTowerName = e.string;
		}
		
		private function onSpawnEnemy(e:ModelEvent):void {
			enemyManager.addEnemy(e.model as Enemy);
		}
		
		private function onTowerFire(e:ModelEvent):void {
			var projectile:Projectile = e.model as Projectile;
			projectileManager.addProjectile(projectile);
		}
		
		private function onEnemyEndPath(e:ModelEvent):void {
			var enemy:Enemy = e.model as Enemy;
			enemyManager.removeEnemy(enemy);
			health--;
			if (health < 1) {
				trace("game over!");
				health = 0;
			}
		}
		
		private function onEnemyRemove(e:ModelEvent):void {
			//add value to enemy class
			var money:int = 5 + waveManager.currentWaveID();
			
			buildManager.addMoney(money);
		}
		
		private function onEnemyAdd(e:ModelEvent):void {
			var enemy:Enemy = e.model as Enemy;
			var endNode:INode = route[route.length - 1];
			
			if (!endNode) {
				throw new Error("Path is null!");
			}
			
			enemy.setAtNode(endNode, route.length);
			pathView.addView( new EnemyView(enemy) );
		}
		
		private function onProjectileAdded(e:ModelEvent):void {
			//ADDTOWER
			if (e.model is ProjectileSlow)				pathView.addView( new ProjectileSlowView(e.model as Projectile) );
			else if (e.model is ProjectileHeavy)			pathView.addView( new ProjectileHeavyView(e.model as Projectile) );
			else if (e.model is ProjectileFlamethrower)		pathView.addView( new ProjectileFlamethowerView(e.model as Projectile) );
			else 							pathView.addView( new ProjectileView(e.model as Projectile) );
		}
		
		private function onTowerAdd(e:ModelEvent):void {
			var tower:Tower = e.model as Tower;
			
			//ADDTOWER
			if (tower is TowerSlow) {
				pathView.addView( new TowerSlowView(tower) );
			} else if (tower is TowerHeavy) {
				pathView.addView( new TowerHeavyView(tower) );
			} else if (tower is TowerFlamethrower) {
				pathView.addView( new TowerFlamethrowerView(tower) );
			} else {
				pathView.addView( new TowerView(tower) );
			}
			
		}
		
		private function onMouseDown(e:MouseEvent):void {
			//pathGrid.getGridObject(0, 0);
		}
		
		public function findRoute():void {
			if (!waveManager.isActive) {
				var endNode:INode = pathGrid.getGridObject(0, 0) as PathGridObject;
				//startNode
				var startNode:INode = pathGrid.getGridObject(10, 10) as PathGridObject;
				Pathfinder.heuristic = Pathfinder.manhattanHeuristic;
				var pre:int = getTimer();
				for (var i:int = 0; i < 1; i++) {
					route = Pathfinder.findPath(startNode, endNode, pathGrid.findConnectedPathGridObjects);
				}
				var post:int = getTimer();
				var timeInMilliseconds:int = post - pre;
				
				if (!route) return;
			}
		}
		
		private function onViewAdd(e:ViewEvent):void {
			e.view.addEventListener(MouseEvent.CLICK, onViewClick);
			e.view.addEventListener(MouseEvent.MOUSE_OVER, onViewMouseOver);
			e.view.addEventListener(MouseEvent.MOUSE_OUT, onViewMouseOut);
		}
		
		private function onViewClick(e:MouseEvent):void {
			var pathGridObject:PathGridObject;
			
			if (e.currentTarget is PathGridObjectView) {
				var view:PathGridObjectView = e.currentTarget as PathGridObjectView;
				pathGridObject = view.followTarget as PathGridObject;
				
				if ( placingTower && !pathGridObject.traversable) {
					if ( buildManager.canBuyTower(placingTowerName) ) {
						if ( towerManager.createTower(pathGridObject, placingTowerName) ) {
							buildManager.removeMoney( buildManager.getCostForTowerByName(placingTowerName) );
						}
					}
				} else if ( !towerManager.checkLocationForTurret(pathGridObject) ) {
					if (pathGridObject.traversable) {
						if ( buildManager.canBuyBlock() && !waveManager.isActive) {
							buildManager.removeMoney(BuildManager.COST_BLOCK);
							toggleTraversable(pathGridObject);
						} else return;
					} else if (!waveManager.isActive) {
						toggleTraversable(pathGridObject);
						buildManager.addMoney(BuildManager.COST_BLOCK);
					}
				}
				disablePlacingTower();
				
			} else if (e.currentTarget is TowerView) {
				var towerView:TowerView = e.currentTarget as TowerView;
				pathGridObject = towerView.tower.location;
				
				//buildManager.refundTowerByTower(towerView.tower);
				//towerManager.removeTowerbyPathGridObject(pathGridObject);
				
				showStatsOfTower(towerView);
			}
		}
		
		private function onViewMouseOver(e:MouseEvent):void {
			var view:View = e.currentTarget as View;
			
			if (view is TowerView) {
				//show range
				var towerView:TowerView = view as TowerView;
				towerView.drawTurretRange();
			}
		}
		
		private function onViewMouseOut(e:MouseEvent):void {
			var view:View = e.currentTarget as View;
			
			if (view is TowerView) {
				//show range
				var towerView:TowerView = view as TowerView;
				towerView.hideTurretRange();
			}
		}
		
		private function showStatsOfTower(towerView:TowerView):void {
			var towerInQuestion:Tower = towerView.tower;
			hudD.showTowerStats(towerInQuestion);
		}
		
		public function disablePlacingTower():void {
			buyMenu.toggleHighLight(placingTowerName, true, false);
			placingTower = false;
		}
		
		private function toggleTraversable(pathGridObject:PathGridObject):void {
			pathGridObject.traversable = !pathGridObject.traversable;
			findRoute();
		}
		
		private function highlightRoute(route:Array):void {
			if (!route) {
				//trace("WARNING: NO PATH DETECTED");
				return;
			}
			for (var i:int = 0; i < route.length; i++) {
				var currentObject:PathGridObject = route[i];
				var currentView:PathGridObjectView = pathView.getViewOfPathObject(currentObject);
				currentView.drawBox(currentObject, 0x5555ff);
			}
		}
		
		private function onObjectAdd(e:GridObjectEvent):void {
			var pathGridObject:PathGridObject = e.gridObject as PathGridObject;
			
			pathView.addView( new PathGridObjectView(pathGridObject) );
		}
		
		public function get currentPhase():String {
			if (buildPhase) 	return BUILD_PHASE;
			else 				return PLAY_PHASE;
		}
	}

}