/*******************************************************************************************
*
*   raylib - classic game: arkanoid
*
*   Sample game developed by Marc Palau and Ramon Santamaria
*
*   This game has been created using raylib v1.3 (www.raylib.com)
*   raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
*
*   Copyright (c) 2015 Ramon Santamaria (@raysan5)
*
*   Icon from https://www.flaticon.com/free-icon/arkanoid_2927802
********************************************************************************************/

#include <raylib.h>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

#if defined(PLATFORM_WEB)
#include <emscripten/emscripten.h>
#endif

//----------------------------------------------------------------------------------
// Some Defines
//----------------------------------------------------------------------------------
#define PLAYER_MAX_LIFE         5
#define LINES_OF_BRICKS         5
#define BRICKS_PER_LINE        20

//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef enum GameScreen { LOGO, TITLE, GAMEPLAY, ENDING } GameScreen;

typedef struct Player {
	Vector2 position;
	Vector2 size;
	int life;
} Player;

typedef struct Ball {
	Vector2 position;
	Vector2 speed;
	int radius;
	bool active;
} Ball;

typedef struct Brick {
	Vector2 position;
	bool active;
} Brick;

//------------------------------------------------------------------------------------
// Global Variables Declaration
//------------------------------------------------------------------------------------
static const int screenWidth = 800;
static const int screenHeight = 450;

static bool gameOver = false;
static bool pause = false;

static Player player = { 0 };
static Ball ball = { 0 };
static Brick brick[LINES_OF_BRICKS][BRICKS_PER_LINE] = { 0 };
static Vector2 brickSize = { 0 };

//------------------------------------------------------------------------------------
// Module Functions Declaration (local)
//------------------------------------------------------------------------------------
static void InitGame(void);         // Initialize game
static void UpdateGame(void);       // Update game (one frame)
static void DrawGame(void);         // Draw game (one frame)
static void UnloadGame(void);       // Unload game
static void UpdateDrawFrame(void);  // Update and Draw (one frame)

//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
	// Initialization (Note windowTitle is unused on Android)
	//---------------------------------------------------------
	InitWindow(screenWidth, screenHeight, "classic game: arkanoid");
	
	InitGame();
	
#if defined(PLATFORM_WEB)
	emscripten_set_main_loop(UpdateDrawFrame, 60, 1);
#else
	SetTargetFPS(60);
	//--------------------------------------------------------------------------------------
	
	// Main game loop
	while (!WindowShouldClose())    // Detect window close button or ESC key
	{
		// Update and Draw
		//----------------------------------------------------------------------------------
		UpdateDrawFrame();
		//----------------------------------------------------------------------------------
	}
#endif
	// De-Initialization
	//--------------------------------------------------------------------------------------
	UnloadGame();         // Unload loaded data (textures, sounds, models...)
	
	CloseWindow();        // Close window and OpenGL context
	//--------------------------------------------------------------------------------------
	
	return 0;
}

//------------------------------------------------------------------------------------
// Module Functions Definitions (local)
//------------------------------------------------------------------------------------

// Initialize game variables
void InitGame(void)
{
	brickSize = (Vector2){ GetScreenWidth()/BRICKS_PER_LINE, 40 };
	
	// Initialize player
	player.position = (Vector2){ screenWidth/2, screenHeight*7/8 };
	player.size = (Vector2){ screenWidth/10, 20 };
	player.life = PLAYER_MAX_LIFE;
	
	// Initialize ball
	ball.position = (Vector2){ screenWidth/2, screenHeight*7/8 - 30 };
	ball.speed = (Vector2){ 0, 0 };
	ball.radius = 7;
	ball.active = false;
	
	// Initialize bricks
	int initialDownPosition = 50;
	
	for (int i = 0; i < LINES_OF_BRICKS; i++)
	{
		for (int j = 0; j < BRICKS_PER_LINE; j++)
		{
			brick[i][j].position = (Vector2){ j*brickSize.x + brickSize.x/2, i*brickSize.y + initialDownPosition };
			brick[i][j].active = true;
		}
	}
}

// Update game (one frame)
void UpdateGame(void)
{
	if (!gameOver)
	{
		if (IsKeyPressed('P')) pause = !pause;
		
		if (!pause)
		{
			// Player movement logic
			if (IsKeyDown(KEY_LEFT)) player.position.x -= 5;
			if ((player.position.x - player.size.x/2) <= 0) player.position.x = player.size.x/2;
			if (IsKeyDown(KEY_RIGHT)) player.position.x += 5;
			if ((player.position.x + player.size.x/2) >= screenWidth) player.position.x = screenWidth - player.size.x/2;
			
			// Ball launching logic
			if (!ball.active)
			{
				if (IsKeyPressed(KEY_SPACE))
				{
					ball.active = true;
					ball.speed = (Vector2){ 0, -5 };
				}
			}
			
			// Ball movement logic
			if (ball.active)
			{
				ball.position.x += ball.speed.x;
				ball.position.y += ball.speed.y;
			}
			else
			{
				ball.position = (Vector2){ player.position.x, screenHeight*7/8 - 30 };
			}
			
			// Collision logic: ball vs walls
			if (((ball.position.x + ball.radius) >= screenWidth) || ((ball.position.x - ball.radius) <= 0)) ball.speed.x *= -1;
			if ((ball.position.y - ball.radius) <= 0) ball.speed.y *= -1;
			if ((ball.position.y + ball.radius) >= screenHeight)
			{
				ball.speed = (Vector2){ 0, 0 };
				ball.active = false;
				
				player.life--;
			}
			
			// Collision logic: ball vs player
			if (CheckCollisionCircleRec(ball.position, ball.radius,
				(Rectangle){ player.position.x - player.size.x/2, player.position.y - player.size.y/2, player.size.x, player.size.y}))
			{
				if (ball.speed.y > 0)
				{
					ball.speed.y *= -1;
					ball.speed.x = (ball.position.x - player.position.x)/(player.size.x/2)*5;
				}
			}
			
			// Collision logic: ball vs bricks
			for (int i = 0; i < LINES_OF_BRICKS; i++)
			{
				for (int j = 0; j < BRICKS_PER_LINE; j++)
				{
					if (brick[i][j].active)
					{
						// Hit below
						if (((ball.position.y - ball.radius) <= (brick[i][j].position.y + brickSize.y/2)) &&
							((ball.position.y - ball.radius) > (brick[i][j].position.y + brickSize.y/2 + ball.speed.y)) &&
							((fabs(ball.position.x - brick[i][j].position.x)) < (brickSize.x/2 + ball.radius*2/3)) && (ball.speed.y < 0))
						{
							brick[i][j].active = false;
							ball.speed.y *= -1;
						}
						// Hit above
						else if (((ball.position.y + ball.radius) >= (brick[i][j].position.y - brickSize.y/2)) &&
							((ball.position.y + ball.radius) < (brick[i][j].position.y - brickSize.y/2 + ball.speed.y)) &&
							((fabs(ball.position.x - brick[i][j].position.x)) < (brickSize.x/2 + ball.radius*2/3)) && (ball.speed.y > 0))
						{
							brick[i][j].active = false;
							ball.speed.y *= -1;
						}
						// Hit left
						else if (((ball.position.x + ball.radius) >= (brick[i][j].position.x - brickSize.x/2)) &&
							((ball.position.x + ball.radius) < (brick[i][j].position.x - brickSize.x/2 + ball.speed.x)) &&
							((fabs(ball.position.y - brick[i][j].position.y)) < (brickSize.y/2 + ball.radius*2/3)) && (ball.speed.x > 0))
						{
							brick[i][j].active = false;
							ball.speed.x *= -1;
						}
						// Hit right
						else if (((ball.position.x - ball.radius) <= (brick[i][j].position.x + brickSize.x/2)) &&
							((ball.position.x - ball.radius) > (brick[i][j].position.x + brickSize.x/2 + ball.speed.x)) &&
							((fabs(ball.position.y - brick[i][j].position.y)) < (brickSize.y/2 + ball.radius*2/3)) && (ball.speed.x < 0))
						{
							brick[i][j].active = false;
							ball.speed.x *= -1;
						}
					}
				}
			}
			
			// Game over logic
			if (player.life <= 0) gameOver = true;
			else
			{
				gameOver = true;
				
				for (int i = 0; i < LINES_OF_BRICKS; i++)
				{
					for (int j = 0; j < BRICKS_PER_LINE; j++)
					{
						if (brick[i][j].active) gameOver = false;
					}
				}
			}
		}
	}
	else
	{
		if (IsKeyPressed(KEY_ENTER))
		{
			InitGame();
			gameOver = false;
		}
	}
}

// Draw game (one frame)
void DrawGame(void)
{
	BeginDrawing();
	
	ClearBackground(RAYWHITE);
	
	if (!gameOver)
	{
		// Draw player bar
		DrawRectangle(player.position.x - player.size.x/2, player.position.y - player.size.y/2, player.size.x, player.size.y, BLACK);
		
		// Draw player lives
		for (int i = 0; i < player.life; i++) DrawRectangle(20 + 40*i, screenHeight - 30, 35, 10, LIGHTGRAY);
		
		// Draw ball
		DrawCircleV(ball.position, ball.radius, MAROON);
		
		// Draw bricks
		for (int i = 0; i < LINES_OF_BRICKS; i++)
		{
			for (int j = 0; j < BRICKS_PER_LINE; j++)
			{
				if (brick[i][j].active)
				{
					if ((i + j) % 2 == 0) DrawRectangle(brick[i][j].position.x - brickSize.x/2, brick[i][j].position.y - brickSize.y/2, brickSize.x, brickSize.y, GRAY);
					else DrawRectangle(brick[i][j].position.x - brickSize.x/2, brick[i][j].position.y - brickSize.y/2, brickSize.x, brickSize.y, DARKGRAY);
				}
			}
		}
		
		if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, GRAY);
	}
	else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, GRAY);
	
	EndDrawing();
}

// Unload game variables
void UnloadGame(void)
{
	// TODO: Unload all dynamic loaded data (textures, sounds, models...)
}

// Update and Draw (one frame)
void UpdateDrawFrame(void)
{
	UpdateGame();
	DrawGame();
}