/*
 *  SDL-Fighter is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  SDL-Fighter is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "main.h"

/* NAME:        rand_ab
 * DESCRIPTION: Losuje liczby z przedziału od 'a' do 'b'
 */
int rand_ab(int a, int b)
{
	return a + (int) ( ((double)(b-a+1))*rand()/(RAND_MAX+1.0));
}

/* NAME:	LoadImage
 * DESCRIPTION: laduje obrazek 	
 */
SDL_Surface *LoadImage(char *filename)
{
	SDL_Surface *tmp1,*tmp2;
	tmp1 = IMG_Load(filename);
	if(!tmp1)
		return NULL;
	tmp2 = SDL_DisplayFormat(tmp1);
	SDL_FreeSurface(tmp1);
	return tmp2;
}

/* NAME:	SetBGAlpha
 * DESCRIPTION:	Ustawia dla podanej powierzchni przezroczystosc 
 *		na kolorze z lewego gornego rogu
 */
void SetBGAlpha(SDL_Surface *img)
{
	int bpp = img->format->BytesPerPixel;
	Uint32 key = 0;

	switch(bpp) 
	{
		case 1:
			key = ((Uint8*)img->pixels)[0];
			break;

		case 2:
			key = ((Uint16*)img->pixels)[0];
			break;

		case 3:
			key |= ((Uint8*)img->pixels)[0];
			key <<= 8;
			key |= ((Uint8*)img->pixels)[1];
			key <<= 8;
			key |= ((Uint8*)img->pixels)[2];
			break;

		case 4:
			key = ((Uint32*)img->pixels)[0];
			break;
	}
	SDL_SetColorKey(img, SDL_SRCCOLORKEY, key);
}

/* NAME:	ShipMoveDown
 * DESCRIPTION:	Porusza statkiem gracza w dol
 */
void ShipMoveDown(void)
{
	if(ship.energy != 0)
		ship.pos1.y=ship.pos1.y+((int)(ship.velocity*dt));
	if(ship.pos1.y>screen->h - ship.image->h)
		ship.pos1.y=screen->h - ship.image->h;
	ship.pos2.y = ship.pos1.y;

}

/* NAME:	ShipMoveUp
 * DESCRIPTION:	Porusza statkiem gracza w gore
 */
void ShipMoveUp(void)
{
	if(ship.energy != 0)
		ship.pos1.y=ship.pos1.y-((int)(ship.velocity*dt));
	if(ship.pos1.y<0)
		ship.pos1.y=0;
	ship.pos2.y = ship.pos1.y;
}

/* NAME:	ShipMoveRight
 * DESCRIPTION:	Porusza statkiem gracza w prawo
 */
void ShipMoveRight(void)
{
	if(ship.energy != 0)
		ship.pos1.x=ship.pos1.x+((int)(ship.velocity*dt));
	if(ship.pos1.x>screen->w - ship.image->w)
		ship.pos1.x=screen->w - ship.image->w;
	ship.pos2.x = ship.pos1.x;
}

/* NAME:	ShipMoveLeft
 * DESCRIPTION:	Porusza statkiem gracza w lewo
 */
void ShipMoveLeft(void)
{
	if(ship.energy != 0)
		ship.pos1.x=ship.pos1.x-((int)(ship.velocity*dt));
	if(ship.pos1.x<0)
		ship.pos1.x=0;
	ship.pos2.x = ship.pos1.x;
}

/* NAME:	MoveDown
 * DESCRIPTION:	Zmienia pozycje obiektu
 */
inline void MoveDown(struct object *obj)
{
	obj->pos1.y+=(int)(obj->velocity*dt);
	obj->pos2.y=obj->pos1.y;
}

/* NAME:        MoveUp
 * DESCRIPTION: Zmienia pozycje obiektu
 */
inline void MoveUp(struct object *obj)
{
	obj->pos1.y-=(int)(obj->velocity*dt);
	obj->pos2.y=obj->pos1.y;
}

/* NAME:	MoveRandomDown
 * DESCRIPTION:	Zmienia pozycje obiektu
 */
inline void MoveRandomDown(struct object *obj)
{
	obj->pos1.y+=(int)(obj->velocity*dt);
	obj->direction = rand_ab(0,6);
	switch(obj->direction)
	{
		case 0:
			obj->pos1.x-=(int)(obj->velocity*dt);
			break;
		case 1:
			obj->pos1.x+=(int)(obj->velocity*dt);
			break;
		default:
			break;
	}
	obj->pos2.y=obj->pos1.y;
	obj->pos2.x=obj->pos1.x;
}

/* NAME:        MoveRandomUp
 * DESCRIPTION: Zmienia pozycje obiektu
 */
inline void MoveRandomUp(struct object *obj)
{
	obj->pos1.y=obj->pos1.y-(int)(obj->velocity*dt);
	obj->direction = rand_ab(0,6);
	switch(obj->direction)
	{
		case 0:
			obj->pos1.x=obj->pos1.x-(int)(obj->velocity*dt);
			break;
		case 1:
			obj->pos1.x=obj->pos1.x+(int)(obj->velocity*dt);
			break;
		default:
			break;
	}
	obj->pos2.y=obj->pos1.y;
	obj->pos2.x=obj->pos1.x;
}

/* NAME:	MoveBOSS
 * DESCRIPTION:	
 */
void MoveBOSS(struct object *obj)
{
	if(obj->pos2.y < obj->image->h/2 && ship.pos1.y > obj->pos1.y) 
		obj->pos2.y+=(int)(obj->velocity*dt);
	else if(ship.pos2.y + ship.image->h < obj->pos1.y)
		obj->pos2.y-=(int)(obj->velocity*dt);

	if(obj->pos2.x + obj->image->w/2 > ship.pos1.x + ship.image->w/2)
		obj->pos2.x-=(int)(obj->velocity*dt);
	if(obj->pos2.x + obj->image->w/2 < ship.pos1.x + ship.image->w/2)
		obj->pos2.x+=(int)(obj->velocity*dt);

	obj->pos1.y = obj->pos2.y + obj->image->h/2 * sin(obj->angle);
	obj->pos1.x = obj->pos2.x + obj->image->w/3 * cos(obj->angle);
	obj->angle += 0.003f * dt;
	if(obj->angle >= 6.28f)
		obj->angle = 0.0f;
}

/* NAME:	MoveLittle
 * DESCRIPTION:	Moves little ship
 */
void MoveLittle(struct object *obj)
{
	if(boss.energy && is_boss)
	{
		obj->pos1.y = boss.pos1.y + boss.image->h/2 + littles_radius * sin(obj->angle) - obj->image->h/2;
		obj->pos1.x = boss.pos1.x + boss.image->w/2 + littles_radius * cos(obj->angle) - obj->image->w/2;
		obj->pos2.y = obj->pos1.y;
		obj->pos2.x = obj->pos1.x;
		obj->angle += 0.003f * dt;
		if(obj->angle >= 6.28f)
			obj->angle = 0.0f;
	}
	if(littles_radius < MAX_RADIUS)
		littles_radius+=2;
	if(!is_boss)
		MoveRandomDown(obj);
}

/* NAME:	AddOBJ
 * DESCRIPTION:	Dodaje obiekt
 */
void AddOBJ(int x,int y,int type, int *size, int max, struct object *objects)
{
	if(*size != max)
	{
		objects[*size].type = type;
		objects[*size].remove = 0;
		objects[*size].angle = 0.0f;

		switch(type) 
		{
			case BULLET:
			case MISSILE:
			case PLASMA_BULLET:
				objects[*size].pos1.x = x;
				objects[*size].pos1.y = y;	    

				if(type==BULLET)
				{
					objects[*size].direction = 2;
					objects[*size].image = obj[0];
					objects[*size].MoveOBJ = MoveDown;
				}
				else if(type==MISSILE)
				{
					objects[*size].direction = rand_ab(0,2); 
					objects[*size].image = obj[12];
					objects[*size].MoveOBJ = MoveRandomDown;
				}
				else
				{
					objects[*size].direction = 2;
					objects[*size].image = obj[13];
					objects[*size].MoveOBJ = MoveDown;
				}

				objects[*size].velocity = 0.7f; 
				objects[*size].energy = true;
				objects[*size].shield = 0;
				SetBGAlpha(objects[*size].image);
				break;
			case SHIP_BULLET:
			case SHIP_MISSILE:
			case SHIP_PLASMA_BULLET:
				if(type==SHIP_BULLET)
				{
					objects[*size].image = obj[0];
					objects[*size].direction = 2;
					objects[*size].MoveOBJ = MoveUp;
				}
				else if(type==SHIP_MISSILE)
				{
					objects[*size].direction = 2;
					objects[*size].image = obj[1];
					objects[*size].MoveOBJ = MoveRandomUp;
				}
				else
				{
					objects[*size].direction = 2;
					objects[*size].image = obj[13];
					objects[*size].MoveOBJ = MoveUp;
				}

				objects[*size].pos1.x = ship.pos1.x + ship.image->w/2 - objects[*size].image->w/2;
				objects[*size].pos1.y = ship.pos1.y - objects[*size].image->h;

				objects[*size].velocity = 0.7f;
				objects[*size].energy = true;
				objects[*size].shield = 0;
				SetBGAlpha(objects[*size].image);
				break;
			case ENEMY:
				objects[*size].image = obj[rand_ab(2,3)];

				objects[*size].pos1.x = rand_ab(20,screen->w - objects[*size].image->w - 20);
				objects[*size].pos1.y = -objects[*size].image->h - 10;

				objects[*size].velocity = 0.1f * rand_ab(1,3);
				objects[*size].tols = SDL_GetTicks();

				objects[*size].energy = 100;
				objects[*size].shield = 0;

				objects[*size].direction = rand_ab(0,2);
				objects[*size].MoveOBJ = MoveRandomDown;  
				SetBGAlpha(objects[*size].image);
				break;
			case LITTLE_ENEMY:
				littles_radius=0;
				objects[*size].image = obj[15];

				objects[*size].pos1.x = boss.pos1.x+boss.image->w/2-objects[*size].image->w/2;
				objects[*size].pos1.y = boss.pos1.y+boss.image->h/2-objects[*size].image->h/2;

				objects[*size].velocity = 0.1f * rand_ab(1,2);
				objects[*size].tols = SDL_GetTicks();

				objects[*size].energy = 70;
				objects[*size].shield = 0;

				objects[*size].MoveOBJ = MoveLittle;
				objects[*size].angle = 6.28f/MAX_LITTLES*num_littles;
				num_littles++;
				SetBGAlpha(objects[*size].image);
				break;
			case PACK_LIFE:
			case PACK_ENERGY:
			case PACK_SHIELD:
			case PACK_FAST_SHOOTING:
			case PACK_SURPRISE:
				objects[*size].velocity = 0.1f;
				if(type==PACK_LIFE)
					objects[*size].image = obj[4];
				else if(type==PACK_SHIELD)
					objects[*size].image = obj[5];
				else if(type==PACK_ENERGY)
					objects[*size].image = obj[6];
				else if(type==PACK_SURPRISE)
					objects[*size].image = obj[7];
				else if(type==PACK_FAST_SHOOTING)
					objects[*size].image = obj[16];

				objects[*size].energy = true;
				objects[*size].shield = 0;

				objects[*size].pos1.x = rand_ab(0,SCREEN_X-objects[*size].image->w);
				objects[*size].pos1.y = -objects[*size].image->h;
				objects[*size].MoveOBJ = MoveDown;
				break;
			default:
				break;
		}
		objects[*size].pos2.x = objects[*size].pos1.x;
		objects[*size].pos2.y = objects[*size].pos1.y;
		*size+=1;
	}
}

/* NAME:	AddEnemy
 * DESCRIPTION:	Add enemy
 */
void AddEnemy(int type)
{
	if(type != LITTLE_ENEMY || num_littles != MAX_LITTLES)
		AddOBJ(0,0,type,&num_enemies,MAX_ENEMIES,enemies);
}

/* NAME:	AddEnemyBullet
 * DESCRIPTION:	Adds enemy bullet
 */
void AddEnemyBullet(int x, int y, int type)
{
	AddOBJ(x,y,type,&num_ebullets, MAX_BULLETS, ebullets);
}

/* NAME:	AddShipBullet
 * DESCRIPTION:	Adds ship bullet
 */
void AddShipBullet(int type)
{
	AddOBJ(0,0,type,&num_ship_bullets, MAX_BULLETS, ship_bullets);
}

/* NAME:	AddPack
 * DESCRIPTION:	Adds pack
 */
void AddPack(int type)
{
	AddOBJ(0,0,type,&num_packs, MAX_PACKS, packs);
}

/* NAME:	SetBoss
 * DESCRIPTION:	Sets parameters for boss
 */
void SetBoss(void)
{
	if(current_level%2 != 1)
		boss.image = obj[8];
	else
		boss.image = obj[9];

	boss.pos1.x = SCREEN_X/2-boss.image->w/2;
	boss.pos1.y = -boss.image->h - 10;

	boss.pos2.x = boss.pos1.x;
	boss.pos2.y = boss.pos1.y;

	boss.velocity = 0.25f;
	boss.tols = SDL_GetTicks();

	boss.energy = BOSS_ENERGY;
	boss.shield = 0;

	boss.angle = 0;
	fawless_victory = true;

	boss.MoveOBJ = MoveBOSS;
	SetBGAlpha(boss.image);
}

/* NAME:	SetShip
 * DESCRIPTION:	Sets parameters for players ship
 */
void SetShip(void)
{
	ship.image = obj[10];
	SetBGAlpha(ship.image);

	player_ammo[0] = DEFAULT_BULLETS;
	player_ammo[1] = DEFAULT_MISSILES;
	player_ammo[2] = DEFAULT_PLASMAS;

	shooting_spd = 1.0f;
	
	ship.velocity = 0.4f;
	ship.energy = 100;
	ship.shield = 50;
	ship.pos1.x = SCREEN_X/2 - ship.image->w/2;
	ship.pos1.y = SCREEN_Y - ship.image->h;
	ship.pos2.x = ship.pos1.x;
	ship.pos2.y = ship.pos1.y;
	ship.weapon = CANNON;
}

/* NAME:	AddExplosion
 * DESCRIPTION:	Dodaje animowany wybuch
 */
void AddExplosion(int x,int y)
{
	if(num_explos!=MAX_EXPLOSIONS)
	{
		explosions[num_explos].pos1.x = x;
		explosions[num_explos].pos1.y = y; 

		explosions[num_explos].starty = y;
		explosions[num_explos].startx = x;

		explosions[num_explos].frame = 0;
		explosions[num_explos].ttw = 35;
		explosions[num_explos].tolfc = SDL_GetTicks();

		num_explos++;
	}
}

/* NAME:        AddExplosionAtObj
 * DESCRIPTION: Adds explosion in centre of object
 */
void AddExplosionAtObj(struct object obj)
{
	AddExplosion(obj.pos1.x + obj.image->w/2, obj.pos1.y + obj.image->h/2);
	Mix_PlayChannel(-1,chunk[3],0);
}

/* NAME:        AddExplosionAtBoss
 * DESCRIPTION: Adds three explosion in boss position
 */
void AddExplosionAtBoss(void)
{
	AddExplosion(boss.pos1.x + 30, boss.pos1.y + 40);
	AddExplosion(boss.pos1.x + boss.image->w - 30, boss.pos1.y + 40);
	AddExplosion(boss.pos1.x + boss.image->w/2, boss.pos1.y + boss.image->h/2 + 20);
	Mix_PlayChannel(-1,chunk[3],0);
	Mix_PlayChannel(-1,chunk[3],0);
}

/* NAME:        AddLittles
 * DESCRIPTION: Adds little ships
 */
void AddLittles(void)
{
	int i;
	for(i=0;i<MAX_LITTLES;i++)
		AddEnemy(LITTLE_ENEMY);
}

/* NAME:	LoadFrames
 * DESCRIPTION:	Laduje wszystkie ramki wybuchu
 */
void LoadFrames()
{
	int loop;
	char buf[100];
	char *name = "explos";
	char *fmt = "png";

	for(loop=1;loop<10;loop++)
	{
		sprintf(buf,explosion_dir"%s0%d.%s",name,loop,fmt);

		frame[loop-1] = LoadImage(buf);
		CheckSurface(frame[loop-1],"Unable to load %s: %s",buf ,SDL_GetError());
		SetBGAlpha(frame[loop-1]);
		SDL_SetAlpha(frame[loop-1],SDL_SRCALPHA,220);
	}
	for(loop=10;loop<=NUM_BOOM_FRAMES;loop++)
	{
		sprintf(buf,explosion_dir"%s%d.%s",name,loop,fmt);
		frame[loop-1] = LoadImage(buf);
		CheckSurface(frame[loop-1],"Unable to load %s: %s",buf ,SDL_GetError());
		SetBGAlpha(frame[loop-1]);
		SDL_SetAlpha(frame[loop-1],SDL_SRCALPHA,220);
	}
}

/* NAME:	DeleteOBJ
 * DESCRIPTION:	Usuwa obiekt
 */
void DeleteOBJ(int which, int *size, struct object *objects)
{
	if(*size != 0)
	{
		memmove(objects+which, objects+which+1, (*size-which)*EN_SIZE);
		*size-=1;
	}
}

/* NAME:	DeleteEnemy
 * DESCRIPTION:	Deletes enemy
 */
void DeleteEnemy(int which)
{
	DeleteOBJ(which, &num_enemies, enemies);
}

/* NAME:	DeleteEnemyBullet
 * DESCRIPTION:	Deletes enemy bullet
 */
void DeleteEnemyBullet(int which)
{
	DeleteOBJ(which, &num_ebullets, ebullets);
}

/* NAME:	DeleteShipBullet
 * DESCRIPTION:	Deletes ship bullet
 */
void DeleteShipBullet(int which)
{
	DeleteOBJ(which, &num_ship_bullets, ship_bullets);
}

/* NAME:	DeletePack
 * DESCRIPTION:	Deletes pack
 */
void DeletePack(int which)
{
	DeleteOBJ(which, &num_packs, packs);
}

/* NAME:        DeleteExplosion
 * DESCRIPTION: Usuwa wybuch
 */
void DeleteExplosion(int which)
{
	if(num_explos != 0)
	{
		memmove(explosions+which, explosions+which+1, (num_explos-which)*EX_SIZE);
		num_explos--;
	}
}

/* NAME:	DrawOBJ
 * DESCRIPTION:	rysuje obiekt
 */
inline void DrawOBJ(struct object obj)
{
	SDL_Rect tmp = {obj.pos1.x, obj.pos1.y, 0, 0};
	SDL_BlitSurface(obj.image,NULL,screen,&tmp);
}

/* NAME:	DrawExplosions
 * DESCRIPTION:	Rysuje wszystkie aktywne wybuchy
 */
void DrawExplosions()
{
	int i;
	SDL_Rect tmp;
	for(i=0;i<num_explos;i++)
	{
		explosions[i].pos1.x = explosions[i].startx - frame[explosions[i].frame]->w/2;
		explosions[i].pos1.y = explosions[i].starty - frame[explosions[i].frame]->h/2;
		tmp.x=explosions[i].pos1.x;
		tmp.y=explosions[i].pos1.y;
		if(explosions[i].frame < NUM_BOOM_FRAMES)
			SDL_BlitSurface(frame[explosions[i].frame],NULL,screen,&tmp);

		if(SDL_GetTicks() - explosions[i].tolfc > explosions[i].ttw && !paused_game)
		{
			explosions[i].frame++;
			explosions[i].tolfc = SDL_GetTicks();
			if(explosions[i].frame >= NUM_BOOM_FRAMES) {
				DeleteExplosion(i);
				i--;
			}
		}
	}
}
