#include "main.h"

#define ALIENS_BLUE_FRAME 19
#define ALIENS_PURPLE_FRAME_START 16
#define ALIENS_PURPLE_NUMFRAMES 8

#define EXPLODE_ANIMATE_SPEED 3

#define ALIEN_TYPE_BLUE		0
#define ALIEN_TYPE_PURPLE	1

#define ALIEN_BULLET_TILE	((4*2 * 20) + 4)

typedef struct
{
	u16 frame, flip;
} alien_purple_frame;

const alien_purple_frame apframes[] = {
	{ 0, 0 },
	{ 1, 0 },
	{ 2, 0 },
	{ 1, VERTICAL_FLIP },
	{ 0, VERTICAL_FLIP },
	{ 1, VERTICAL_FLIP | HORIZONTAL_FLIP },
	{ 2, HORIZONTAL_FLIP },
	{ 1, HORIZONTAL_FLIP }
};

alien_bullet* aliens_create_bullets(int num)
{
	alien_bullet* b = calloc(sizeof(alien_bullet), num);
	int i;

	for(i=0; i<num; i++)
	{
		b[i].sprite.bx = 3;
		b[i].sprite.by = 3;
		b[i].sprite.bw = 2;
		b[i].sprite.bh = 2;
	
		b[i].sprite.a0 = COLOR_256 | SQUARE;
		b[i].sprite.a1 = SIZE_8;
		b[i].sprite.tile = ALIEN_BULLET_TILE;
		b[i].sprite.pri = 2;

		b[i].distance_total = 350;
		b[i].active = 0;
	}
	
	return b;
}

void aliens_create_wave_blue(alien* aliens, int num)
{
	int i;
	for(i=0; i < num; i++)
	{
		alien_blue* ab;
		alien* a = &aliens[i];
		a->x = FIXED_INT(240 + (i * 20));
		//a->y = rand() / (RAND_MAX / (160-16));
		a->y = FIXED_INT(i * 34);
		a->active = 1;
		a->explode = 0;

		a->sprite.sprite = sprite_alloc();
		a->sprite.bx = 2;		a->sprite.by = 2;
		a->sprite.bw = 12;	a->sprite.bh = 12;
		a->sprite.tile = (4*2) * ALIENS_BLUE_FRAME;
		a->sprite.pri = 1;

		a->type = ALIEN_TYPE_BLUE;
		ab = malloc(sizeof(alien_blue));
		ab->speed = FIXED_INT(0.9);
		a->data = ab;

		a->bullets = NULL; a->num_bullets = 0;

		a->sprite.a0 = COLOR_256 | SQUARE;
		a->sprite.a1 = SIZE_16;
	}
}

void aliens_free_wave(alien* aliens, int num)
{
	int i;
	for(i=0; i < num; i++)
	{
		if(aliens[i].data) free(aliens[i].data);
	}
}

void aliens_create_wave_purple(alien* aliens, int num)
{
	int i;
	for(i=0; i < num; i++)
	{
		//char foo[100];
		alien_purple* ap;
		alien* a = &aliens[i];

		//sprintf(foo, "purple: %d\n", i); print(foo);

		a->x = FIXED_INT(240 + (i * 20));
		a->y = FIXED_INT((160-16) - (i * 30));
		a->active = 1;
		a->explode = 0;

		a->sprite.sprite = sprite_alloc();
		a->sprite.bx = 2;		a->sprite.by = 2;
		a->sprite.bw = 12;	a->sprite.bh = 12;
		a->sprite.pri = 1;

		a->type = ALIEN_TYPE_PURPLE;
		ap = malloc(sizeof(alien_purple));
		ap->x_speed = FIXED_INT(-0.8);
		ap->y_speed = FIXED_INT(2);
		ap->frame = 0;
		a->data = ap;

		a->num_bullets = 1;
		a->bullets = aliens_create_bullets(a->num_bullets);
		
		a->sprite.a0 = COLOR_256 | SQUARE;
		a->sprite.a1 = SIZE_16;
	}
}

void aliens_draw(game* g)
{
	int i;
	//char foo[100];
	s32 dx=0, dy=0;

	for(i=0; i < g->num_aliens; i++)
	{
		alien* a = &g->aliens[i];
		if(a->active)
		{
			switch(a->type)
			{
				case ALIEN_TYPE_BLUE:
				{
					alien_blue* data = (alien_blue*)a->data;
					
					a->x -= data->speed;
					if(a->x <= FIXED_INT(-16)) a->active = 0;
					
					break;
				}
				case ALIEN_TYPE_PURPLE:
				{
					alien_purple* data = (alien_purple*)a->data;
				
					a->x += data->x_speed;
					if(a->x <= FIXED_INT(-16)) a->active = 0;

					a->y += data->y_speed;
					if(a->y < FIXED_INT(0) || a->y > FIXED_INT(160-16)) data->y_speed = -data->y_speed;

					a->sprite.a1 &= ~(HORIZONTAL_FLIP | VERTICAL_FLIP);
					a->sprite.tile = (apframes[data->frame / 8].frame + ALIENS_PURPLE_FRAME_START) * (4*2);
					a->sprite.a1 |= apframes[data->frame/8].flip;

					data->frame++;
					if(data->frame == ALIENS_PURPLE_NUMFRAMES*8) data->frame=0;

					dx = data->x_speed; dy = data->y_speed;
					break;
				}
			}	
			a->sprite.x = FIXED_TO_INT(a->x);
			a->sprite.y = FIXED_TO_INT(a->y);
			DRAW_SPRITE(a->sprite);

			/*sprintf(foo, "sprite: %d x: %d y: %d sx: %d sy: %d dx: %d dy: %d\n", 
				a->sprite.sprite, a->x, a->y, a->sprite.x, a->sprite.y, dx, dy);
			print(foo);*/

			/* handle firing */
			if(a->num_bullets)
			{
				int i;
				
				if(rand() < (RAND_MAX / 600))
				{
					/* fire a bullet! */

					for(i=0; i < g->alien_bullets_num; i++)
					{
						if(g->alien_bullets[i].active == 0)
						{
							alien_bullet* b = &g->alien_bullets[i];
							s32 dx, dy;
							s32 dist, scale_factor;
							//char foo[100];
							
							b->x = a->x; b->y = a->y;
							b->distance_travelled = 0;
							b->active = 1;

							dx = FIXED_TO_INT(g->ship->x - b->x);
							dy = FIXED_TO_INT(g->ship->y - b->y);
							dist = sqrt(dx*dx + dy*dy);
							scale_factor = (dist << 8) / FIXED_INT(0.6);
							/*sprintf(foo, "dx: %d %d dy: %d %d dist: %d xs: %d ys: %d\n",
									dx, (g->ship->x - b->x), 
									dy, (g->ship->y - b->y), 
									dist,
									(g->ship->x - b->x) / scale_factor,
									(g->ship->y - b->y) / scale_factor);
							print(foo);*/
							
							//b->x_speed = FIXED_INT(-2); b->y_speed = FIXED_INT(0);
							b->x_speed = (g->ship->x - b->x) / scale_factor;
							b->y_speed = (g->ship->y - b->y) / scale_factor;
							
							b->sprite.sprite = sprite_alloc();

							break;
						}
					}
				}
			}
			
			if(a->active == 0)
			{
				sprite_free(a->sprite.sprite);
			}
		}
		if(a->explode)
		{
			a->sprite.x = FIXED_TO_INT(a->x);
			a->sprite.y = FIXED_TO_INT(a->y);
			a->sprite.tile = ((a->explode / EXPLODE_ANIMATE_SPEED) + EXPLOSION_FRAMES_START - 1) * (4*2);
			DRAW_SPRITE(a->sprite);
			
			a->explode++;
			if(a->explode > (EXPLOSION_FRAMES_NUM * EXPLODE_ANIMATE_SPEED))
			{
				a->explode = 0;
				sprite_free(a->sprite.sprite);
			}
		}
	}

	/* draw bullets */
	for(i=0; i < g->alien_bullets_num; i++)
	{
		if(g->alien_bullets[i].active == 1)
		{
			alien_bullet* b = &g->alien_bullets[i];

			b->distance_travelled++;
			b->x += b->x_speed;
			b->y += b->y_speed;

			if(b->distance_travelled > b->distance_total)
			{
				b->active = 0;
				sprite_free(b->sprite.sprite);
				break;
			}

			b->sprite.x = FIXED_TO_INT(b->x);
			b->sprite.y = FIXED_TO_INT(b->y);
			DRAW_SPRITE(b->sprite);
		}
	}
}

/* macro for checking collision of bounding boxes */

#define BOUNDS_COLLIDE(minx1, miny1, w1, h1, minx2, miny2, w2, h2) \
 (((minx1 + w1) >= minx2) && (minx1 <= (minx2 + w2)) && \
	((miny1 + h1) >= miny2) && (miny1 <= (miny2 + h2)))

#define SPRITE_BOUNDS_COLLIDE(s1, s2) \
 (((s1.x + s1.bx + s1.bw) >= (s2.x + s2.bx)) && \
	((s1.x + s1.bx) <= (s2.x + s2.bx + s2.bw)) && \
  ((s1.y + s1.by + s1.bh) >= (s2.y + s2.by)) && \
	((s1.y + s1.by) <= (s2.y + s2.by + s2.bh)))
 
int collide_aliens_bullets_ship(game* g)
{
	int i;
	playership* ship = g->ship;

	/* iterate through the aliens */
	for(i=0; i < g->num_aliens; i++)
	{
		alien* a = &g->aliens[i];

		if(a->active && a->sprite.x < 240)
		{
			/* check this alien against the ship */

			if(SPRITE_BOUNDS_COLLIDE(ship->sprite, a->sprite))
			{
				/* we're dead - return true */
				return(1);
			}
			
			/* check this alien against each bullet in turn */
			int bul;
			for(bul=0; bul < ship->num_bullets; bul++)
			{
				bullet* b = &ship->bullets[bul];
				if(b->active)
				{
					/* we have an active bullet and an active alien - see if they
					 * collide */
					
					/* check x and y bounds */
					if(SPRITE_BOUNDS_COLLIDE(b->sprite, a->sprite))
					{
						/* woo - the alien and bullet bounding boxes collide */

						/* we should do a per-pixel comparison of the sprite data to make
						 * sure the objects (as opposed to their bounding boxes) have
						 * collided - do this later :) */

						b->active = 0;
						a->active = 0;
						a->explode = 1;
						sfxSystemPlay(&exp_sample);

						sprite_free(b->sprite.sprite);
						ship->score++;
					}
				}
			}
		}
	}
	
	/* iterate through the alien bullets */
	for(i=0; i < g->alien_bullets_num; i++)
	{
		if(g->alien_bullets[i].active)
		{
			/* check this bullet against the ship */
			if(SPRITE_BOUNDS_COLLIDE(ship->sprite, g->alien_bullets[i].sprite))
			{
				/* we're dead - return true */
				return(1);
			}
		}
	}
	
	return(0);
}

