#include "Menu.h"

// Menu item number controls.
#define START_GAME_KEY_ ALLEGRO_KEY_1
#define OPTIONS_KEY_ ALLEGRO_KEY_2
#define HIGH_SCORES_KEY_ ALLEGRO_KEY_3
#define EXIT_KEY_ ALLEGRO_KEY_4

// The colour of the text on the menus.
ALLEGRO_COLOR textColour_;

// High score variables.
// The number of high score entries.
#define SCORE_COUNT_ 5

// The maximum length that a high score name can be.
#define MAX_NAME_LENGTH_ 10

// The high score values in the high score list.
u16 highScores_[SCORE_COUNT_];

// The names for the high score values in the high score list.
u8 highScoreNames_[SCORE_COUNT_][11];

void runMenu(ALLEGRO_CONFIG* config, ALLEGRO_PATH* configPath)
{
    u8 menuRunning = TRUE;
    u8 menuItemChosen = FALSE;

    ALLEGRO_EVENT_QUEUE* menuEventQueue = NULL;
    ALLEGRO_EVENT allegroEvent;
    textColour_ = al_map_rgb(255, 255, 255);

    // Set the default high score entries.
    initHighScores();

    // Set the default keys for the player.
    setDefaultKeys(config);

    // Draw the menu on screen.
    drawMenu();

    setupMenuEventQueue(&menuEventQueue);

    while (menuRunning)
    {
        // Updates the content of the screen buffers.
        al_flip_display();

        al_wait_for_event(menuEventQueue, &allegroEvent);

        // Use the close button to exit the menu.
        if (allegroEvent.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
        {
            menuRunning = false;
        }
        else if (allegroEvent.type == ALLEGRO_EVENT_KEY_UP)
        {
            u16 key = allegroEvent.keyboard.keycode;

            switch (key)
            {
            case START_GAME_KEY_:
                // Runs the full game session and displays victory or defeat based on outcome (starting at level 0).
                runFullGame(&menuEventQueue, config);
                break;

            case OPTIONS_KEY_:
                displayOptions(&menuEventQueue, config, configPath);
                break;

            case HIGH_SCORES_KEY_:
                displayHighScores(&menuEventQueue);
                break;

            case EXIT_KEY_:
                menuRunning = FALSE;
                break;
            }

            // Redraw the menu after completing the action.
            drawMenu();
        }
    }

    // Cleanup
    al_destroy_event_queue(menuEventQueue);
}

void drawMenu()
{
    // Clear the screen before drawing the menu.
    al_clear_to_color(al_map_rgb(0, 0, 128));

    // Game title.
    drawString(138, 25, scaleFactor, "TINS 2024", textColour_);

    // Menu item choices.
    drawString(130, 55, scaleFactor, "1. START GAME", textColour_);
    drawString(130, 76, scaleFactor, "2. OPTIONS", textColour_);
    drawString(130, 97, scaleFactor, "3. HIGH SCORES", textColour_);
    drawString(130, 118, scaleFactor, "4. EXIT", textColour_);

    // Draw the game version number on the bottom right corner of the screen.
    drawString(240, 160, scaleFactor, STRFILEVER, textColour_);
}

void setupMenuEventQueue(ALLEGRO_EVENT_QUEUE** eventQueue)
{
    al_install_keyboard();

    const char logicSpeed = 60;
    ALLEGRO_TIMER* timer = al_create_timer(1.0 / logicSpeed);

    // Setup the event queue and the event sources.
    *eventQueue = al_create_event_queue();
    al_register_event_source(*eventQueue, al_get_keyboard_event_source());
    al_register_event_source(*eventQueue, al_get_timer_event_source(timer));
    al_register_event_source(*eventQueue, al_get_display_event_source(al_get_current_display()));

    // Start the timer that will ensure the logic is at a consistent speed.
    al_start_timer(timer);
}

void initHighScores()
{
    u8 i = 0;

    for (i = 0; i < SCORE_COUNT_; ++i)
    {
        highScores_[i] = 100;
        memcpy(highScoreNames_[i], "TINS\0", 6);
    }

    // Reads the values previously saved from file if it exists.
    loadHighScores("scores.bin");
}

void displayHighScores(ALLEGRO_EVENT_QUEUE** eventQueue)
{
    u8 i = 0;
    u8 scoreStart = 25;

    al_clear_to_color(al_map_rgb(0, 0, 128));

    // High scores title.
    drawString(138, 25, scaleFactor, "HIGH SCORES", textColour_);

    // Each of the entires in the high score list.
    for (i = 0; i < SCORE_COUNT_; ++i)
    {
        u8 y = 55 + i * 21;

        // Draw the score with the zeros (there is an extra 0 so that scores can be bigger than the are actually stored).
        drawScoreAllegro(highScores_[i], 132, y, scaleFactor, textColour_);

        // Draw the name of the player from the names array.
        drawString(172, y, scaleFactor, highScoreNames_[i], textColour_);
    }

    al_flip_display();

    getKeyAllegro(eventQueue);
}

u16 getKeyAllegro(ALLEGRO_EVENT_QUEUE** menuEventQueue)
{
    ALLEGRO_EVENT allegroEvent;
    u16 keycode = ALLEGRO_KEY_MAX;

    // Flush the event queue to prevent previous keyboard events being caught.
    al_flush_event_queue(*menuEventQueue);

    while (keycode == ALLEGRO_KEY_MAX)
    {
        al_wait_for_event(*menuEventQueue, &allegroEvent);

        if (allegroEvent.type == ALLEGRO_EVENT_KEY_DOWN)
        {
            keycode = allegroEvent.keyboard.keycode;
        }
        else if (allegroEvent.type == ALLEGRO_EVENT_KEY_UP)
        {
            if (keycode != ALLEGRO_KEY_MAX)
            {
                return keycode;
            }
        }
    }

    return keycode;
}


void loadHighScores(const u8* filename)
{
    FILE* scoreFile;
    u8 i = 0;

    // fopen_s is not available in VC6.
#if _MSC_VER > 1200
    fopen_s(&scoreFile, filename, "rb");
#else
    scoreFile = fopen(filename, "rb");
#endif

    // Make sure file is valid.
    if (scoreFile)
    {
        // Read the high score names from file.
        for (i = 0; i < SCORE_COUNT_; ++i)
        {
            fread(highScoreNames_[i], sizeof(u8), 11, scoreFile);
        }

        // Read the high score values from file.
        fread(highScores_, sizeof(u16), 5, scoreFile);

        fclose(scoreFile);
    }
}

void displayOptions(ALLEGRO_EVENT_QUEUE** eventQueue, ALLEGRO_CONFIG* config, ALLEGRO_PATH* configPath)
{
    al_clear_to_color(al_map_rgb(0, 0, 128));

    // Options title.
    drawString(138, 25, scaleFactor, "OPTIONS", textColour_);

    al_flip_display();

    getKeyAllegro(eventQueue);

    // Save the user chosen controls to the config file.
    saveControls(config, configPath);
}

void runFullGame(ALLEGRO_EVENT_QUEUE** eventQueue, ALLEGRO_CONFIG* config)
{
    // Runs the full game session.
    runGameSession(config);

    // Displays either game over or victory screen depending on if the player succeeded or not.
    displayEndScreen(eventQueue);

    // Update high scores.
    updateHighScores(eventQueue);

    // Display high scores after entering high score.
    displayHighScores(eventQueue);
}

void displayEndScreen(ALLEGRO_EVENT_QUEUE** eventQueue)
{
    // Clear the screen.
    al_clear_to_color(al_map_rgb(0, 0, 128));

    if (levelState == LEVEL_GAME_COMPLETE)
    {
        // Draw victorious text on screen.
        drawString(130, 74, scaleFactor, "VICTORIOUS", textColour_);
    }
    else
    {
        // Draw Game Over.
        drawString(128, 74, scaleFactor, "GAME OVER", textColour_);
    }

    // Need to refresh the allegro screen.
    al_flip_display();

    // Flush the event queue to prevent previous keyboard events being caught.
    al_flush_event_queue(*eventQueue);

    // Don't care what the key is just force the player to press a key before exiting the game over screen.
    getKeyAllegro(eventQueue);
}

void updateHighScores(ALLEGRO_EVENT_QUEUE** eventQueue)
{
    // Check that the player got a higher score than the bottom score.
    // TODO figure out multiple players
    if (players[0].score > highScores_[4])
    {
        // Let the player enter their name.
        static u8 scoreName[11];
        u8 i = 0;

        // Set scoreName to the null terminator to end the string.
        memset(scoreName, '\0', 11);

        // Clear the screen
        al_clear_to_color(al_map_rgb(0, 0, 128));

        // Draw the score with padding of zeroes.
        drawString(118, 40, scaleFactor, "NEW HIGH SCORE", textColour_);
        drawScoreAllegro(players[0].score, 125, 60, scaleFactor, textColour_);
        drawString(115, 80, scaleFactor, "ENTER YOUR NAME", textColour_);

        // Enter the name of the user for the high scores.
        al_flip_display();
        enterWordAllegro(scoreName, MAX_NAME_LENGTH_, 130, 100, eventQueue);

        // Update score values.
        highScores_[4] = players[0].score;
        memcpy(highScoreNames_[4], scoreName, 11);

        for (i = 4; i > 0; --i)
        {
            if (highScores_[i] > highScores_[i - 1])
            {
                highScores_[i] = highScores_[i - 1];
                memcpy(highScoreNames_[i], highScoreNames_[i - 1], 11);

                highScores_[i - 1] = players[0].score;
                memcpy(highScoreNames_[i - 1], scoreName, 11);
            }
        }
    }

    // Write the high scores to file.
    saveHighScores("scores.bin");
}

void saveHighScores(const u8* filename)
{
    FILE* scoreFile;
    u8 i = 0;

    // fopen_s is not available in VC6.
#if _MSC_VER > 1200
    fopen_s(&scoreFile, filename, "wb");
#else
    scoreFile = fopen(filename, "wb");
#endif

    // Write the high score names to file.
    for (i = 0; i < SCORE_COUNT_; ++i)
    {
        fwrite(highScoreNames_[i], sizeof(u8), 11, scoreFile);
    }

    // Write the high score values to the file.
    fwrite(highScores_, sizeof(i32), 5, scoreFile);

    fclose(scoreFile);
}

void enterWordAllegro(u8* word, u8 maxLength, u16 x, u16 y, ALLEGRO_EVENT_QUEUE** eventQueue)
{
    // Default the chosen key to whatever.
    u16 chosenKey = ALLEGRO_KEY_0;
    u8 letterCount = 0;
    u8 letter = 0;
    u8 wordChange = FALSE;

    do
    {
        // Get the next character from the user.
        chosenKey = getKeyAllegro(eventQueue);

        // Del to move back to the previous key.
        if (chosenKey == ALLEGRO_KEY_DELETE || chosenKey == ALLEGRO_KEY_BACKSPACE)
        {
            if (letterCount)
            {
                // Remove the letter from the word array.
                --letterCount;
                word[letterCount] = '\0';

                // Indicate a word change to draw it on screen.
                wordChange = TRUE;
            }
        }

        // Make sure the key is in the correct range and the word is not too long.
        else if (letterCount < maxLength)
        {
            // Convert the chosen key (cpct_keyID to a character to an ASCII character).
            switch (chosenKey)
            {
            case ALLEGRO_KEY_A:
                letter = 'A';
                break;

            case ALLEGRO_KEY_B:
                letter = 'B';
                break;

            case ALLEGRO_KEY_C:
                letter = 'C';
                break;

            case ALLEGRO_KEY_D:
                letter = 'D';
                break;

            case ALLEGRO_KEY_E:
                letter = 'E';
                break;

            case ALLEGRO_KEY_F:
                letter = 'F';
                break;

            case ALLEGRO_KEY_G:
                letter = 'G';
                break;

            case ALLEGRO_KEY_H:
                letter = 'H';
                break;

            case ALLEGRO_KEY_I:
                letter = 'I';
                break;

            case ALLEGRO_KEY_J:
                letter = 'J';
                break;

            case ALLEGRO_KEY_K:
                letter = 'K';
                break;

            case ALLEGRO_KEY_L:
                letter = 'L';
                break;

            case ALLEGRO_KEY_M:
                letter = 'M';
                break;

            case ALLEGRO_KEY_N:
                letter = 'N';
                break;

            case ALLEGRO_KEY_O:
                letter = 'O';
                break;

            case ALLEGRO_KEY_P:
                letter = 'P';
                break;

            case ALLEGRO_KEY_Q:
                letter = 'Q';
                break;

            case ALLEGRO_KEY_R:
                letter = 'R';
                break;

            case ALLEGRO_KEY_S:
                letter = 'S';
                break;

            case ALLEGRO_KEY_T:
                letter = 'T';
                break;

            case ALLEGRO_KEY_U:
                letter = 'U';
                break;

            case ALLEGRO_KEY_V:
                letter = 'V';
                break;

            case ALLEGRO_KEY_W:
                letter = 'W';
                break;

            case ALLEGRO_KEY_X:
                letter = 'X';
                break;

            case ALLEGRO_KEY_Y:
                letter = 'Y';
                break;

            case ALLEGRO_KEY_Z:
                letter = 'Z';
                break;

            case ALLEGRO_KEY_SPACE:
                letter = ' ';
                break;
            }

            if (letter)
            {
                // Add the chosen letter to the string that makes up the word that the user is entering.
                word[letterCount] = letter;

                // Indicate a word change to draw it on screen.
                wordChange = TRUE;

                // Indicate a new letter has been added to the word array.
                ++letterCount;

                // Make sure the chosen character is reset so the previous character
                // does not get drawn if the user chooses an invalid character.
                letter = 0;
            }
        }

        // Draw the whole word after a letter has been added or removed from the word.
        if (wordChange)
        {
            al_draw_filled_rectangle(x * scaleFactor, y * scaleFactor, (x + maxLength * 10) * scaleFactor, (y + 15) * scaleFactor, al_map_rgb(0, 0, 128));
            drawString(x, y, scaleFactor, word, textColour_);
            al_flip_display();
        }
    }
    while (chosenKey != ALLEGRO_KEY_ENTER && chosenKey != ALLEGRO_KEY_PAD_ENTER);
}