#include "Menu.h"

#ifdef CPC

// Menu item number controls.
#define START_GAME_KEY_ Key_1
#define REBIND_CONTROLS_KEY_ Key_2
#define HIGH_SCORES_KEY_ Key_3

#else

// Menu item number controls.
#define START_GAME_KEY_ ALLEGRO_KEY_1
#define REBIND_CONTROLS_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_;
#endif

// 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];

#ifdef ALLEGRO5
void runMenu(ALLEGRO_CONFIG* config, ALLEGRO_PATH* configPath)
#else
void runMenu()
#endif
{
    u8 menuRunning = TRUE;

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

#elif ALLEGRO4

    u8 menuItemChosen = FALSE;
    textColour_ = makecol(255, 255, 255);
#endif

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

    // Set the default keys for the player.
#ifdef ALLEGRO5
    setDefaultKeys(config);
#else
    setDefaultKeys();
#endif

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

#ifdef ALLEGRO5
    setupMenuEventQueue(&menuEventQueue);
#endif

    while (menuRunning)
    {
#ifdef ALLEGRO4
        if (key[START_GAME_KEY_])
        {
            // Runs the full game session and displays victory or defeat based on outcome (starting at level 0).
            runFullGame();
            menuItemChosen = TRUE;
        }
        else if (key[REBIND_CONTROLS_KEY_])
        {
            // Display rebind controls menu which asks the user for their chosen controls.
            rebindControls();
            saveControls();

            menuItemChosen = TRUE;
        }
        else if (key[HIGH_SCORES_KEY_])
        {
            displayHighScores();
            menuItemChosen = TRUE;
        }
        else if (key[EXIT_KEY_])
        {
            menuRunning = FALSE;
        }

        if (menuItemChosen)
        {
            // Redraw the menu after completing the action.
            drawMenu();
            menuItemChosen = FALSE;
        }

#elif ALLEGRO5
        // 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);
                break;

            case REBIND_CONTROLS_KEY_:
                // Display rebind controls menu which asks the user for their chosen controls.
                rebindControls(&menuEventQueue);

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

            case HIGH_SCORES_KEY_:
                displayHighScores(&menuEventQueue);
                break;

            case EXIT_KEY_:
                menuRunning = FALSE;
                break;
            }

            // Redraw the menu after completing the action.
            drawMenu();
        }
#else
        if (cpct_isKeyPressed(START_GAME_KEY_))
        {
            // Runs the full game session and displays victory or defeat based on outcome (starting at level 0).
            runFullGame();

            // Redraw the menu after finishing the game session.
            drawMenu();
        }
        else if (cpct_isKeyPressed(REBIND_CONTROLS_KEY_))
        {
            // Display rebind controls menu which asks the user for their chosen controls.
            rebindControls();

            // Redraw the menu after leaving rebind controls menu.
            drawMenu();
        }
        else if (cpct_isKeyPressed(HIGH_SCORES_KEY_))
        {
            displayHighScores();

            // Redraw the menu after leaving the high scores.
            drawMenu();
        }
#endif
    }

#ifdef ALLEGRO5
    // Cleanup
    al_destroy_event_queue(menuEventQueue);
#endif
}

void drawMenu()
{
    // Sets the palette to the initial value.
    initPalette();

#ifdef CPC

    // Sets background to blue.
    cpct_clearScreen(0);

#elif ALLEGRO4
    acquire_screen();

    // Clear the screen before drawing the menu.
    clear_to_color(screen, makecol(0, 128, 0));
#else
    // Clear the screen before drawing the menu.
    al_clear_to_color(al_map_rgb(0, 0, 128));
#endif

#ifdef CPC
    // Game title.
    drawString(20, 30, "krampushack 2024");

    // Menu item choices.
    drawString(20, 45, "1. start game");
    drawString(20, 60, "2. rebind controls");
    drawString(20, 75, "3. high scores");

    // Draw the game version number on the bottom right corner of the screen.
    drawString(70, 180, GAME_VERSION);

    // Draw the build number on the bottom right corner of the screen.
    drawString(70, 190, BUILD_NUMBER);
#else
    // Game title.
    drawString(80, 30, scaleFactor, "KRAMPUSHACK 2024", textColour_);

    // Menu item choices.
    drawString(80, 60, scaleFactor, "1. START GAME", textColour_);
    drawString(80, 75, scaleFactor, "2. REBIND CONTROLS", textColour_);
    drawString(80, 90, scaleFactor, "3. HIGH SCORES", textColour_);
    drawString(80, 105, scaleFactor, "4. EXIT", textColour_);

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

    // Draw the build number on the bottom right corner of the screen.
    drawString(190, 170, scaleFactor, BUILD_NUMBER, textColour_);
#endif

#ifdef ALLEGRO4
    release_screen();
#endif
}

#ifdef ALLEGRO5
void setupMenuEventQueue(ALLEGRO_EVENT_QUEUE** eventQueue)
{
    al_install_keyboard();

    const u8 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);
}
#endif

#ifdef ALLEGRO4
void rebindControls()
{
    // Clear the screen so that we can show control text.
    clear_to_color(screen, makecol(0, 64, 0));

    // Enter controls title.
    drawString(104, 30, scaleFactor, "ENTER CONTROLS", textColour_);

    // Draw the text indicating which control is being set.
    // Make sure no key is currently down.
    // Set the key for the next control (this is done for each of control below).
    gameControls.left = choseNextControl(130, 50, "LEFT", textColour_);
    gameControls.right = choseNextControl(130, 65, "RIGHT", textColour_);
    gameControls.up = choseNextControl(130, 80, "UP", textColour_);
    gameControls.down = choseNextControl(130, 95, "DOWN", textColour_);
    gameControls.action = choseNextControl(130, 110, "ACTION", textColour_);
    gameControls.reset = choseNextControl(130, 125, "RESET", textColour_);
    gameControls.exit = choseNextControl(130, 140, "EXIT", textColour_);
}

int choseNextControl(u16 x, u16 y, u8* string, ALLEGRO_COLOR textColour)
{
    acquire_screen();
    drawString(x, y, scaleFactor, string, textColour);
    release_screen();

    return readKeyScanCode();
}

u8 readKeyScanCode()
{
    i16 readKeyDown = 0;
    u8 scanCode = 0;

    clear_keybuf();

    // Read the current key.
    readKeyDown = readkey();

    // Convert the key to a scan code.
    scanCode = readKeyDown >> 8;

    return scanCode;
}

#elif ALLEGRO5

void rebindControls(ALLEGRO_EVENT_QUEUE** eventQueue)
{
    // Clear the screen so that we can show control text.
    al_clear_to_color(al_map_rgb(0, 0, 128));
    al_flip_display();

    // Enter controls title.
    drawString(104, 30, scaleFactor, "ENTER CONTROLS", textColour_);

    // Draw the text indicating which control is being set.
    // Make sure no key is currently down.
    // Set the key for the next control (this is done for each of control below).
    gameControls.left = choseNextControl(130, 50, "LEFT", textColour_, eventQueue);
    gameControls.right = choseNextControl(130, 65, "RIGHT", textColour_, eventQueue);
    gameControls.up = choseNextControl(130, 80, "UP", textColour_, eventQueue);
    gameControls.down = choseNextControl(130, 95, "DOWN", textColour_, eventQueue);
    gameControls.action = choseNextControl(130, 110, "ACTION", textColour_, eventQueue);
    gameControls.reset = choseNextControl(130, 125, "RESET", textColour_, eventQueue);
    gameControls.exit = choseNextControl(130, 140, "EXIT", textColour_, eventQueue);
}

int choseNextControl(u16 x, u16 y, u8* string, ALLEGRO_COLOR textColour, ALLEGRO_EVENT_QUEUE** eventQueue)
{
    drawString(x, y, scaleFactor, string, textColour);
    al_flip_display();

    return 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;
}

#elif CPC

void rebindControls()
{
    // Clear the screen so that we can show control text.
    cpct_clearScreen(0);

    // Enter controls title.
    drawString(15, 8, "enter controls");

    // Draw the text indicating which control is being set.
    // Make sure no key is currently down.
    // Set the key for the next control (this is done for each of control below).
    drawString(25, 30, "left");
    gameControls.left = getKey();

    drawString(25, 45, "right");
    gameControls.right = getKey();

    drawString(25, 60, "up");
    gameControls.up = getKey();

    drawString(25, 75, "down");
    gameControls.down = getKey();

    drawString(25, 90, "action");
    gameControls.action = getKey();

    drawString(25, 105, "reset");
    gameControls.reset = getKey();

    drawString(25, 120, "exit");
    gameControls.exit = getKey();
}

cpct_keyID getKey()
{
    // From Ronaldo and Arnaud on the cpcwiki forum.
    u8* keyStatus = cpct_keyboardStatusBuffer;
    u8 i;

    // Wait for any previous key presses to clear first.
    while (cpct_isAnyKeyPressed());

    // Then wait for the desired key press.
    while (!cpct_isAnyKeyPressed());

    for (i = 0; i < 10; ++i)
    {
        cpct_keyID keypressed = *keyStatus++ ^ 0xFF;

        if (keypressed)
        {
            return (keypressed << 8) + i;
        }
    }

    return 0;
}
#endif

#ifdef ALLEGRO5
void runFullGame(ALLEGRO_EVENT_QUEUE** eventQueue)
#else
void runFullGame()
#endif
{
    // Runs the full game session.
    runGameSession();

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

    // Update high scores.
#ifdef ALLEGRO5
    updateHighScores(eventQueue);
#else
    updateHighScores();
#endif

#ifndef CPC
    // Write the high scores to file.
    saveHighScores("scores1.bin");
#endif

    // Display high scores after entering high score.
#ifdef ALLEGRO5
    displayHighScores(eventQueue);
#else
    displayHighScores();
#endif
}

#ifdef ALLEGRO5
void displayEndScreen(ALLEGRO_EVENT_QUEUE** eventQueue)
#else
void displayEndScreen()
#endif
{
#ifdef CPC

    // Wait for any previous key presses to clear first.
    while (cpct_isAnyKeyPressed());

    // Clear the screen.
    cpct_clearScreen(0);

#elif ALLEGRO4

    acquire_screen();

    // Clear the screen.
    clear_to_color(screen, makecol(0, 54, 128));

#else

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

#endif

    if (levelState == LEVEL_GAME_COMPLETE)
    {
        // Draw victorious text on screen.
#ifdef CPC
        drawString(108, 44, "victorious");
#else
        drawString(108, 44, scaleFactor, "VICTORIOUS", textColour_);
#endif
    }
    else
    {
        // Draw Game Over.
#ifdef CPC
        drawString(108, 44, "game over");
#else
        drawString(108, 44, scaleFactor, "GAME OVER", textColour_);
#endif
    }

#ifdef CPC

    // Wait for a key press before going back to the main menu.
    while (!cpct_isAnyKeyPressed());

#elif ALLEGRO4

    release_screen();

    // Clear the key buffer to ignore the keyboard inputs when the player ended the game.
    clear_keybuf();

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

#else

    // 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);

#endif
}

#ifdef ALLEGRO5
void displayHighScores(ALLEGRO_EVENT_QUEUE** eventQueue)
#else
void displayHighScores()
#endif
{
    u8 i = 0;

#ifdef CPC

    cpct_clearScreen(0);

#elif ALLEGRO4

    acquire_screen();
    clear_to_color(screen, makecol(0, 54, 128));

#elif ALLEGRO5

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

#endif

#ifdef CPC

    // High scores title.
    drawString(25, 5, "high scores");

#else

    // High scores title.
    drawString(104, 30, scaleFactor, "HIGH SCORES", textColour_);

#endif

    // Each of the entires in the high score list.
    for (i = 0; i < SCORE_COUNT_; ++i)
    {
        // Draw the score with the zeros (there is an extra 0 so that scores can be bigger than the are actually stored).
#ifdef CPC
        u8 y = 45 + i * 15;
        drawScore(highScores_[i], 25, y);
#else
        u8 y = 65 + i * 15;
        drawScore(highScores_[i], 90, y, scaleFactor, textColour_);
#endif

        // Draw the name of the player from the names array.
#ifdef CPC
        drawString(40, y, highScoreNames_[i]);
#else
        drawString(160, y, scaleFactor, highScoreNames_[i], textColour_);
#endif
    }

#ifdef ALLEGRO4

    release_screen();

#elif ALLEGRO5

    al_flip_display();

#endif

#ifdef CPC

    getKey();

#elif ALLEGRO4

    readkey();
    clear_keybuf();

#elif ALLEGRO5

    getKeyAllegro(eventQueue);

#endif
}

void initHighScores()
{
    u8 i = 0;

    for (i = 0; i < SCORE_COUNT_; ++i)
    {
        highScores_[i] = 10;

#ifdef CPC
        cpct_memcpy(highScoreNames_[i], "cpc\0", 4);
#else
        memcpy(highScoreNames_[i], "KRAMPUS\0", 9);
#endif
    }

#ifndef CPC
    // Reads the values previously saved from file if it exists.
    loadHighScores("scores1.bin");
#endif
}

#ifdef ALLEGRO5
void updateHighScores(ALLEGRO_EVENT_QUEUE** eventQueue)
#else
void updateHighScores()
#endif
{
    // Check that the player got a higher score than the bottom score.
    if (gameScore > 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.
#ifdef CPC
        cpct_memset(scoreName, '\0', 11);
#else
        memset(scoreName, '\0', 11);
#endif

#ifdef CPC
        // Clear the screen
        cpct_clearScreen(0);
#elif ALLEGRO4
        // Clear the screen
        clear_to_color(screen, makecol(0, 0, 128));
#else
        // Clear the screen
        al_clear_to_color(al_map_rgb(0, 0, 128));
#endif

#ifdef CPC
        // Draw the score with padding of zeroes.
        drawString(15, 5, "new high score");
        drawScore(gameScore, 45, 5);
        drawString(20, 25, "enter your name");
#else
        // Draw the score with padding of zeroes.
        drawString(68, 40, scaleFactor, "NEW HIGH SCORE", textColour_);
        drawScore(gameScore, 90, 60, scaleFactor, textColour_);
        drawString(65, 80, scaleFactor, "ENTER YOUR NAME", textColour_);
#endif

        // Enter the name of the user for the high scores.
#ifdef CPC
        enterWord(scoreName, MAX_NAME_LENGTH_);
#elif ALLEGRO4
        enterWordAllegro(scoreName, MAX_NAME_LENGTH_, 90, 100);
#else
        al_flip_display();
        enterWordAllegro(scoreName, MAX_NAME_LENGTH_, 90, 100, eventQueue);
#endif

        // Update score values.
        highScores_[4] = gameScore;
#ifdef CPC
        cpct_memcpy(highScoreNames_[4], scoreName, 11);
#else
        memcpy(highScoreNames_[4], scoreName, 11);
#endif

        for (i = 4; i > 0; --i)
        {
            if (highScores_[i] > highScores_[i - 1])
            {
                highScores_[i] = highScores_[i - 1];
#ifdef CPC
                cpct_memcpy(highScoreNames_[i], highScoreNames_[i - 1], 11);
#else
                memcpy(highScoreNames_[i], highScoreNames_[i - 1], 11);
#endif

                highScores_[i - 1] = gameScore;
#ifdef CPC
                cpct_memcpy(highScoreNames_[i - 1], scoreName, 11);
#else
                memcpy(highScoreNames_[i - 1], scoreName, 11);
#endif
            }
        }
    }
}

#ifdef CPC
void enterWord(u8* word, u8 maxLength)
{
    // Default the chosen key to whatever.
    cpct_keyID chosenKey = Key_0;
    u8 letterCount = 0;
    u8 letter = 0;

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

        // Del to move back to the previous key.
        if (chosenKey == Key_Del)
        {
            // Go back to the previous letter.
            if (letterCount)
            {
                --letterCount;
                --word;

                // Draw letter showing a space where it used to be a letter.
                drawString(25 + letterCount * 2, 50, " ");
            }
        }

        // 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 Key_A:
                letter = 'a';
                break;

            case Key_B:
                letter = 'b';
                break;

            case Key_C:
                letter = 'c';
                break;

            case Key_D:
                letter = 'd';
                break;

            case Key_E:
                letter = 'e';
                break;

            case Key_F:
                letter = 'f';
                break;

            case Key_G:
                letter = 'g';
                break;

            case Key_H:
                letter = 'h';
                break;

            case Key_I:
                letter = 'i';
                break;

            case Key_J:
                letter = 'j';
                break;

            case Key_K:
                letter = 'k';
                break;

            case Key_L:
                letter = 'l';
                break;

            case Key_M:
                letter = 'm';
                break;

            case Key_N:
                letter = 'n';
                break;

            case Key_O:
                letter = 'o';
                break;

            case Key_P:
                letter = 'p';
                break;

            case Key_Q:
                letter = 'q';
                break;

            case Key_R:
                letter = 'r';
                break;

            case Key_S:
                letter = 's';
                break;

            case Key_T:
                letter = 't';
                break;

            case Key_U:
                letter = 'u';
                break;

            case Key_V:
                letter = 'v';
                break;

            case Key_W:
                letter = 'w';
                break;

            case Key_X:
                letter = 'x';
                break;

            case Key_Y:
                letter = 'y';
                break;

            case Key_Z:
                letter = 'z';
                break;

            case Key_Space:
                letter = ' ';
                break;
            }

            if (letter)
            {
                // Array that is used for drawing the current letter on screen.
                static u8 chosenCharacters[2];
                chosenCharacters[1] = '\0';

                // Add the chosen letter to the string that makes up the word that the user is entering.
                *word = letter;

                // Save the letter to chosen characters for drawing.
                chosenCharacters[0] = letter;

                // Draw letter, the one just selected.
                drawString(25 + letterCount * 2, 50, chosenCharacters);

                ++word;
                ++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;
            }
        }
    }
    while (chosenKey != Key_Return && chosenKey != Key_Enter);
}
#else
#ifdef ALLEGRO4
void enterWordAllegro(u8* word, u8 maxLength, u16 x, u16 y)
#else
void enterWordAllegro(u8* word, u8 maxLength, u16 x, u16 y, ALLEGRO_EVENT_QUEUE** eventQueue)
#endif
{
    // 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.
#ifdef ALLEGRO4
        chosenKey = readKeyScanCode();
#else
        chosenKey = getKeyAllegro(eventQueue);
#endif

        // 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)
        {
#ifdef ALLEGRO4
            rectfill(screen, x * scaleFactor, y * scaleFactor, (x + maxLength * 10) * scaleFactor, (y + 15) * scaleFactor, makecol(0, 0, 128));
            drawString(x, y, scaleFactor, word, textColour_);
#else
            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();
#endif
        }
    }
    while (chosenKey != ALLEGRO_KEY_ENTER && chosenKey != ALLEGRO_KEY_PAD_ENTER);
}
#endif

#ifndef CPC
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(u16), 5, scoreFile);

    fclose(scoreFile);
}

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(i32), 5, scoreFile);

        fclose(scoreFile);
    }
}
#endif
