/*******************************************************************************************
*
*   raylib - sample game: space invaders
*
*   Sample game developed by Ian Eito, Albert Martos 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)
*
********************************************************************************************/

#include "raylib.h"

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

//----------------------------------------------------------------------------------
// Some Defines
//----------------------------------------------------------------------------------
#define NUM_SHOOTS 50
#define NUM_MAX_ENEMIES 50
#define FIRST_WAVE 10
#define SECOND_WAVE 10
#define THIRD_WAVE 20
#define FOURTH_WAVE 20
#define ItemNumber 3

//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef enum { FIRST = 0, SECOND, THIRD, FOURTH } EnemyWave;
typedef enum {Life=0,Speed,SizeShoot} ItemEffect;

typedef struct Player{
    Rectangle rec;
    Vector2 speed;
    Color color;
    int life;
} Player;

typedef struct Enemy{
    Rectangle rec;
    Vector2 speed;
    bool active;
    Color color;
    bool boss;
    int life;
} Enemy;

typedef struct Shoot{
    Rectangle rec;
    Vector2 speed;
    bool active;
    Color color;
    bool bonus; 
} Shoot;

typedef struct Item{
    
    Rectangle rec;
    Vector2 speed;
    ItemEffect effect;
    Color color;
    bool active;
} Item; 



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

static Music music;
static Sound sound1; 
static Sound sound2;

static bool gameOver = false;
static bool pause =  false;
static int score = 0;
static bool victory = false;
static int highscore;

static Player player = { 0 };
static Enemy enemy[NUM_MAX_ENEMIES] = { 0 };
static Shoot shoot[NUM_SHOOTS] = { 0 };
static EnemyWave wave = { 0 };
static Item item[ItemNumber] = {0};

static int shootRate = 0;
static float alpha = 0.0f;

static int activeEnemies = 0;
static int enemiesKill = 0;
static bool smooth = false;
static int ItemNumberWave= 1;

//------------------------------------------------------------------------------------
// 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, "sample game: space invaders");
    InitAudioDevice();
    music = LoadMusicStream("music.mp3");
    PlayMusicStream(music);
    sound1 = LoadSound("Hit.mp3");
    sound2 = LoadSound("sound2.mp3");


    InitGame();

#if defined(PLATFORM_WEB)
    emscripten_set_main_loop(UpdateDrawFrame, 0, 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...)
    UnloadMusicStream(music);    // Unload music stream buffers from RAM
    UnloadSound(sound1);
    UnloadSound(sound2);
     
    CloseAudioDevice();     
    CloseWindow();        // Close window and OpenGL context
    //--------------------------------------------------------------------------------------

    return 0;
}

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

// Initialize game variables
void InitGame(void)
{
    // Initialize game variables
    shootRate = 0;
    pause = false;
    gameOver = false;
    victory = false;
    smooth = false;
    wave = FIRST;
    activeEnemies = FIRST_WAVE;
    enemiesKill = 0;
    score = 0;
    alpha = 0;
    highscore =LoadStorageValue(0);
    PlayMusicStream(music);

    // Initialize player
    player.rec.x =  20;
    player.rec.y = 100;
    player.rec.width = 20;
    player.rec.height = 20;
    player.speed.x = 5;
    player.speed.y = 5;
    player.color = BLACK;
    player.life =10;

    // Initialize enemies
    for (int i = 0; i < NUM_MAX_ENEMIES; i++)
    {
        enemy[i].rec.width = 10;
        enemy[i].rec.height = 10;
        enemy[i].rec.x = GetRandomValue(screenWidth, screenWidth + 1000);
        enemy[i].rec.y = GetRandomValue(0, screenHeight - enemy[i].rec.height);
        enemy[i].speed.x = 5;
        enemy[i].speed.y = 5;
        enemy[i].active = true;
        enemy[i].color = GRAY;
        enemy[i].boss = false;
        enemy[i].life = 0;
    }
    
   
    // Initialize shoots
    for (int i = 0; i < NUM_SHOOTS; i++)
    {
        shoot[i].rec.x = player.rec.x;
        shoot[i].rec.y = player.rec.y + player.rec.height/4;
        shoot[i].rec.width = 10;
        shoot[i].rec.height = 5;
        shoot[i].speed.x = 10;
        shoot[i].speed.y = 0;
        shoot[i].active = false;
        shoot[i].color = MAROON;
        shoot[i].bonus = false;
    }
    
    // Initialize Item 
    for (int i =0; i<ItemNumber;i++)
    {
        item[i].rec.x =  5;
        item[i].rec.y = 5;
        item[i].rec.width = 20;
        item[i].rec.height= 20;
        item[i].speed.x = 5;
        item[i].speed.y = 5;
        item[i].effect = i;
        switch (item[i].effect)
        {
            case 0:
            item[i].color=RED;
            break;
            case 1:
            item[i].color = GOLD;
            break;
            case 2:
            item[i].color = PINK;
            break;
            default: 
            item[i].color = GRAY;
            break;
        }
        item[i].active = true;
    }
    
    
}

// Update game (one frame)
void UpdateGame(void)
{
    if (!gameOver)
    {
        UpdateMusicStream(music);

        if (IsKeyPressed('P')) pause = !pause;

        if (!pause)
        {
            switch (wave)
            {
                case FIRST:
                {
                    
                    if (!smooth)
                    {
                        alpha += 0.02f;
                        if (alpha >= 1.0f) smooth = true;
                    }
                    
                    if (smooth) alpha -= 0.02f;
                   
                    if (enemiesKill == activeEnemies)
                    {
                        enemiesKill = 0;

                        for (int i = 0; i < activeEnemies; i++)
                        {
                            if (!enemy[i].active) enemy[i].active = true;
                        }
                        
                        activeEnemies = SECOND_WAVE;
                        wave = SECOND;
                        smooth = false;
                        alpha = 0.0f;
                    }
                    ItemNumberWave = 2;
                } break;
                
                case SECOND:
                {
                    if (!smooth)
                    {
                        alpha += 0.02f;
                        
                        if (alpha >= 1.0f) smooth = true;
                    }
                    
                    if (smooth) alpha -= 0.02f;

                    if (enemiesKill == activeEnemies)
                    {
                        enemiesKill = 0;

                         for (int i = 0; i < activeEnemies; i++)
                        {
                            enemy[i].active= true; 
                            enemy[i].rec.width =25;
                            enemy[i].rec.height = 25;
                            enemy[i].rec.x = screenWidth/2;
                            enemy[i].rec.y = 200;
                            enemy[i].speed.x = 5;
                            enemy[i].speed.y = 5;
                            enemy[i].active = true;
                            enemy[i].color = GRAY;
                            enemy[i].boss = false;
                            enemy[i].life = 10;
                        }
                        
                        activeEnemies = THIRD_WAVE;
                        wave = THIRD;
                        smooth = false;
                        alpha = 0.0f;
                    }
                } break;
                case THIRD:
                {
                    if (!smooth)
                    {
                        alpha += 0.02f;
                        
                        if (alpha >= 1.0f) smooth = true;
                    }
                    
                    if (smooth) alpha -= 0.02f;
                    
                    if (enemiesKill == activeEnemies)
                    {
                        enemiesKill = 0;
                         //Initiliaze the boss
                        enemy[0].active= true; 
                        enemy[0].rec.width =200;
                        enemy[0].rec.height = 700;
                        enemy[0].rec.x = screenWidth;
                        enemy[0].rec.y = 0;
                        enemy[0].speed.x = 1;
                        enemy[0].speed.y = 1;
                        enemy[0].active = true;
                        enemy[0].color = DARKPURPLE;
                        enemy[0].boss = true;
                        enemy[0].life = 100;

                        for (int i = 1; i < activeEnemies; i++)
                        {
                            enemy[i].active= true; 
                            enemy[i].rec.width =50;
                            enemy[i].rec.height = 50;
                            enemy[i].rec.x = screenWidth/2;
                            enemy[i].rec.y = 200;
                            enemy[i].speed.x = 5;
                            enemy[i].speed.y = 5;
                            enemy[i].active = true;
                            enemy[i].color = GRAY;
                            enemy[i].boss = false;
                            enemy[i].life = 10;
                        }
                        for (int i = 0; i < activeEnemies; i++)
                        {
                            if (!enemy[i].active) enemy[i].active = true;
                        }  
                       
                        activeEnemies = FOURTH_WAVE;
                        wave = FOURTH;
                        smooth = false;
                        alpha = 0.0f;
                    }
                    ItemNumberWave=3;
                } break;
                case FOURTH: 
                {
                       if (!smooth)
                     {
                        alpha += 0.02f;
                        
                        if (alpha >= 1.0f) smooth = true;
                    }
                    if (smooth) alpha -= 0.02f;
                    
                    if (enemy[0].life == 0){
                        victory = true;
                        
                        if (score > highscore )
                        {
                            highscore = score;
                            SaveStorageValue(0,highscore);
                            gameOver = true;
                            
                        }

                    }
                    
                    
                }
                
                default: break;
            }

            // Player movement
            if (IsKeyDown(KEY_RIGHT)) player.rec.x += player.speed.x;
            if (IsKeyDown(KEY_LEFT)) player.rec.x -= player.speed.x;
            if (IsKeyDown(KEY_UP)) player.rec.y -= player.speed.y;
            if (IsKeyDown(KEY_DOWN)) player.rec.y += player.speed.y;

            // Player collision with enemy
            for (int i = 0; i < activeEnemies; i++)
            {
                if (CheckCollisionRecs(player.rec, enemy[i].rec))
                {
                    if (player.life == 1 || enemy[i].boss == true)  
                    {
                        gameOver = true;
                    } else 
                    {
                        enemy[i].rec.x = GetRandomValue(screenWidth, screenWidth + 1000);
                        enemy[i].rec.y = GetRandomValue(0, screenHeight - enemy[i].rec.height);
                        player.life--;
                        PlaySound(sound1);
 
                    }
                    // Perte du bonus
                    player.speed.x= 5; 
                    player.speed.y = 5; 
                     for (int i=0; i<NUM_SHOOTS;i++) 
                    {
                        shoot[i].rec.width = 10;
                        shoot[i].rec.height = 5;
                        shoot[i].color = MAROON;
                        shoot[i].bonus = false;
                    }
                }
            }
            // Player get item 
             for (int i = 0; i < ItemNumber; i++)
            {   if (item[i].active)
                {
                    if (CheckCollisionRecs(player.rec, item[i].rec))
                    {
                        PlaySound(sound2);
                        item[i].active = false; 
                        if ( i == ItemNumber -1)
                        {
                             item[0].active = true;
                        }else {
                            item[i+1].active = true;
                        }
                        item[i].rec.x = GetRandomValue(screenWidth, screenWidth + 1000);
                        item[i].rec.y = GetRandomValue(0, screenHeight - item[i].rec.height);
                       
                        switch (item[i].effect)
                        {
                            case Life:
                            player.life ++;
                            break;
                            
                            case Speed: 
                            if (player.speed.x < 10)
                            {
                                player.speed.x += 2;
                                player.speed.y += 2;
                            }
                            break;

                            case SizeShoot:
                            for (int i=0; i<NUM_SHOOTS;i++)
                            {
                                shoot[i].bonus = true;
                                shoot[i].color = YELLOW;
                            }
                            break;
                            default:
                            break;
                        }
                    }
                }
            }

             // Enemy behaviour
            for (int i = 0; i < activeEnemies; i++)
            {
                if (enemy[i].active)
                {
                    enemy[i].rec.x -= enemy[i].speed.x;

                    if (enemy[i].rec.x < 0)
                    {
                        if (!enemy[i].boss){
                        
                            enemy[i].rec.x = GetRandomValue(screenWidth, screenWidth + 1000);
                            enemy[i].rec.y = GetRandomValue(0, screenHeight - enemy[i].rec.height);
                        } 
                        
                    }
                }
            }
            
            // Wall behaviour
            if (player.rec.x <= 0) player.rec.x = 0;
            if (player.rec.x + player.rec.width >= screenWidth) player.rec.x = screenWidth - player.rec.width;
            if (player.rec.y <= 0) player.rec.y = 0;
            if (player.rec.y + player.rec.height >= screenHeight) player.rec.y = screenHeight - player.rec.height;

            // Shoot initialization
            if (IsKeyDown(KEY_SPACE))
            {
                shootRate += 5;

                for (int i = 0; i < NUM_SHOOTS; i++)
                {
                    if (!shoot[i].active && shootRate%20 == 0)
                    {
                        if (shoot[i].bonus)
                        {
                            shoot[i].rec.height =10 ;
                            shoot[i].rec.width = 20;
                        }
                            shoot[i].rec.x = player.rec.x;
                            shoot[i].rec.y = player.rec.y + player.rec.height/4;
                            shoot[i].active = true;
                        
                        break;
                    }
                }
            }

            // Shoot logic
            for (int i = 0; i < NUM_SHOOTS; i++)
            {
                if (shoot[i].active)
                {
                    
                    // Movement
                    shoot[i].rec.x += shoot[i].speed.x;

                    // Collision with enemy
                    for (int j = 0; j < activeEnemies; j++)
                    {
                        if (enemy[j].active)
                        {
                             if (CheckCollisionRecs(shoot[i].rec, enemy[j].rec)){
                                if (enemy[j].life == 0)
                                {
                                    shoot[i].active = false;
                                    enemy[j].rec.x = GetRandomValue(screenWidth, screenWidth + 1000);
                                    enemy[j].rec.y = GetRandomValue(0, screenHeight - enemy[j].rec.height);
                                    shootRate = 0;
                                    enemiesKill++;
                                    score += 100;
                                }else {
                                    
                                    shoot[i].active = false;
                                    shootRate = 0;
                                    enemy[j].life--;
                                    score +=100;
                                }
                            }
                            if (shoot[i].rec.x + shoot[i].rec.width >= screenWidth)
                            {
                                shoot[i].active = false;
                                shootRate = 0;
                            }
                        }
                    }
                }
            }
            
            //item behaviour
             for (int i = 0; i < ItemNumber; i++)
            {
                if (item[i].active)
                {
                    item[i].rec.x -= item[i].speed.x;

                    if (item[i].rec.x < 0)
                    {                        
                        item[i].rec.x = GetRandomValue(screenWidth, screenWidth + 1000);
                        item[i].rec.y = GetRandomValue(0, screenHeight - item[i].rec.height);
                    }
                }
            }
        }
    }
    else
    {
        if (IsKeyPressed(KEY_ENTER))
        {
            InitGame();
            gameOver = false;
        }
    }
}

// Draw game (one frame)
void DrawGame(void)
{
    BeginDrawing();

        ClearBackground(RAYWHITE);
        
        if (!gameOver)
        {
            DrawRectangleRec(player.rec, player.color);

            if (wave == FIRST) DrawText("FIRST WAVE", screenWidth/2 - MeasureText("FIRST WAVE", 40)/2, screenHeight/2 - 40, 40, Fade(BLACK, alpha));
            else if (wave == SECOND) DrawText("SECOND WAVE", screenWidth/2 - MeasureText("SECOND WAVE", 40)/2, screenHeight/2 - 40, 40, Fade(BLACK, alpha));
            else if (wave == THIRD) DrawText("THIRD WAVE", screenWidth/2 - MeasureText("THIRD WAVE", 40)/2, screenHeight/2 - 40, 40, Fade(BLACK, alpha));
            else if (wave == FOURTH)
            {
            DrawText("FOURTH WAVE:", screenWidth/2 - MeasureText("FOURTH WAVE", 40)/2, screenHeight/2 - 40, 40, Fade(BLACK, alpha));
            DrawText("BOSS IN COMMING", screenWidth/2 - MeasureText("BOSS IN COMMING", 40)/2, 2*screenHeight/3 - 40, 40, Fade(DARKPURPLE, alpha));
            }
            
            
            for (int i = 0; i < activeEnemies; i++)
            {
                if (enemy[i].active) DrawRectangleRec(enemy[i].rec, enemy[i].color);
            
            }
            for (int i = 0; i < NUM_SHOOTS; i++)
            {
                if (shoot[i].active) DrawRectangleRec(shoot[i].rec, shoot[i].color);
            }
            for (int i = 0; i < ItemNumber; i++)
            {
                if (item[i].active) {
                    DrawRectangleRec(item[i].rec, item[i].color);
                }
            
            }
            
            // Display of score and player life
            DrawText("Score:",20,10,30, GRAY);
            DrawText(TextFormat("%04i", score), 120, 10, 30, GRAY);
            DrawText("Life:",20,40,30, GRAY);
            DrawText(TextFormat("%i",player.life),100,40,30,RED);
        
            if (pause){
                DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/3 - 40, 40, GRAY);
                DrawText("SCORE", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, 2*screenHeight/3, 40, BLACK);
                DrawText(TextFormat("%04i", score), screenWidth/2+MeasureText("SCORE:", 40)/2, 2*screenHeight/3, 40, RED);
            }
        }else if (victory)
        {
                DrawText("YOU WIN", screenWidth/2 - MeasureText("YOU WIN", 40)/2, screenHeight/4 - 40, 40, BLACK);
                DrawText(TextFormat("SCORE:%04i",score), screenWidth/2 - MeasureText("SCORE:%04i", 40)/2, 2*screenHeight/4, 40, BLACK);
                DrawText(TextFormat("HIGHSCORE:%04i",highscore), screenWidth/2 - MeasureText("HIGHSCORE:%04i", 40)/2, 3*screenHeight/4, 40, RED);
        }
        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();
}
