#include "missile.h"
#include "weapons.h"

#include "../../ObjManager.h"
#include "../../caret.h"
#include "../../trig.h"
#include "../../sound/sound.h"
#include "../../common/misc.h"


#include "../../game.h"
#include "../../player.h"
#include "../../autogen/sprites.h"


#define STATE_WAIT_RECOIL_OVER		1
#define STATE_RECOIL_OVER			2
#define STATE_MISSILE_CAN_EXPLODE	3

struct MissileSettings
{
	int maxspeed;		// max speed of missile
	int hitrange;		// 
	int lifetime;		// number of boomflashes to create on impact
	int boomrange;		// max dist away to create the boomflashes
	int boomdamage;		// damage dealt by contact with a boomflash (AoE damage)
}
missile_settings[] =
{
//  Level 1-3 regular missile
//  maxspd   hit,  life, range,  bmdmg
	{0xA00,  16,   10,   16,     1},
	{0xA00,  16,   15,   32,     1},
	{0xA00,  16,   5,    40,     1},
	
//  Level 1-3 super missile
//  maxspd   hit,  life, range, bmdmg
	{0x1400, 12,   10,   16,    2},
	{0x1400, 12,   14,   32,    2},
	{0x1400, 12,   6,    40,    2}
};


INITFUNC(AIRoutines)
{
	AFTERMOVE(OBJ_MISSILE_SHOT, ai_missile_shot);
	AFTERMOVE(OBJ_SUPERMISSILE_SHOT, ai_missile_shot);
	ONTICK(OBJ_MISSILE_BOOM_SPAWNER, ai_missile_boom_spawner_tick);
	AFTERMOVE(OBJ_MISSILE_BOOM_SPAWNER, ai_missile_boom_spawner);
}

/*
void c------------------------------() {}
*/

void ai_missile_shot(Object *o)
{
	int index = o->shot.level + ((o->type == OBJ_SUPERMISSILE_SHOT) ? 3 : 0);
	MissileSettings *settings = &missile_settings[index];
	
	if (o->state == 0)
	{
		o->shot.damage = 0;
		
		if (o->shot.level == 2)
		{
			// initilize wavey effect
			o->xmark = o->x;
			o->ymark = o->y;
			o->speed = (o->type == OBJ_SUPERMISSILE_SHOT) ? -64 : -32;
			
			// don't let it explode until the "recoil" effect is over.
			o->state = STATE_WAIT_RECOIL_OVER;
			// record position we were fired at (we won't explode until we pass it)
			o->xmark2 = player->x;
			o->ymark2 = player->y;
		}
		else
		{
			o->state = STATE_MISSILE_CAN_EXPLODE;
		}
	}
	
	// accelerate according to current type and level of missile
	// don't use LIMITX here as it can mess up recoil of level 3 super missiles
	switch(o->shot.dir)
	{
		case RIGHT:
			o->xinertia += o->shot.accel;
			if (o->xinertia > settings->maxspeed) o->xinertia = settings->maxspeed;
		break;
		
		case LEFT:
			o->xinertia -= o->shot.accel;
			if (o->xinertia < -settings->maxspeed) o->xinertia = -settings->maxspeed;
		break;
		
		case UP:
			o->yinertia -= o->shot.accel;
			if (o->yinertia < -settings->maxspeed) o->yinertia = -settings->maxspeed;
		break;
		
		case DOWN:
			o->yinertia += o->shot.accel;
			if (o->yinertia > settings->maxspeed) o->yinertia = settings->maxspeed;
		break;
	}
	
	// wavey effect for level 3
	// (markx/y is used as a "speed" value here)
	if (o->shot.level == 2)
	{
		if (o->shot.dir == LEFT || o->shot.dir == RIGHT)
		{
			if (o->y >= o->ymark) o->speed = (o->type == OBJ_SUPERMISSILE_SHOT) ? -64 : -32;
			else o->speed = (o->type == OBJ_SUPERMISSILE_SHOT) ? 64 : 32;
			o->yinertia += o->speed;
		}
		else
		{
			if (o->x >= o->xmark) o->speed = (o->type == OBJ_SUPERMISSILE_SHOT) ? -64 : -32;
			else o->speed = (o->type == OBJ_SUPERMISSILE_SHOT) ? 64 : 32;
			o->xinertia += o->speed;
		}
	}
	
	// check if we hit an enemy
	// level 3 missiles can not blow up while they are "recoiling"
	// what we do is first wait until they're traveling in the direction
	// they're pointing, then wait till they pass the player's original position.
	switch(o->state)
	{
		case STATE_WAIT_RECOIL_OVER:
			switch(o->shot.dir)
			{
				case LEFT: if (o->xinertia <= 0) o->state = STATE_RECOIL_OVER; break;
				case RIGHT: if (o->xinertia >= 0) o->state = STATE_RECOIL_OVER; break;
				case UP: if (o->yinertia <= 0) o->state = STATE_RECOIL_OVER; break;
				case DOWN: if (o->yinertia >= 0) o->state = STATE_RECOIL_OVER; break;
			}
		if (o->state != STATE_RECOIL_OVER)
			break;
			
		case STATE_RECOIL_OVER:
			switch(o->shot.dir)
			{
				case LEFT: if (o->x <= o->xmark2-(2 * CSFI)) o->state = STATE_MISSILE_CAN_EXPLODE; break;
				case RIGHT: if (o->x >= o->xmark2+(2 * CSFI)) o->state = STATE_MISSILE_CAN_EXPLODE; break;
				case UP: if (o->y <= o->ymark2-(2 * CSFI)) o->state = STATE_MISSILE_CAN_EXPLODE; break;
				case DOWN: if (o->y >= o->ymark2+(2 * CSFI)) o->state = STATE_MISSILE_CAN_EXPLODE; break;
			}
		if (o->state != STATE_MISSILE_CAN_EXPLODE)
			break;
			
		case STATE_MISSILE_CAN_EXPLODE:
		{
			bool blow_up = false;
			
			if (damage_enemies(o))
			{
				blow_up = true;
			}
			else
			{	// check if we hit a wall
				if (o->shot.dir==LEFT && o->blockl) 	  blow_up = true;
				else if (o->shot.dir==RIGHT && o->blockr) blow_up = true;
				else if (o->shot.dir==UP && o->blocku)	  blow_up = true;
				else if (o->shot.dir==DOWN && o->blockd)  blow_up = true;
			}
			
			if (blow_up)
			{
				sound(SND_MISSILE_HIT);
				
				// create the boom-spawner object for the flashes, smoke, and AoE damage
				int y = o->CenterY();
				if (o->shot.dir==LEFT || o->shot.dir==RIGHT) y-=3*CSFI;
				Object *sp = CreateBullet(o->CenterX(), y, OBJ_MISSILE_BOOM_SPAWNER);
				
				sp->shot.boomspawner.range = settings->hitrange;
				sp->shot.boomspawner.booms_left = settings->lifetime;
				sp->shot.damage = settings->boomdamage;
				sp->shot.level = settings->boomdamage;
				
				o->Delete();
				return;
			}
		}
		break;
	}
	
	if (--o->shot.ttl < 0)
		shot_dissipate(o, EFFECT_STARPOOF);
	
	// smoke trails
	if (++o->timer > 2)
	{
		o->timer = 0;
		Caret *trail = effect(o->CenterX() - o->xinertia, \
							  o->CenterY() - o->yinertia, EFFECT_SMOKETRAIL);
		
		const int trailspd = 0x400;
		switch(o->shot.dir)
		{
			case LEFT:	trail->xinertia = trailspd;  trail->y -= (2 * CSFI); break;
			case RIGHT: trail->xinertia = -trailspd; trail->y -= (2 * CSFI); break;
			case UP:	trail->yinertia = trailspd;  trail->x -= (1 * CSFI); break;
			case DOWN:	trail->yinertia = -trailspd; trail->x -= (1 * CSFI); break;
		}
	}
	
}

void ai_missile_boom_spawner(Object *o)
{
	
	if (o->state == 0)
	{
		o->state = 1;
		o->timer = 0;
		
		o->xmark = o->x;
		o->ymark = o->y;
		
		// give us the same bounding box as the boomflash effects
		o->sprite = SPR_BOOMFLASH;
		o->invisible = true;
	}
	
	if (!(o->shot.boomspawner.booms_left % 3))
	{
	    int range = 0;
	    switch (o->shot.level)
	    {
	        case 1:
	          range = 16;
	          break;
	        case 2:
	          range = 32;
	          break;
	        case 3:
	          range = 40;
	          break;
	    }
		int x = o->CenterX() + (random(-range, range) * CSFI);
		int y = o->CenterY() + (random(-range, range) * CSFI);
		
		effect(x,y, EFFECT_BOOMFLASH);
		missilehitsmoke(x,y, o->shot.boomspawner.range);
	}
	
	if (--o->shot.boomspawner.booms_left < 0)
		o->Delete();
}

void ai_missile_boom_spawner_tick(Object *o)
{
	damage_all_enemies_in_bb(o, FLAG_INVULNERABLE, o->CenterX(), o->CenterY(), o->shot.boomspawner.range);
}

static void missilehitsmoke(int x, int y, int range)
{
	int smokex = x + (random(-range, range) * CSFI);
	int smokey = y + (random(-range, range) * CSFI);
	Object *smoke;
	
	for(int i=0;i<2;i++)
	{
		smoke = CreateObject(smokex, smokey, OBJ_SMOKE_CLOUD);
		smoke->sprite = SPR_MISSILEHITSMOKE;
		vector_from_angle(random(0,255), random(0x100,0x3ff), &smoke->xinertia, &smoke->yinertia);
	}
}






