/*
* A rock that rolls.
*/
"use strict";

var GO_Ball = function(ballType){
	this.sprite = new Sprite(ballType.spriteSheet,"Ball");
	this.sprite.collision_flags = 
		DEFINE_SPRITE_COLLISION_FLAGS.GROUND | 
		DEFINE_SPRITE_COLLISION_FLAGS.SPRITES_ACTIVE |
		DEFINE_SPRITE_COLLISION_FLAGS.SPRITES_PASSIVE |
		DEFINE_SPRITE_COLLISION_FLAGS.SOLID;
	this.sprite.setBoundingBox(
		ballType.box[0],
		ballType.box[1],
		ballType.box[2],
		ballType.box[3]);
	//sprite.constraints=constraints;
	this.sprite.max_step = ballType.max_step;
	this.sprite.game_object = this; // no longer a generic GO

	this.gravity_index = 0;

	this.alreadyHit = []; // things we hit while falling; don't process them again

	this.resistance = ballType.resistance;
	this.push_counter = 0;
	this.momentum = ballType.momentum;
	this.momentum_counter = 0;
	this.rebound = ballType.rebound;
	this.dampening = ballType.dampening;
	this.dampening_count = this.dampening;

	this.frames = ballType.frames;
	this.frame = 0; // rolling animation frame

	this.impact=ballType.impact; // if hit an enemy while airborne
	this.passive_impact=ballType.passive_impact; // if enemy/button WITHOUT collision flag SPRITES_ACTIVE touched this while it was grounded; -1 for no eval

	this.seen_push = 0; // counter to stop sound
	this.push_brake = 0; // counter to stop momentum accumulation

	this.held=false;

};

GO_Ball.TYPES = {
	SPIKEFRUIT : { // probably tastes somewhere between a durian and a jalapeno
		image:"spikefruit", 
		frames:[0,1,2,3], // rotation sequence (clockwise / rolling right)
		size: {x:4, y:4, sheetw:2, sheeth:0}, 
		box:[5,1,10,14], // until/unless I rewrite the collision code to transition nicely from box-corners to midpoint collision on slopes, the best stopgap is narrow collision boxes for slope-friendly objects
		resistance:1, 
		max_step:1, // keep in mind that this how far we autotraverse a slope perpendicular to our intended travel REGARDLESS of initial direction, e.g. push lateral vs gravity down
		impact:2, // damage if it collides with enemy OR player (at speed)
		passive_impact:0, // damage if enemy collides with it
//		momentum:[
//			0,0,
//			1,0,1,0,1,0,
//			1,1,1,1,1,1,
//			2,1,2,1,2,1,
//			2,2,2,2,2,2,
//			3,3,3,3],
		momentum:[
			0,0,
			1,0,1,0,1,0,1,0,1,0,1,0,
			1,1,1,1,1,1,1,1,1,1,1,1,
			2,1,2,1,2,1,2,1,2,1,2,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],
		rebound:0, // on hitting a wall, momentum = -(momentum>>rebound)
		dampening:4 // on flat, how may frames to lose 1 momentum?
	}, 
}
for (var type in GO_Ball.TYPES){
	var ballType = GO_Ball.TYPES[type];
	ballType.spriteImg = SDS_Load_Image("art/sheets/sprite/"+ballType.image+".png");
	ballType.spriteSheet = new TileSheet(ballType.size.x, ballType.size.y, ballType.size.sheetw, ballType.size.sheeth, ballType.spriteImg);
}

GO_Ball.prototype = new GameObject(
	new Sprite(
		new TileSheet(
			4, 4, 2, 0, 
			SDS_Load_Image("art/sheets/sprite/spikefruit.png")
		),
		"Ball"
	),
	"Ball"
	);
	
	GO_Ball.prototype.interactions = 
		DEFINE_GAMEOBJECT_INTERACTIONS.GRAB |
		DEFINE_GAMEOBJECT_INTERACTIONS.PUSH |
		DEFINE_GAMEOBJECT_INTERACTIONS.DAMAGE;

	GO_Ball.prototype.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];
	GO_Ball.prototype.gravity_water = [ 
		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];

	GO_Ball.prototype.weight = DEFINE_PROJECTILE_WEIGHT.MEDIUM;
	GO_Ball.prototype.hoistpoint = [
		{'x':13, 'y':8}, // grab point when moving direction -1
		{'x':3, 'y':8} // grab point when moving direction 1
	];

	GO_Ball.prototype.Update = function(){
		this._heap.gravityArray = (this.sprite.isTouchingTerrain(DEFINE_MAP_TERRAIN_TYPES.WATER)) ? this.gravity_water : this.gravity_air;

		if (this._heap.gravityArray[this.gravity_index] > 3 || this.momentum[Math.abs(this.momentum_counter)]>2){
			this.interactions |= DEFINE_GAMEOBJECT_INTERACTIONS.INFLICT;
			this.sprite.collision_flags &= ~DEFINE_SPRITE_COLLISION_FLAGS.SOLID;
		}
		else{
			this.interactions &= ~DEFINE_GAMEOBJECT_INTERACTIONS.INFLICT;
			this.sprite.collision_flags |= DEFINE_SPRITE_COLLISION_FLAGS.SOLID;
		}

		// CAVEAT: if Meli pushed into us or we rolled into Meli last frame, she may be in the
		// collision list for the falling, and will be crushed even if not per se under us
		// if we do the fall logic first.
		// ...at least, that was working hypothesis #1 for the mystery crushes at slope bottom

		if (this.push_brake>0){
			--this.push_brake;
		}
		else if (this.gravity_index==0){
			this._heap.leftDrop = this.sprite.map_room.findVertically(
				DEFINE_MAP_TERRAIN_TYPES.GROUND,
				this.sprite.x+this.sprite.bottom_left.x, this.sprite.y+this.sprite.bottom_left.y,
				1, this.sprite.max_step * (this.sprite.box_right - this.sprite.box_left));
			this._heap.rightDrop = this.sprite.map_room.findVertically(
				DEFINE_MAP_TERRAIN_TYPES.GROUND,
				this.sprite.x+this.sprite.bottom_right.x, this.sprite.y+this.sprite.bottom_right.y,
				1, this.sprite.max_step * (this.sprite.box_right - this.sprite.box_left));
			if (this._heap.rightDrop>=0 && this._heap.leftDrop>=0){
				if (this._heap.rightDrop != this._heap.leftDrop){
					this.momentum_counter += this._heap.rightDrop - this._heap.leftDrop;
					this.momentum_counter = Math.min(this.momentum.length-1,Math.max(this.momentum_counter,-(this.momentum.length-1)));
					this.dampening_count = this.dampening;
				}
				else if (this.momentum_counter!=0) {
					--this.dampening_count;
					while (this.dampening_count<=0){
						this.momentum_counter += (this.momentum_counter>0) ? -1 : 1;
						this.dampening_count += this.dampening;
					}
				}
			}
		}

		this._heap.broken = false;

		this._heap.dx = 0;
		if (this.momentum_counter!=0){
			this._heap.dx = (this.momentum_counter>0 ? 1 : -1)*this.momentum[Math.abs(this.momentum_counter)];
			this.frame += this._heap.dx;

			this.sprite.move(this._heap.dx, 0,  this._heap.spriteMoveRes);
			if (this._heap.spriteMoveRes.flags & DEFINE_SPRITE_MOVE_RESULT_EJECTED_H){
				this._heap.exchanged = false;
				if (this._heap.spriteMoveRes.flags & DEFINE_SPRITE_MOVE_RESULT_FLAGS.EJECTED_SH){
					for (this._heap.i=0; !this._heap.exchanged && this._heap.i<this._heap.spriteMoveRes.ejections_h.length; this._heap.i++){
						if (this._heap.spriteMoveRes.ejections_h[this._heap.i].game_object instanceof GO_Ball){
							this._heap.mc = this.momentum_counter;
							this.momentum_counter = this._heap.spriteMoveRes.ejections_h[this._heap.i].game_object.momentum_counter;
							this._heap.spriteMoveRes.ejections_h[this._heap.i].game_object.momentum_counter = this._heap.mc;
							
							this.capMomentum();
							this._heap.spriteMoveRes.ejections_h[this._heap.i].game_object.capMomentum();

							this._heap.exchanged = true;
						}
						else if (this._heap.spriteMoveRes.ejections_h[this._heap.i].game_object instanceof GO_Forceblock){
							this._heap.broken = true;
						}
					}
				}
				if (!this._heap.exchanged) this.momentum_counter = -1 * (this.momentum_counter>>this.rebound);
			}
		}

		// auto-ejection on slope corners during falling is regularly disrupting
		// our local more sophisticated rolling math
		// so temporarily disable it for gravity application
		this._heap.stepCache = this.max_step;
		this.max_step = 0;
		this.sprite.move(0, this._heap.gravityArray[this.gravity_index],  this._heap.spriteMoveRes);
		if (this._heap.spriteMoveRes.flags & DEFINE_SPRITE_MOVE_RESULT_EJECTED_V){
			if (this.gravity_index>4){
				if (this.sfx) this.sfx.stop();
				this.sfx = new Chiptune("FX_fallcrash",SDS.sfxchip);
				this.sfx.play(false);
			}

			this.gravity_index=0;
			this.alreadyHit.length=0;

			if (this._heap.spriteMoveRes.flags & DEFINE_SPRITE_MOVE_RESULT_FLAGS.EJECTED_SV){
				for (this._heap.i=0; !this._heap.exchanged && this._heap.i<this._heap.spriteMoveRes.ejections_v.length; this._heap.i++){
					if (this._heap.spriteMoveRes.ejections_v[this._heap.i].game_object instanceof GO_Forceblock){
						this._heap.broken = true;
					}
				}
			}
		}
		else{
			this.gravity_index = Math.min(this.gravity_index+1,this._heap.gravityArray.length-1);
		}
		this.max_step = this._heap.stepCache;

		
		// can't have a pure gravity_index>0 check here or they won't hit switches.
		// hence new passive_impact param
		if ((this._heap.spriteMoveRes.flags & DEFINE_SPRITE_MOVE_RESULT_FLAGS.COLLIDING_S)){
			// apply damage to target
			for (this._heap.i=0; this._heap.i<this._heap.spriteMoveRes.collisions.length; this._heap.i++){
				if (this._heap.spriteMoveRes.collisions[this._heap.i].game_object.interactions & DEFINE_GAMEOBJECT_INTERACTIONS.DAMAGE &&
					(this.gravity_index>0 || this.passive_impact>=0)){
					if (this.alreadyHit.indexOf(this._heap.spriteMoveRes.collisions[this._heap.i])==-1){
						this._heap.broken = this._heap.broken || !this._heap.spriteMoveRes.collisions[this._heap.i].game_object.damage(this,this.gravity_index>0 ? this.impact : this.passive_impact);
						this.alreadyHit.push(this._heap.spriteMoveRes.collisions[this._heap.i]);
					}
				}
			}
			
		}

		this.seen_push--;
		if (this.seen_push<=0){
			this.seen_push=0;
			if (this.sfx && this.sfx.name=="FX_grind"){
				this.sfx.stop();
			}
		}
		

		if (this._heap.broken){
			if (this.sfx){
				this.sfx.stop();
			}

			this._heap.poof = new GO_Poof();
			this._heap.poof.spawnOnSprite(this.sprite,this.manager);
			this._heap.poof = null;

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

		while (this.frame < 0){
			this.frame += (this.frames.length<<2);
		}
		while ((this.frame>>2) >= this.frames.length){
			this.frame -= (this.frames.length<<2);
		}
		this.sprite.frame = (this.frame>>2);	
	};

	GO_Ball.prototype.IndisposedUpdate = function(){
		
	};
	GO_Ball.prototype.Unindispose = function(dx, dy, moveRes){
		if (moveRes.flags & (DEFINE_SPRITE_MOVE_RESULT_EJECTED_H | DEFINE_SPRITE_MOVE_RESULT_EJECTED_V)){
			if (Math.abs(dx)+Math.abs(dy) > 1){
				if (this.sfx) this.sfx.stop();
				this.sfx = new Chiptune("FX_fallcrash",SDS.sfxchip);
				this.sfx.play(false);
			}
			for (
				this.momentum_counter=0; 
				this.momentum_counter<this.momentum.length-1 && this.momentum[this.momentum_counter] < Math.abs(dx); 
				++this.momentum_counter){}
			this.momentum_counter *= (dx>0) ? 1 : -1;

		}
	};

	
	GO_Ball.prototype.grab = function(){
		this.gravity_index=0;
		this.momentum_counter=0;
		this.interactions &= ~DEFINE_GAMEOBJECT_INTERACTIONS.INFLICT;
		this.sprite.collision_flags |= DEFINE_SPRITE_COLLISION_FLAGS.SOLID;

		if (this.sfx && this.sfx.name=="FX_grind"){
			this.sfx.stop();
		}

		this.held=true;
	};
	GO_Ball.prototype.drop = function(){
		this.gravity_index=0;
		this.held=false;
	};

	GO_Ball.prototype.push = function(dx){
		if (dx>=0){
			this.push_counter++;
		}
		else{
			this.push_counter--;
		}
		if (this.push_counter>=this.resistance){
			//this.sprite.move(1,0);
			//++this.frame;
			++this.momentum_counter;
			this.push_brake=this.resistance;
			this.push_counter-=this.resistance;
		}
		else if (this.push_counter<=-this.resistance){
			//this.sprite.move(-1,0);
			//--this.frame;
			--this.momentum_counter;
			this.push_brake=this.resistance;
			this.push_counter+=this.resistance;
		}

		if (this.gravity_index<2) this.seen_push = 5; // 1 frame to catch up if it moves, 1 to account for update ordering, 3 to actually wait
		if (!this.sfx || this.sfx.name!="FX_grind"){
			this.sfx = new Chiptune("FX_grind",SDS.sfxchip);
		}
		if (!this.sfx.is_playing && !this.held){
			this.sfx.play(true);
		}
	};

	GO_Ball.prototype.inflict = function(){
		return {hp:this.impact, chi:0};
	};

	GO_Ball.prototype.damage = function(source,points){
		if (source instanceof GO_Ball) return false; // already do custom stuff for ball-on-ball in Update

		// if "damaged" e.g. by a kickwave, arrest or gain momentum
		if (source.sprite.x+source.sprite.center.x > this.sprite.x+this.sprite.center.x){
			if (this.momentum_counter>0){
				this.momentum_counter = this.momentum_counter<<(points+1);
			}
			else{
				this.momentum_counter -= (points+1)<<4;
			}
		}
		else{
			if (this.momentum_counter<0){
				this.momentum_counter = this.momentum_counter<<(points+1);
			}
			else{
				this.momentum_counter += (points+1)<<4;
			}
		}

		this.capMomentum();
		return false;
	};

	GO_Ball.prototype.capMomentum = function(){
		this.momentum_counter = (this.momentum_counter>0 ? 1 : -1) * Math.min(Math.abs(this.momentum_counter),this.momentum.length-1);
	};
