"use strict";

var EN_Frog = function(){
	this.sprite = new Sprite(EN_Frog.spriteSheet,"Frog");
	this.sprite.game_object = this; // no longer a generic GO

	this._basicInit(EN_Frog.params);

	//-----

	this.sprite.collision_flags |= DEFINE_SPRITE_COLLISION_FLAGS.GROUND |
								DEFINE_SPRITE_COLLISION_FLAGS.SPRITES_ACTIVE; // to walk on nuts

	this.controls = new Controls(); // ground-runner; easier to just patch in standard controls


	this.direction = 1;

	this.grounded = false;
	this.in_water_cache = false;
	this.horizontal_move_index = 0;
	this.vertical_move_index = 0;
	this.jump_index = 0;
	this.fall_index = 0;

	this.meli = null;
	this.meliFind=false; // she probably won't be in the map when we init; hopefully when we update

	this.tongue_x = -1;
	this.tongue_y = -1;
	this.tongue_sprite = new Sprite(EN_Frog.tongueSheet,"Frog_tongue");
	this.tongue_sprite.game_object = this; // no longer a generic GO
	this.tongue_sprite.frame = 44;

	this.tongue_sprite_chain = [
		new Sprite(EN_Frog.tongueSheet,"Frog_tongue0"),
		new Sprite(EN_Frog.tongueSheet,"Frog_tongue1"),
		new Sprite(EN_Frog.tongueSheet,"Frog_tongue2")
	];
	for (this._heap.t = 0;  this._heap.t<this.tongue_sprite_chain.length; ++this._heap.t){
		this.tongue_sprite_chain[this._heap.t].game_object = this; // no longer a generic GO
		this.tongue_sprite_chain[this._heap.t].frame = 44;
	}

	// considered tracking a whoe sprite/object as the target,
	// but retaining references to things which can die without clearing the room is bad.
	this.tongue_target_x = -1;
	this.tongue_target_y = -1;
	this.mouth = {x:-5, y:6}; // x will be -5 or +13 depending on direction; offset from sprite origin to tongue_sprite origin

	this.tongue_out = false;

	this.bullet_timer = 64;

	this.anim_state = this.ANIM_STATES.stand;
	this.frame_timer=0;

	this.behavior_state = this.BEHAVIOR_STATES.sit;
	this.behavior_timer = 0;
	this.leftward_move_land = this.leftward_shimmy;
	this.rightward_move_land = this.rightward_shimmy;

	this._heap.targets = []; // for flycatching
};
EN_Frog.spriteImg = SDS_Load_Image("art/sheets/sprite/frog.png");
EN_Frog.spriteSheet = new TileSheet(4, 4, 3, 2, EN_Frog.spriteImg);
EN_Frog.tongueSheet = new TileSheet(3, 3, 4, 3, EN_Frog.spriteImg);
EN_Frog.params = {
	name: "Frog",
	box: [1,2,14,15], // left, top, right, bottom
	max_step: 0,
	hp: 2,
	infection: 1,
};

EN_Frog.prototype = new GO_Enemy();

	EN_Frog.prototype.interactions = GO_Enemy.prototype.interactions |
			DEFINE_GAMEOBJECT_INTERACTIONS.CONTROL;
	// NPC controls
	EN_Frog.prototype.leftward_shimmy = [0,0,0,-1,0,0,-1,0];
	EN_Frog.prototype.rightward_shimmy = [0,0,0,1,0,0,1,0];

	EN_Frog.prototype.leftward_hop = [0,-1,-2,-3];
	EN_Frog.prototype.rightward_hop = [0,1,2,3];

	EN_Frog.prototype.leftward_move_water = [0,-1];
	EN_Frog.prototype.rightward_move_water = [0,1];
	EN_Frog.prototype.upward_move_water = [0,-1];
	EN_Frog.prototype.downward_move_water = [0,1];
	EN_Frog.prototype.jump_rise_air = [ //27px = 1.75 blocks
		-4,-4,-4,-4,
		-3,-3, 
		-2,-2,
		-1,
		0, 0];
	EN_Frog.prototype.fall_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];

	// we'll have transitioned to swim by this point, but for thoroughness
	EN_Frog.prototype.jump_rise_water = [ //27px = 1.75 blocks
		-4,-4,-4,-4,
		-3,-3, 
		-2,-2,
		-1,
		0, 0];
	EN_Frog.prototype.fall_water = [ 
		1];

	EN_Frog.prototype.ANIM_STATES = {
		"stand" : 0,
		"walk" : 1,
		"jump" : 2,
		"fall" : 3,

		"bob" : 4,
		"swim" : 5,

		"tongue_out" : 6,
		"tongue_in" : 7
	};
	EN_Frog.prototype.anim_frames = [
		[0], //"stand" : 
		[0], //"walk" : 
		[1], //"jump" : 
		[1], //"fall" :

		[3], // "bob" :
		[2,3], // "swim" :

		[4], // "tongue_out" :
		[4], // "tongue_in" :
	];

	EN_Frog.prototype.BEHAVIOR_STATES = {
		"sit" : 0,
		"meander" : 1,
		"seek" : 2,
		"tongue" : 3
	};

	EN_Frog.prototype.ai = function(){
		if (!this.meli && !this.meliFind){
			this.meli = this.manager.getNamedObject("Meli");
			this.meliFind = true;
		}
		if (this.tongue_sprite.map_room==null && this.sprite.map_room!=null){
			this.sprite.map_room.addSprite(this.tongue_sprite);
			for (this._heap.t = 0;  this._heap.t<this.tongue_sprite_chain.length; ++this._heap.t){
				this.sprite.map_room.addSprite(this.tongue_sprite_chain[this._heap.t]);
			}
		}

		this._heap.oldAnimState=this.anim_state;

		++this.behavior_timer;
		switch (this.behavior_state){
			case this.BEHAVIOR_STATES.sit:
				if (this.behavior_timer>=90){
					this.behavior_timer=0;
					this.controls.unpress(this.controls,"B");
					this.behavior_state = (this.infection>0) ? this.BEHAVIOR_STATES.seek : this.BEHAVIOR_STATES.meander;
				}

				//if (this.infection>0){
					--this.bullet_timer;
					if (this.bullet_timer<32){
						this.controls.press(this.controls,"B");
					}
					if (this.bullet_timer<=0){
						this.controls.unpress(this.controls,"B");
						this.bullet_timer=128;
					}
				//}

			break;

			case this.BEHAVIOR_STATES.meander:
				// move/pause/repeat
				// if infected & seen; seek.

				this.controls.unpress(this.controls,"left");
				this.controls.unpress(this.controls,"right");
				this.controls.unpress(this.controls,"up");
				this.controls.unpress(this.controls,"down");

				if (this.sprite.isTouchingTerrain(DEFINE_MAP_TERRAIN_TYPES.AIR)){
					if (this.behavior_timer<=60){
						if (this.grounded){
							if (this.controls.held.A) this.controls.unpress(this.controls,"A"); 
							else this.controls.press(this.controls,"A"); // jump
						}
						this.controls.press(this.controls,this.direction<0 ? "left" : "right");
					}
					else if (this.behavior_timer>=150){
						this.behavior_state = this.BEHAVIOR_STATES.sit;
						this.behavior_timer=0;
					}
					else {
						this.controls.unpress(this.controls,"A");
					}

					this.leftward_move_land = this.grounded ? this.leftward_shimmy : this.leftward_hop;
					this.rightward_move_land = this.grounded ? this.rightward_shimmy : this.rightward_hop;
				}
				else if (this.sprite.isTouchingTerrain(DEFINE_MAP_TERRAIN_TYPES.WATER)){
					if (this.behavior_timer<=60){
						this.controls.press(this.controls,this.direction<0 ? "left" : "right");
						// TODO: up/down?
					}
					else if (this.behavior_timer>=90){
						this.behavior_state = this.BEHAVIOR_STATES.sit;
						this.behavior_timer=0;
					}
				}
				if (this.infection>0){
					this._heap.seenMeli = this.meli && 
						(this.meli.sprite.y+this.meli.sprite.box_bottom>this.sprite.y+this.sprite.box_top) && 
						(this.meli.sprite.y+this.meli.sprite.box_top<this.sprite.y+this.sprite.box_bottom);
					this._heap.seenMeli &= this.meli &&
						((this.direction<0 && this.meli.sprite.x+this.meli.sprite.box_left < this.sprite.x+this.sprite.box_left) ||
						(this.direction>0 && this.meli.sprite.x+this.meli.sprite.box_right > this.sprite.x+this.sprite.box_right));	
				

					if (this._heap.seenMeli){
						this.behavior_timer=0;
						this.behavior_state = this.BEHAVIOR_STATES.seek;
					}
					else if (this.behavior_timer==0){
						this.direction *= -1;
					}
				}
			break;

			case this.BEHAVIOR_STATES.seek:
				// while seen, move towards
				// when not seen, meander

				this._heap.seenMeli = this.meli && 
					(this.meli.sprite.y+this.meli.sprite.box_bottom>this.sprite.y+this.sprite.box_top) && 
					(this.meli.sprite.y+this.meli.sprite.box_top<this.sprite.y+this.sprite.box_bottom);
				this._heap.seenMeli &= this.meli &&
					((this.direction<0 && this.meli.sprite.x+this.meli.sprite.box_left < this.sprite.x+this.sprite.box_left) ||
					(this.direction>0 && this.meli.sprite.x+this.meli.sprite.box_right > this.sprite.x+this.sprite.box_right));	

				if (!this._heap.seenMeli){
					this.behavior_timer=0;
					this.behavior_state = this.BEHAVIOR_STATES.meander;
				}
				else if (this.sprite.isTouchingTerrain(DEFINE_MAP_TERRAIN_TYPES.AIR)){
					// if grounded for 20 frames, start next hop
					if (this.grounded && this.behavior_timer>=20){
						this.controls.unpress(this.controls,"left");
						this.controls.unpress(this.controls,"right");
						this.controls.unpress(this.controls,"up");
						this.controls.unpress(this.controls,"down");

						this.controls.press(this.controls,"A"); // jump
						this.controls.press(this.controls,this.direction<0 ? "left" : "right");
						this.behavior_timer=0;
					}
					// if we've landed, stop jump
					else if (this.grounded){
						this.controls.unpress(this.controls,"left");
						this.controls.unpress(this.controls,"right");
						this.controls.unpress(this.controls,"up");
						this.controls.unpress(this.controls,"down");
						this.controls.unpress(this.controls,"A");
					}
					// if airborne, suppress countup
					else {
						this.behavior_timer=0;
					}

					this.leftward_move_land = this.grounded ? this.leftward_shimmy : this.leftward_hop;
					this.rightward_move_land = this.grounded ? this.rightward_shimmy : this.rightward_hop;
				}
				else if (this.sprite.isTouchingTerrain(DEFINE_MAP_TERRAIN_TYPES.WATER)){
					this.controls.unpress(this.controls,"left");
					this.controls.unpress(this.controls,"right");
					this.controls.unpress(this.controls,"up");
					this.controls.unpress(this.controls,"down");

					this.controls.press(this.controls,this.direction<0 ? "left" : "right");
					this.controls.press(this.controls,this.sprite.y+this.sprite.center.y>this.meli.sprite.y+this.meli.sprite.center.y ? "up" : "down");
					
				}
			break;

			case this.BEHAVIOR_STATES.tongue:
				if ((this.behavior_timer>=32 && this.tongue_out) || (this.controls.held.B && !this.tongue_out)){
					this.controls.unpress(this.controls,"B");
					this.tongue_out = false;
				}
				// reset from tongue to sit occurs in tongue processing logic after retraction
			break;
		}

		if (this.controls.pressed.B){
			// TODO: burp bullets
			// spit
			/*this._heap.spit = new GO_Bullet(0,2<<3);
			this._heap.spit.sprite.x = (this.bullet_timer&7)==0 ?
				this.sprite.x+3 :
				this.sprite.x+4;
			this._heap.spit.sprite.y = this.sprite.y+14;

			this.sprite.map_room.addSprite(this._heap.spit.sprite);
			this.manager.add(this._heap.spit);*/

			this.tongue_target_x = -1;
			this.tongue_target_y = -1;
			this._heap.target_dist_sq = 64*64; // effective max radius
			this._heap.cx = this.sprite.x+this.sprite.center.x;
			this._heap.cy = this.sprite.y+this.sprite.center.y;
			this.manager.getAllNamedObjects("Mosquito",this._heap.targets);
			if (this._heap.targets.length){
				for (this._heap.i=0; this._heap.i<this._heap.targets.length; ++this._heap.i){
					this._heap.tx = this._heap.targets[this._heap.i].sprite.x+this._heap.targets[this._heap.i].sprite.center.x;
					this._heap.ty = this._heap.targets[this._heap.i].sprite.y+this._heap.targets[this._heap.i].sprite.center.y;
					this._heap.dx = (this._heap.cx - this._heap.tx);
					this._heap.dy = (this._heap.cy - this._heap.ty);
					this._heap.dist_sq = (this._heap.dx*this._heap.dx + this._heap.dy*this._heap.dy);
					if (this._heap.dist_sq<this._heap.target_dist_sq){
						this.tongue_target_x = this._heap.tx;
						this.tongue_target_y = this._heap.ty;
						this._heap.target_dist_sq = this._heap.dist_sq;
					}
				}
				this._heap.targets.length=0; // don't retain references
			}
			else if (this.infection>0 && this.meli){
				this._heap.tx = this.meli.sprite.x+this.meli.sprite.center.x;
				this._heap.ty = this.meli.sprite.y+this.meli.sprite.center.y;
				this._heap.dx = (this._heap.cx - this._heap.tx);
				this._heap.dy = (this._heap.cy - this._heap.ty);
				this._heap.dist_sq = (this._heap.dx*this._heap.dx + this._heap.dy*this._heap.dy);
				if (this._heap.dist_sq<this._heap.target_dist_sq){
					this.tongue_target_x = this._heap.tx;
					this.tongue_target_y = this._heap.ty;
					this._heap.target_dist_sq = this._heap.dist_sq;
				}
			}

			this.anim_state = this.ANIM_STATES.tongue_out;
			// really, for lethal efficiency we ought to only need to test x OR y...
			if (this.tongue_target_x>0 && this.tongue_target_y>0){
				// set up tongue sprites
				this.mouth.x = (this.direction>0) ? 13 : -5;
				this.tongue_sprite.x = this.sprite.x+this.mouth.x;
					this.tongue_sprite.y = this.sprite.y+this.mouth.y;

				// compute this once, since it's involved.
				// cache in the main tongue sprite, which will be reset to 44 later
				this.tongue_sprite.frame = this.chooseTongue(this.tongue_target_x-this.tongue_sprite.x, this.tongue_target_y-this.tongue_sprite.y);

				for (this._heap.i=0; this._heap.i<this.tongue_sprite_chain.length; ++this._heap.i){
					this.tongue_sprite_chain[this._heap.i].x = this.tongue_sprite.x;
					this.tongue_sprite_chain[this._heap.i].y = this.tongue_sprite.y;
					this.tongue_sprite_chain[this._heap.i].frame = this.tongue_sprite.frame;
					this.tongue_sprite_chain[this._heap.i].visible=true;
				}
				this.tongue_sprite.frame = 44;
				this.tongue_sprite.visible = true;

				this.tongue_out = true;
				this.behavior_state = this.BEHAVIOR_STATES.tongue;
				this.behavior_timer = 0;

				this.bullet_timer = Math.max(this.bullet_timer,64); // because this line used to not be here.
			}
			else if (this.infection>0){
				// burp bullets
				this._heap.spit = new GO_Bullet((this.direction*2)<<3,0);
				this._heap.spit.sprite.x = (this.direction>0) ?
					this.sprite.x+13 :
					this.sprite.x;
				this._heap.spit.sprite.y = this.sprite.y+7;
				this.sprite.map_room.addSprite(this._heap.spit.sprite);
				this.manager.add(this._heap.spit);

				this._heap.spit = new GO_Bullet((this.direction*2)<<3,1);
				this._heap.spit.sprite.x = (this.direction>0) ?
					this.sprite.x+13 :
					this.sprite.x;
				this._heap.spit.sprite.y = this.sprite.y+7;
				this.sprite.map_room.addSprite(this._heap.spit.sprite);
				this.manager.add(this._heap.spit);

				this._heap.spit = new GO_Bullet((this.direction*2)<<3,-1);
				this._heap.spit.sprite.x = (this.direction>0) ?
					this.sprite.x+13 :
					this.sprite.x;
				this._heap.spit.sprite.y = this.sprite.y+7;
				this.sprite.map_room.addSprite(this._heap.spit.sprite);
				this.manager.add(this._heap.spit);

				this._heap.spit = null;
			}
		}
		else if (this.behavior_state===this.BEHAVIOR_STATES.tongue){
			

			if (this.tongue_out){
				this._heap.dx = this.tongue_target_x - (this.tongue_sprite.x + this.tongue_sprite.center.x);
				this._heap.dy = this.tongue_target_y - (this.tongue_sprite.y + this.tongue_sprite.center.y);
				this.tongue_sprite.x += (this._heap.dx>>2);
				this.tongue_sprite.y += (this._heap.dy>>2);

				// eating code for tongue
				
				this.tongue_sprite.getCollisions(this._heap.targets);
				for (this._heap.i = 0; this._heap.i<this._heap.targets.length; ++this._heap.i){
					if (this._heap.targets[this._heap.i].game_object instanceof EN_Mosquito){
						this.hp += this._heap.targets[this._heap.i].game_object.hp;
						this.infection += this._heap.targets[this._heap.i].game_object.infection;
						this._heap.targets[this._heap.i].game_object.damage(this,this._heap.targets[this._heap.i].game_object.hp);
						this.tongue_out = false;
						this.behavior_timer = 0;
					}
					else if (this._heap.targets[this._heap.i].game_object instanceof GO_Meli){
						this._heap.targets[this._heap.i].game_object.takedamage(this._inflictVal);
						this.tongue_out = false;
						this.behavior_timer = 0;
					}
				}
				this._heap.targets.length=0; // don't retain references
				
				this.anim_state = this.ANIM_STATES.tongue_out;
			}
			else{
				this._heap.dx = this.tongue_target_x - (this.sprite.x+this.mouth.x);
				this._heap.dy = this.tongue_target_y - (this.sprite.y+this.mouth.y);

				this.tongue_sprite.x = (this.sprite.x+this.mouth.x) + (((16-this.behavior_timer)*this._heap.dx)>>4);
				this.tongue_sprite.y = (this.sprite.y+this.mouth.y) + (((16-this.behavior_timer)*this._heap.dy)>>4);

				if (this.behavior_timer>=16){
					this.behavior_state = this.BEHAVIOR_STATES.sit;
					this.behavior_timer = 0;

					for (this._heap.i=0; this._heap.i<this.tongue_sprite_chain.length; ++this._heap.i){
						this.tongue_sprite_chain[this._heap.i].visible=false;
					}
					this.tongue_sprite.visible=false;
				}

				this.anim_state = this.ANIM_STATES.tongue_in;
			}

			this._heap.tx = this.sprite.x+this.mouth.x;
			this._heap.ty = this.sprite.y+this.mouth.y;

			this._heap.dx = this.tongue_sprite.x - this._heap.tx;
			this._heap.dy = this.tongue_sprite.y - this._heap.ty;
			// yes, implicitly assuming 3 links; try to keep to pow2-1 links to avoid division
			for (this._heap.i=0; this._heap.i<this.tongue_sprite_chain.length; ++this._heap.i){
				this.tongue_sprite_chain[this._heap.i].x = this._heap.tx + ((this._heap.dx*(this._heap.i+1))>>2);
				this.tongue_sprite_chain[this._heap.i].y = this._heap.ty + ((this._heap.dy*(this._heap.i+1))>>2);
			}
		}
		else {
			if (this.sprite.isTouchingTerrain(DEFINE_MAP_TERRAIN_TYPES.AIR)){
				this.NPCWalk();
			}
			else if (this.sprite.isTouchingTerrain(DEFINE_MAP_TERRAIN_TYPES.WATER)){
				this.NPCSwim();
			}

			if (this._heap.spriteMoveRes.flags & DEFINE_SPRITE_MOVE_RESULT_FLAGS.EJECTED_H){
				this.direction *= -1;
				this.behavior_state=this.BEHAVIOR_STATES.meander;
				this._heap.spriteMoveRes.flags &= ~DEFINE_SPRITE_MOVE_RESULT_FLAGS.EJECTED_H; // because _heap is shared by all instances, and thus if another instance moves and we don't, we get its _moveRes :P
			}
		}

		
		

		this.toggleInteractionsStd();

		if (this._heap.oldAnimState != this.anim_state){
			this.frame_timer=0;
		}
		this.frame_timer++;
		this._heap.aniFrame = (this.frame_timer>>(this.infection ? 3 : 4)) % this.anim_frames[this.anim_state].length;

		this.sprite.frame = this.anim_frames[this.anim_state][this._heap.aniFrame];

		//if (this.controls.held.B){
		//	this.sprite.frame = 4; // early hack before tongue was fully implemented; still safe/benign
		//}

		this.sprite.frame = this.sprite.frame & 7;

		if (this.infection>0) this.sprite.frame+=16;
		if (this.direction<0) this.sprite.frame+=8;
	};
	EN_Frog.prototype.damage = function(source,points){
		
		
		// face attacker when hit
		if (source!=this){
			if (source.sprite.x + (source.sprite.box_left+source.sprite.box_right)>>1 < this.sprite.x + (this.sprite.box_left+this.sprite.box_right)>>1){
				this.direction = -1;
			}
			else{
				this.direction = 1;
			}
		}

		if (points>0){
			this.hp-=points;
		}
		else{
			this._heap.cough = new GO_Smoke();
			
			this._heap.cough.sprite.x = this.sprite.x + ((this.direction>0) ? -8 : 8);
			this._heap.cough.sprite.y = this.sprite.y;
			this._heap.cough.launch_dir.x=((this.direction<0) ? -1 : 1);
			this._heap.cough.launch_dir.y=0;
			
			this._heap.cough.host_obj = this;

			this.sprite.map_room.addSprite(this._heap.cough.sprite);
			this.manager.add(this._heap.cough);

			this.infection--;
		}

		if (this.behavior_state===this.BEHAVIOR_STATES.seek){
			this.behavior_timer=0;
			this.behavior_state=this.BEHAVIOR_STATES.meander;
		}

		// if we're going away, clean up what won't get auto-cleaned
		if (this.hp<=0 && this.tongue_sprite.map_room!=null){
			this.tongue_sprite.map_room.removeSprite(this.tongue_sprite);

			for (this._heap.t = 0;  this._heap.t<this.tongue_sprite_chain.length; ++this._heap.t){
				this.tongue_sprite_chain[this._heap.t].map_room.removeSprite(this.tongue_sprite_chain[this._heap.t]);
			}
		}

		return (this.hp<=0);
		
	};
	EN_Frog.prototype._inflictVal = {hp:1, chi:2};
	EN_Frog.prototype.inflict = function(){
		return this._inflictVal;
	};
	EN_Frog.prototype.drop = function(){
		this._heap.dropRand = Math.random();
		if (this._heap.dropRand<0.1){
			return new GO_HeartDrop(GO_HeartDrop.SIZE.BIG);
		}
		else if (this._heap.dropRand<0.4){
			return new GO_HeartDrop(GO_HeartDrop.SIZE.SMALL);
		}
		return null;
	};


	EN_Frog.prototype.chooseTongue = function(dx,dy){
		if (dx<0){
			dx*=-1;
			dy*=-1;
		}

		this._heap.ady = Math.abs(dy);
		if (4*this._heap.ady<dx) return 12; // horizontal; 0 to 4:1
		else if (3*this._heap.ady<2*dx) return (dy<0) ? 13 : 31; // 2:1 angle; 4:1 to 3:2
		else if (2*this._heap.ady<3*dx) return (dy<0) ? 14 : 30; // 1:1 angle; 3:2 to 2:3
		else if (this._heap.ady<4*dx) return (dy<0) ? 15 : 29; // 1:2 angle; 2:3 to 1:4
		else return 28;
	};


