/*
the first of our "Meta GameObjects" - a class of thing which takes a GameObject
and takes over its Update for awhile.
2 optional callbacks may be invoked:
IndisposedUpdate() to still do something at the top of the update loop while the 
MGO's Update is in effect
Unindispose(last_dx, last_dy, last_moveRes) when control is returned to usual 
*/
"use strict";

var DEFINE_PROJECTILE_WEIGHT = {
	"LIGHT" : 0,
	"MEDIUM" : 1,
	"HEAVY" : 2
}

var DEFINE_PROJECTILE_GRAVITY_AIR = [ 
		1, 
		2, 2, 2, 
		3, 3, 3, 
		4, 4, 4, 4, 4, 4, 
		5, 5, 5, 5, 5, 5,
		6, 6, 6, 6, 6, 6,
		7, 7, 7, 7, 7, 7, 
		8];

var DEFINE_PROJECTILE_GRAVITY_WATER = [
	[ 
		1, 
		1, 1, 1, 1, 1, 1, 
		2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
		3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 
		4],
	[ 
		1, 
		1, 1, 1, 
		2, 2, 2, 
		3, 3, 3, 3, 3, 3,
		4, 4, 4, 4, 4, 4, 
		5, 5, 5, 5, 5, 5,
		6, 6, 6, 6, 6, 6, 
		7],
	[ 
		1, 
		2, 2, 2, 
		3, 3, 3, 
		4, 4, 4, 4, 4, 4, 
		5, 5, 5, 5, 5, 5,
		6, 6, 6, 6, 6, 6,
		7, 7, 7, 7, 7, 7, 
		8]
];

var DEFINE_PROJECTILE_SPEED_AIR = 4;

var DEFINE_PROJECTILE_SPEED_WATER = [
	[4, 4, 4, 4, 4,
	3, 3, 3, 3, 3,
	2, 2, 2, 2, 2,
	1, 1, 1, 1, 1,
	0],

	[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 
	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
	0],

	[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	0]
];

var DEFINE_PROJECTILE_RISE_AIR = [ //40px = 2.5 blocks
		-4,-4,-4,-4,-4,-4,
		-3,-3,-3, 
		-2,-2,-2,
		-1,
		0, 0];

var DEFINE_PROJECTILE_RISE_WATER = [
	[ //22px
		-3,-3,-3,-3,
		-2,-2,-2,-2, 
		-1,-1,
		-0,-0,
		 0, 0, 0],
	[ //34px
		-4,-4,-4,-4,
		-3,-3,-3,-3, 
		-2,-2,
		-1,-1,
		 0, 0, 0],
	[ //40px = 2.5 blocks
		-4,-4,-4,-4,-4,-4,
		-3,-3,-3, 
		-2,-2,-2,
		-1,
		0, 0]
];

/* dx and dy are purely about + or - */
function MGO_Projectile(gameobject,dx,dy){
	//if (gameobject.cachedUpdate!=null){
	//	gameobject.Update = gameobject.cachedUpdate;
	//}
	//gameobject.cachedUpdate = gameobject.Update;

	gameobject.pushContext(["Update","onPushContext","onPopContext","metaVars"]);
	gameobject.metaVars = {
		"horizontalIndex" : 0,
		"verticalIndex" : (dy>0) ? 5 : (dy==0) ? DEFINE_PROJECTILE_RISE_AIR.length-5 : 0, // little bit up if straight, little bit down if down, start of the array if up
		"horizontalDirection" : (dx>0) ? 1 : (dx==0) ? 0 : -1,
		"verticalDirection" : (dy>0) ? 1 : -1,//"0" actually needs a touch of negative to clear the ground
		"lastDx" : (dx>0) ? DEFINE_PROJECTILE_SPEED_AIR : (dx==0) ? 0 :-DEFINE_PROJECTILE_SPEED_AIR,
		"lastDy" : (dy>0) ? DEFINE_PROJECTILE_GRAVITY_AIR[5] : (dy==0) ? DEFINE_PROJECTILE_RISE_AIR[DEFINE_PROJECTILE_RISE_AIR.length-5] : DEFINE_PROJECTILE_RISE_AIR[0],
		"submerged" : gameobject.sprite.isTouchingTerrain(DEFINE_MAP_TERRAIN_TYPES.WATER),
		"alreadyHit" : [], // sprites we've hit and damaged
		"_heap" : {} // to avoid collisions with the GO's native _heap
	};

	gameobject.onPushContext = function(){
		// if something else is taking over control,
		// it would be silly for us to keep projectiling
		// revert to normal
		if (this.Unindispose) this.Unindispose(this.metaVars._heap.dx, this.metaVars._heap.dy, this._heap.spriteMoveRes);
		delete this.metaVars;
		this.popContext(); // Update and any prior metaVars
	};
	gameobject.onPopContext = function(){
		// the above should preclude ever getting here, but jic
		// if control is being returned to us, from non-projectile motion,
		// it would be silly for us to keep projectiling
		// revert to normal
		if (this.Unindispose) this.Unindispose(this.metaVars._heap.dx, this.metaVars._heap.dy, this._heap.spriteMoveRes);
		delete this.metaVars;
		this.popContext(); // Update and any prior metaVars
	};

	gameobject.Update = function(){
		if (this.IndisposedUpdate) this.IndisposedUpdate();

		// TY Yagamoth 2026.7.13
		// some projectilables (GuyRock) can destroy themselves in IndesposedUpdate
		// or alter their sprite states.  Abort processing if it no longer makes sense.
		if (!this.manager) return;
		if (!this.sprite || !this.sprite.map_room) return;

		this.metaVars._heap.submerged = this.sprite.isTouchingTerrain(DEFINE_MAP_TERRAIN_TYPES.WATER);

		this.metaVars._heap.newIndex = 0;
		//this.metaVars._heap.oldSampleArray = null;
		this.metaVars._heap.newSampleArray = null;
		// if transitioning from air to water, work back down the index until we cap or hit the air speed
		if (this.metaVars._heap.submerged && !this.metaVars.submerged){
			if (this.metaVars.horizontalDirection!=0){
				// start at the slow end of the array and work our way up
				for (this.metaVars._heap.newIndex=DEFINE_PROJECTILE_SPEED_WATER[this.weight].length-1; Math.abs(DEFINE_PROJECTILE_SPEED_WATER[this.weight][this.metaVars._heap.newIndex])<Math.abs(this.metaVars.lastDx) && this.metaVars._heap.newIndex>0; this.metaVars._heap.newIndex--){
					//noop();
				}
				this.metaVars.horizontalIndex = this.metaVars._heap.newIndex;
				this.metaVars.lastDx = DEFINE_PROJECTILE_SPEED_WATER[this.weight][this.metaVars._heap.newIndex]*this.metaVars.horizontalDirection;
			}
			if (this.metaVars.verticalDirection!=0){
				if (this.metaVars.verticalDirection>0){
					this.metaVars._heap.newSampleArray = DEFINE_PROJECTILE_GRAVITY_WATER[this.weight];
					// start at the slow end of the array and work our way up
					for (this.metaVars._heap.newIndex=0; Math.abs(this.metaVars._heap.newSampleArray[this.metaVars._heap.newIndex])<Math.abs(this.metaVars.lastDy) && this.metaVars._heap.newIndex<this.metaVars._heap.newSampleArray.length; this.metaVars._heap.newIndex++){

					}
				}
				else{
					this.metaVars._heap.newSampleArray = DEFINE_PROJECTILE_RISE_WATER[this.weight];
					// start at the slow end of the array and work our way down
					for (this.metaVars._heap.newIndex=this.metaVars._heap.newSampleArray.length-1; Math.abs(this.metaVars._heap.newSampleArray[this.metaVars._heap.newIndex])<Math.abs(this.metaVars.lastDy) && this.metaVars._heap.newIndex>0; this.metaVars._heap.newIndex--){

					}
				}
				
				this.metaVars.verticalIndex = this.metaVars._heap.newIndex;
				this.metaVars.lastDx = this.metaVars._heap.newSampleArray[this.metaVars._heap.newIndex];
			}
		}

		// if transitioning from water to air, work up the index until we hit the water speed
		else if (!this.metaVars._heap.submerged && this.metaVars.submerged){
			//if (metaVars.horizontalDirection!=0){
				// nothing to do- persist last dx
			//}
			if (this.metaVars.verticalDirection!=0){
				if (this.metaVars.verticalDirection>0){
					this.metaVars._heap.newSampleArray = DEFINE_PROJECTILE_GRAVITY_AIR;
					// start at the slow end of the array and work our way up
					for (this.metaVars._heap.newIndex=0; Math.abs(this.metaVars._heap.newSampleArray[this.metaVars._heap.newIndex])<Math.abs(this.metaVars.lastDy) && this.metaVars._heap.newIndex<this.metaVars._heap.newSampleArray.length; this.metaVars._heap.newIndex++){

					}
				}
				else{
					this.metaVars._heap.newSampleArray = DEFINE_PROJECTILE_RISE_AIR;
					// start at the slow end of the array and work our way down
					for (this.metaVars._heap.newIndex=this.metaVars._heap.newSampleArray.length-1; Math.abs(this.metaVars._heap.newSampleArray[this.metaVars._heap.newIndex])<Math.abs(this.metaVars.lastDy) && this.metaVars._heap.newIndex>0; this.metaVars._heap.newIndex--){

					}
				}
				this.metaVars.verticalIndex = this.metaVars._heap.newIndex;
				this.metaVars.lastDx = this.metaVars._heap.newSampleArray[this.metaVars._heap.newIndex];
			}
		}

		this.metaVars.submerged = this.metaVars._heap.submerged;

		// now apply our movement, test collision, break out of projectile if needed, and increase the indices

		this.metaVars._heap.xSampleArray = (this.metaVars._heap.submerged) ? DEFINE_PROJECTILE_SPEED_WATER[this.weight] : DEFINE_PROJECTILE_SPEED_AIR;
		this.metaVars._heap.ySampleArray = (this.metaVars._heap.submerged) ?
			(this.metaVars.verticalDirection>0) ? DEFINE_PROJECTILE_GRAVITY_WATER[this.weight] : DEFINE_PROJECTILE_RISE_WATER[this.weight] :
			(this.metaVars.verticalDirection>0) ? DEFINE_PROJECTILE_GRAVITY_AIR : DEFINE_PROJECTILE_RISE_AIR;
		this.metaVars._heap.dx = (this.metaVars.horizontalDirection==0) ? 0 :
			(this.metaVars._heap.submerged) ? this.metaVars._heap.xSampleArray[this.metaVars.horizontalIndex] : this.metaVars.lastDx;
		this.metaVars._heap.dx = Math.abs(this.metaVars._heap.dx) * this.metaVars.horizontalDirection;
		this.metaVars._heap.dy = (this.metaVars.verticalDirection==0) ? 0 : this.metaVars._heap.ySampleArray[this.metaVars.verticalIndex];

		this.sprite.move(this.metaVars._heap.dx,this.metaVars._heap.dy,  this._heap.spriteMoveRes);
		this.metaVars._heap.broken = false;
		if (this._heap.spriteMoveRes.flags & DEFINE_SPRITE_MOVE_RESULT_FLAGS.COLLIDING_S){
			// apply damage to target
			for (this.metaVars._heap.i=0; this.metaVars._heap.i<this._heap.spriteMoveRes.collisions.length; this.metaVars._heap.i++){
				if (this._heap.spriteMoveRes.collisions[this.metaVars._heap.i].game_object.interactions & DEFINE_GAMEOBJECT_INTERACTIONS.DAMAGE){
					if (this.metaVars.alreadyHit.indexOf(this._heap.spriteMoveRes.collisions[this.metaVars._heap.i])==-1){
						
						var metaCache = this.metaVars; // no _heap is safe around context shift
						this.metaVars._heap.broken = this.metaVars._heap.broken || !this._heap.spriteMoveRes.collisions[this.metaVars._heap.i].game_object.damage(this,this.impact);
						
						// in instances where the above damage call triggers a context push/pop, 
						// we're no longer really a MGO_Projectile.
						// Abort processing.
						if (metaCache !== this.metaVars) return;

						this.metaVars.alreadyHit.push(this._heap.spriteMoveRes.collisions[this.metaVars._heap.i]);
					}
				}
			}
			
		}
		if (this.metaVars._heap.broken || (this._heap.spriteMoveRes.flags & (DEFINE_SPRITE_MOVE_RESULT_EJECTED_H | DEFINE_SPRITE_MOVE_RESULT_EJECTED_V))){
			
			// break if needed (this used to happen before cleanup, until the advent of push/popContext)
			if (this.metaVars._heap.broken){
				this.metaVars._heap.poof = new GO_Poof();
				this.metaVars._heap.poof.spawnOnSprite(this.sprite,this.manager);
				this.metaVars._heap.poof;//pending pooling

				this.sprite.map_room.removeSprite(this.sprite);
				this.manager.remove(this);
			}

			// revert to normal
			if (this.Unindispose) this.Unindispose(this.metaVars._heap.dx, this.metaVars._heap.dy, this._heap.spriteMoveRes);
			delete this.metaVars;
			this.popContext(); // Update and any prior metaVars

			return;
		}

		this.metaVars.horizontalIndex = Math.min(this.metaVars.horizontalIndex+1, this.metaVars._heap.xSampleArray.length-1);
		this.metaVars.verticalIndex++;
		if (this.metaVars.verticalIndex >= this.metaVars._heap.ySampleArray.length){
			if (this.metaVars.verticalDirection<=0){
				this.metaVars.verticalDirection=1;
				this.metaVars.verticalIndex=0;
			}
			else{
				this.metaVars.verticalIndex=this.metaVars._heap.ySampleArray.length-1;
			}
		}


	}
}