/*
* Copyright 2023, Ali Arya
* Anyone Can Code: Algorithmic Thinking
* Example 5.14c
*/

#include "Main.h"

#define BULLET_SPEED 3
#define ENEMY_SPEED  3
#define SCREEN_W 640
#define SCREEN_H 480

struct GameObject
{
	int x;
	int y;
	SDLX_Bitmap* shape;
	int vx;
	int vy;
	bool visible;
};
struct ListItem
{
	GameObject* obj;
	ListItem* next;
};
struct PlayerBullets
{
	ListItem* first;	//first on the linked list
	ListItem* last;		//last on the linked list
	SDLX_Bitmap* shape;	//common shape for all bullets
};
//init object with a filename to load for shape
void InitObject(GameObject* obj, char* fname, int x, int y, int vx, int vy, bool v)
{
	obj->shape = SDLX_LoadBitmap(fname);
	obj->x = x;
	obj->y = y;
	obj->vx = vx;
	obj->vy = vy;
	obj->visible = v;
}
//init object with a pre-loaded shape
//this helps save resources when multiple objects share teh same image
void InitObject(GameObject* obj, SDLX_Bitmap* bmp, int x, int y, int vx, int vy, bool v)
{
	obj->shape = bmp;
	obj->x = x;
	obj->y = y;
	obj->vx = vx;
	obj->vy = vy;
	obj->visible = v;
}
void MoveObject(GameObject* obj)
{
	obj->x += obj->vx;
	obj->y += obj->vy;
}
void DrawObject(GameObject* obj)
{
	if(obj->visible == true)
		SDLX_DrawBitmap(obj->shape, obj->x, obj->y);
}
bool Collision(GameObject* o1, GameObject* o2)
{
	int distanceX = abs(o1->x - o2->x);
	int distanceY = abs(o1->y - o2->y);
	if (distanceX < 10 && distanceY < 10)
		return true;
	else
		return false;
}

void FireBullet(GameObject* p, PlayerBullets* b)
{
	GameObject* new_bullet = new GameObject();
	InitObject(new_bullet, b->shape, p->x, p->y, 0, -1*BULLET_SPEED, true);
	ListItem* li = new ListItem();
	li->obj = new_bullet;
	li->next = NULL;
	if (b->last == NULL)
	{
		b->last = b->first = li;
	}
	else
	{
		b->last->next = li;
		b->last = li;
	}
}
void MoveBullets(PlayerBullets* b)
{
	ListItem* temp = b->first;
	while (temp != NULL)
	{
		MoveObject(temp->obj);
		temp = temp->next;
	}
}
void DrawBullets(PlayerBullets* b)
{
	ListItem* temp = b->first;
	while (temp != NULL)
	{
		DrawObject(temp->obj);
		temp = temp->next;
	}
}
bool CheckBullets(PlayerBullets* b, GameObject* e)
{
	//check if any hits the enemy
	bool result = false;
	if (b->first == NULL)
		return false;
	if (Collision(b->first->obj, e))
		result = true;
	if (b->first->obj->y < 0) //remove when gets to the top
	{
		ListItem* temp = b->first;
		b->first = b->first->next;
		if (b->first == NULL)
			b->last = NULL;
		delete temp->obj;
		delete temp;
	}
	return result;
}
int main(int argc, char* argv[])
{
	/////////////////////////////////////////////////////////////////
	// INIT part of the game
	// things that are done only once at the start (initialization)
	/////////////////////////////////////////////////////////////////

	//init SDL library and graphics window
	SDLX_Init("Simple 2D Game", SCREEN_W, SCREEN_H, true);

	//game map (background)
	SDLX_Bitmap* map = SDLX_LoadBitmap("map.bmp", false, NULL); //do not use any mask for transparent color

	//objects
	SDLX_Bitmap* bullet_shape = SDLX_LoadBitmap("Bullet.bmp");
	GameObject player;
	InitObject(&player, (char*) "Player.bmp", 300, 450, 0, 0, true);
	PlayerBullets player_bullets;
	player_bullets.last = player_bullets.first = NULL;
	player_bullets.shape = bullet_shape;
	GameObject enemy;
	InitObject(&enemy, (char*)"Enemy.bmp", 300, 0, ENEMY_SPEED, 0, true);
	GameObject e_bullet;
	InitObject(&e_bullet, bullet_shape, 300, 0, 0, BULLET_SPEED, true);


	//main loop and winning
	bool quit = false;
	bool win = false;

	//left and right movement for player
	//true when key is pressed down. false when key released
	bool right = false;
	bool left = false;


	/////////////////////////////////////////////////////////////////
	//--------Main game Loop---------- -
	// things that are done repeatedly
	/////////////////////////////////////////////////////////////////
	while (!quit)
	{
		/////////////////////////////////////////////
		// UPDATE
		/////////////////////////////////////////////

		//check if there is any movement
		//player controlled by keyboard
		SDLX_Event e;
		//keyboard events
		while (SDLX_PollEvent(&e))
		{
			if (e.type == SDL_KEYDOWN)
			{
				//press escape to end the game
				if (e.keycode == SDLK_ESCAPE)
					quit = true;
				//press space bar to fire
				if (e.keycode == SDLK_SPACE)
				{
					FireBullet(&player, &player_bullets);
				}
	
				//update player  movement
				//we could just move the player here, but this way we can process multiple key presses better
				if (e.keycode == SDLK_RIGHT)
					right = true;
				if (e.keycode == SDLK_LEFT)
					left = true;
			}
			//this is needed only if we are using left and right flags for movement
			if (e.type == SDL_KEYUP)
			{
				if (e.keycode == SDLK_RIGHT)
					right = false;
				if (e.keycode == SDLK_LEFT)
					left = false;
			}
		}

		//update enemy 
		MoveObject(&enemy);
		if (enemy.x < 0 || enemy.x > SCREEN_W)
			enemy.vx *= -1;
		if (e_bullet.visible == false && abs(enemy.x - player.x)<5) //fire enemy bullet
		{
			e_bullet.x = enemy.x;
			e_bullet.y = enemy.y;
			e_bullet.visible = true;
		}
		MoveObject(&e_bullet);
		if (Collision(&player, &e_bullet))
		{
			quit = true;
			win = false;
			player.visible = false;
		}
		//check if enemy bullet out of screen
		if (e_bullet.y > SCREEN_H) 
			e_bullet.visible = false;

		//update player 
		if (right)
			player.x += 2;
		if (left)
			player.x -= 2;
		MoveBullets(&player_bullets);
		if (CheckBullets(&player_bullets, &enemy))
		{
			enemy.visible = false;
			quit = true;
			win = true;
		}

		/////////////////////////////////////////////
		// DRAW
		/////////////////////////////////////////////
		//note the order (last one will be drawn on top)
		SDLX_DrawBitmap(map, 0, 0);
		DrawObject(&e_bullet);
		DrawObject(&enemy);
		DrawObject(&player);
		DrawBullets(&player_bullets);

		//Update the screen
		SDLX_Render();


		//now wait for loop timing
		SDLX_Delay(1);
	}

	SDLX_Delay(3000);

	return 0;
}