﻿DEBUG = false;

function HSEngine(startPoint) {
    var me = this;

    //Player metrics
	if(!DEBUG)this.playerMoney = 50;else this.playerMoney = 5000;

    //Game entities
    this.turrets = new Array();
    this.bases = {green: new GreenBase("green"), blue: new BlueBase("blue"), teal: new TealBase("teal")};
    this.curWave = null;
    this.garbageMarkers = new Array();
    this.garbageTime = new Array();
    
    //Set HTML Elements
	$('#MTurretDamage').text(MTurret.damagePower);
    $('#MTurretPrice').text("$"+MTurret.price);
	$('#MTurretRange').text(MTurret.range + "m");

    $('#ITurretPrice').text("$"+ITurret.price);
	$('#ITurretDamage').text(ITurret.damagePower);
	$('#ITurretRange').text(ITurret.range + "m");
	$('#ITurretTargets').text(ITurret.maxTargets);

    $('#CTurretSplashRange').text(CTurret.splashRange + "m");
	$('#CTurretPrice').text("$"+CTurret.price);
	$('#CTurretDamage').text(CTurret.damagePower);
	$('#CTurretRange').text(CTurret.range + "m");
    
    //Game configuration
    this.maxCycles = 100;
    this.curCycle = 0;
    this.intervalID = null;
    this.timerMs = 75;
    this.greenSpotUrl = "http://labs.ribaudio.com/herosquad/img/greenspot.png";

    //Google maps API
    this.map = new GMap2(document.getElementById("map"));
    this.map.disableDoubleClickZoom();
    this.smallMap = new GMap2(document.getElementById("smallMap"));
    this.streetView = new GStreetviewClient();
    this.streetViewMarker = null;
    this.smallMapOverlay = null;
    this.map.addControl(new GSmallZoomControl());
    //this.map.setMapType(G_HYBRID_MAP);
    this.map.setCenter(startPoint, 16);
    this.smallMap.disableDragging();
    this.smallMap.setCenter(startPoint, 12);
    this.mapClickListener = GEvent.bind(this.map, "click", this, this.mapOnClick);
    GEvent.bind(this.map, "drag", this, this.mapOnDrag);
    GEvent.bind(this.map, "zoomend", this, this.mapOnZoom);
    //GEvent.bind(this.map, "addoverlay", this, this.mapOnAddOverlay);

    //Google search API
    this.localSearch = new google.search.LocalSearch();
    this.localSearch.setResultSetSize(google.search.Search.SMALL_RESULTSET);
    this.localSearch.setNoHtmlGeneration();
    this.localSearch.setCenterPoint(this.map);
	this.executeLocalSearch("blue"); //Assign base locations

    //Only process mousemove requests every 50 events
    this.mouseMoveCycle = 50;
    this.curMouseMoveEvent = 0;
    //GEvent.bind(this.map, "mousemove", this, this.mapOnMouseMove);

    this.onMarkerDrop = new YAHOO.util.CustomEvent("onMarkerDrop", this);

    //////////////DRAW MAPS
    this.drawSmallMapOverlay = function() {
        //Create the rectangle of the small map
		mapBounds = this.map.getBounds();
        ne = mapBounds.getNorthEast();
        sw = mapBounds.getSouthWest();
        se = new GLatLng(sw.lat(), ne.lng());
        nw = new GLatLng(ne.lat(), sw.lng());

        this.smallMapOverlay = new GPolyline([sw, nw, ne, se, sw], "blue", 3);
        this.smallMap.addOverlay(this.smallMapOverlay);     
    }

    this.drawSmallMapOverlay();
}

HSEngine.prototype.start = function() { this.intervalID = setInterval(this.runByGame, this.timerMs); }
//Have to use this function becuse the setinterval calls are made by the Window object
HSEngine.prototype.runByGame = function() { game.run(); }
//******************RUN*************//
HSEngine.prototype.run = function() {
        this.curCycle++;
		
        //Run the current wave
        if (this.curWave) {
            if (!this.curWave.isComplete()) {
                //Run the enemies
                this.curWave.run();
                //Run the turrets
                if (this.curCycle % 20 == 0 && this.turrets.length > 0) {
            			this.runTurrets();				
                }
                //Delete unused markers (explosions, dead creatures, etc)
                for (var i = 0; i < this.garbageMarkers.length; i++) {
                    if (this.garbageMarkers[i]) {
                        if (new Date().getTime() >= this.garbageTime[i]) {
                            this.map.removeOverlay(this.garbageMarkers[i]);
                            delete this.garbageTime[i];
                            delete this.garbageMarkers[i];
                        }
                    }
                }

            }
            //Wave complete, create new wave
            else {
                alert("Wave " + this.curWave.id + "  of " + Wave.maxWaves + " complete!");
                //Clear overlays and load next Wave
                this.map.clearOverlays();
                this.smallMap.clearOverlays();

                nextWaveId = this.curWave.id + 1;
                this.garbageMarkers = new Array();
                this.garbageTime = new Array();
                //Redraw turrets and bases
                for (var i in this.turrets) {
                    if (this.turrets[i]) {
                        this.map.addOverlay(this.turrets[i].marker);
                    }
                }

                for (var i in this.bases)
                    if (this.bases[i].health > 0) {
                    this.map.addOverlay(this.bases[i].marker);
                    this.smallMap.addOverlay(this.bases[i].smallMarker);
                }
                if (nextWaveId <= Wave.maxWaves) {
                    this.curWave = Wave.getWaveById(nextWaveId, this);
                }
                else
                    clearInterval(this.intervalID);
            }
        }

        if (this.curCycle >= this.maxCycles) this.curCycle = 0;
    }

/////////////////EVENTS
HSEngine.prototype.mapOnZoom = function() {
    this.smallMap.removeOverlay(this.smallMapOverlay);
    this.drawSmallMapOverlay();
}

HSEngine.prototype.mapOnDrag = function() {
    this.smallMap.removeOverlay(this.smallMapOverlay);
    this.drawSmallMapOverlay();
}

//Not being used currently
HSEngine.prototype.mapOnClick = function(overlay, latlng, overlaylatlng) {
    if (!latlng)
        return;
}

HSEngine.prototype.mapOnClickAddTurret = function(overlay, latlng, overlaylatlng) {
    //If they clicked on a map overlay object, don't add a turret there
    if (!latlng)
        return;

    this.onMarkerDrop.fire();
    
    //Determine turret type
    switch(this.mouseMoveTurret.getTitle())
    {
        case "MTurret":
        turret = new MTurret(latlng, "Machine Gun Turret [" + (this.turrets.length + 1) + "]");
        break;
        
        case "ITurret":
        turret = new ITurret(latlng, "Ice Turret [" + (this.turrets.length + 1) + "]");
		break;
		
        case "CTurret":
        turret = new CTurret(latlng, "Canon [" + (this.turrets.length + 1) + "]");
		break;
    }

    if (this.playerMoney < turret.constructor.price) {
        alert("Not enough money");
		this.movePlayerMode();
        return;
    }

    this.removeMoney(turret.constructor.price);
    turret.onChangeIcon.subscribe(this.onChangeIcon, this);
    turret.onAttack.subscribe(this.onTurretAttack, this);

    this.map.addOverlay(turret.overlayMarker);
    this.map.addOverlay(turret.marker);

    this.turrets.push(turret);

    //Switch back to regular mode
    this.movePlayerMode();
}

HSEngine.prototype.mapOnMouseMove = function(latlng) {
    this.curMouseMoveEvent++;
    if (this.curMouseMoveEvent % this.mouseMoveCycle == 0) {
        this.streetView.getNearestPanoramaLatLng(latlng,
        function(newLatLng) {
            if (newLatLng) {
                if (!me.streetViewMarker) {
                    //Add creatures to map
                    greenSpotIcon = new GIcon(G_DEFAULT_ICON);
                    greenSpotIcon.image = me.greenSpotUrl;
                    greenSpotIcon.shadow = "";
                    greenSpotIcon.iconSize = new GSize(20, 20);
                    greenSpotIcon.iconAnchor = new GPoint(10, 10);
                    greenSpotOptions = { icon: greenSpotIcon, clickable: false };
                    me.streetViewMarker = new GMarker(newLatLng, greenSpotOptions);
                    me.map.addOverlay(me.streetViewMarker);
                }
                else
                    me.streetViewMarker.setLatLng(newLatLng);

            }
            //console.log("nearest latlng: " + newLatLng.lat() + ", " + newLatLng.lng());
        });
        this.curMouseMoveEvent = 0;
    }
}

//Called when a creature reaches a base and explodes.  Measures health of base and redirects monsters if the base is destroyed
//Move this to Wave class?
HSEngine.prototype.creatureOnReachedTarget = function(type, args, me) {
   	var enemies = me.curWave.enemies;
	
	for (var i in me.bases) {
		if (this._targetPoint.equals(me.bases[i].point)) {
			curBase = me.bases[i];
			break;
		}
	}
	if(!DEBUG)curBase.health -= this.constructor.damagePower;
	if (curBase.health <= 0) {
		curBase.health = 0;
		curBase.marker.hide();
		curBase.smallMarker.hide();
		
		//Redirect remaining enemies
		for (var i in me.bases)
			if(me.bases[i].health>0)
				nextBase = me.bases[i];
		
		if(nextBase)
			for(var i in enemies)	
				if(enemies[i] && enemies[i]._targetPoint.equals(curBase.point))
				{
					enemies[i]._targetPoint = nextBase.point;
					enemies[i].loadingDirections = true;
					enemies[i].loadingDirectionsCount = 30 - Math.floor(Math.random() * 100); //add some randomization so they don't all request at the same time
				}
	}
	
	//Update health on interface
	$("#" + curBase.color + "BaseHealth").text(curBase.health).yft('red');

	//Check to see if any bases have health left		
	for(var i in me.bases)
	{
		if(me.bases[i].health > 0)
			return;
	}
	
	//Otherwise you lost
	alert("You lose!"); 
	clearInterval(me.intervalID);
}

//Main event called when an entity changes icons, used by multiple classes
HSEngine.prototype.onChangeIcon = function(type, args, me) {
    if (args.length > 0)
        if (args[0])
            me.map.removeOverlay(args[0]);
	
    //Only add new marker to the map if it is within the viewspace or it's a turret
    if (me.map.getBounds().containsLatLng(this.marker.getLatLng()) || this instanceof Turret) {
        this.onMap = true;
        me.map.addOverlay(this.marker);
    }
}

//Called when turrets that use polylines attack
HSEngine.prototype.onTurretAttack = function(type, args, me) {
	if (this instanceof ITurret) {
		//Create polyline from the turret to the target using the attackcolor
		var laserGraphic;
		var numTargets = this.maxTargets;
		attackColor = args[0];
		if(this.targets.length < 3)numTargets = this.targets.length;
	
		for (var i = 0; i < numTargets; i++) {
			laserGraphic = new GPolyline([new GLatLng(this.point.lat() + .0001, this.point.lng()), this.targets[i]._curPoint], attackColor, 8, .8);
			me.garbageMarkers.push(laserGraphic);
			me.garbageTime.push(new Date().getTime() + 700); //Expediate removing this icon
			me.map.addOverlay(laserGraphic);	
		}
	} 
	else if(this instanceof CTurret)
	{
		//Find creatures near the target point and give damage if they are in the splash range
		for(var i in me.curWave.enemies)
		{
			if(this.targets[0]._curPoint.distanceFrom(me.curWave.enemies[i]._curPoint) <= this.splashRange)
				me.curWave.enemies[i].takeDamage(this.damagePower);
		}
	}
}

//Change to make the wave subscribe to this not the game
//Called when a creature is killed by a turret
HSEngine.prototype.onCreatureKilled = function(type, args, me) {
    me.addMoney(this.constructor.value); //Add the creature's value to the player's money
    me.map.removeOverlay(this.marker);
	if(this.smallMarker)
    	me.smallMap.removeOverlay(this.smallMarker);
    var carcass = new GMarker(this.point(), {
		icon: this.constructor.getDeathIcon(),
		clickable: false,
		zIndexProcess: function(){return -9999}
	});
    me.garbageMarkers.push(carcass);
    me.garbageTime.push(new Date().getTime() + 4000);
    me.map.addOverlay(carcass);
}

//Change to make the wave subscribe to this not the game
//Called when creature is killed on hitting a base
HSEngine.prototype.onCreatureExploded = function(type, args, me) {
    me.map.removeOverlay(this.marker);
	if(this.smallMarker)
    	me.smallMap.removeOverlay(this.smallMarker);
    var explosion = new GMarker(this.point(), { icon: Creature.getExplodeIcon(), clickable: false, zIndexProcess: function() { return 99999; } });
    me.garbageMarkers.push(explosion);
    me.garbageTime.push(new Date().getTime() + 2000);
    me.map.addOverlay(explosion);
    this.setIsDead(true);
}

HSEngine.prototype.addTurretMode = function(turretType) {
    switch(turretType)
    {
        case "MTurret": 
        $("#btnMTurret").val("Cancel");
        GEvent.clearListeners(this.map, "click");
        GEvent.bind(this.map, "click", this, this.mapOnClickAddTurret);
        this.mouseMoveTurret = new GMarker(new GLatLng(0,0), { icon: MTurret.facingNWIcon, clickable: false, title: "MTurret", zIndexProcess: function(){return 100} });
        break;
        
        case "ITurret": 
        $("#btnITurret").val("Cancel");
        GEvent.clearListeners(this.map, "click");
        GEvent.bind(this.map, "click", this, this.mapOnClickAddTurret);
        this.mouseMoveTurret = new GMarker(new GLatLng(0,0), { icon: ITurret.facingNWIcon, clickable: false, title: "ITurret", zIndexProcess: function(){return 100}  });
        break;
        
		case "CTurret": 
        $("#btnCTurret").val("Cancel");
        GEvent.clearListeners(this.map, "click");
        GEvent.bind(this.map, "click", this, this.mapOnClickAddTurret);
        this.mouseMoveTurret = new GMarker(new GLatLng(0,0), { icon: CTurret.facingNWIcon, clickable: false, title: "CTurret", zIndexProcess: function(){return 100}  });
        break;
    }
    this.map.addOverlay(this.mouseMoveTurret);
    GEvent.bind(this.map, "mousemove", this, this.mapOnMouseMoveAddTurret);
}

//Used when adding turrets to the map.  Follows the mouse movement
HSEngine.prototype.mapOnMouseMoveAddTurret = function(latlng) {
    this.mouseMoveTurret.setLatLng(latlng);
}

//Switches from adding a turret to normal click behavior
HSEngine.prototype.movePlayerMode = function() {
    $("#btnMTurret").val("($5) Machine Gun Turret");
    $("#btnITurret").val("($7) Ice Turret");
    
    if (this.mouseMoveTurret)
        this.map.removeOverlay(this.mouseMoveTurret);
    GEvent.clearListeners(this.map, "click"); 
    GEvent.clearListeners(this.map, "mousemove");
    this.mapClickListener = GEvent.bind(this.map, "click", this, this.mapOnClick);
}

//Called from the turret infowindow in order to sell turrets or remove them
HSEngine.prototype.removeTurret = function(turretName) {
    for (var i in this.turrets) {
        if (this.turrets[i].name == turretName) {
            this.map.closeInfoWindow();
            this.map.removeOverlay(this.turrets[i].marker);
            this.map.removeOverlay(this.turrets[i].overlayMarker);
            //Give them their money
            this.addMoney(this.turrets[i].sellPrice);
            delete this.turrets[i];
			return;
        }
    }
}

//Called from the turret infowindo in order to upgrade turrets
HSEngine.prototype.upgradeTurret = function(turretName) {
    for (var i in this.turrets) {
        if (this.turrets[i].name == turretName) {
            //Remove money
            if (this.playerMoney >= this.turrets[i].upgradePrice) {
                this.removeMoney(this.turrets[i].upgradePrice);
                this.turrets[i].upgrade();
                this.turrets[i].overlayMarker.closeInfoWindow();
            }
            else {
                alert("Not enough money to upgrade");
            }

            return;
        }
    }
}

HSEngine.prototype.removeMoney = function(amount) {
    this.playerMoney -= amount;
    $("#playerMoney").text("$" + this.playerMoney).yft('red');
}

HSEngine.prototype.addMoney = function(amount) {
    this.playerMoney += amount;
    $("#playerMoney").text("$" + this.playerMoney).yft();
}

//Used in order to chunk out the tasks of the turrets.  Allows the JS engine to perform operations while the turrets loop through enemies.
HSEngine.prototype.runTurrets = function(){
	var maxTurrets = this.turrets.length;
	var curTurret = 0;
	var me = this;
	
	(function(){
		//Must check for turret's existence because it could be deleted before setTimeout fires
		if(me.turrets[curTurret])
		{
			me.turrets[curTurret].targets = new Array();
			for (z in me.curWave.enemies) {
				//Find all enemies in range
				if (!me.curWave.enemies[z].isDead() && me.curWave.enemies[z].point().distanceFrom(me.turrets[curTurret].point) <= me.turrets[curTurret].range) {
					me.turrets[curTurret].targets.push(me.curWave.enemies[z]);
				}
			}
			me.turrets[curTurret].run();
			curTurret++;
			if(curTurret < maxTurrets)	
				setTimeout(arguments.callee, 15);
		}
		else{
			curTurret++;
			if(curTurret < maxTurrets)	
				setTimeout(arguments.callee, 15);
		}
	})();
}

jQuery.fn.yft = function(color){
	var hex = color == 'red' ? "#EE0000" : '#FBEC5D';
	return this.css({backgroundColor : hex}).animate({backgroundColor : '#ffffff'},'normal');
}
