#include "Game.h"

void runGameSession()
{
    gameRunning = TRUE;

    cpct_clearScreen(0);

    // Update the palette for either colour or green.
    updatePalette();

    // Reset game state values for a new game.
    resetGameSessionState();

    // Initialises arkos 2 sounds.
    initSound();

    // Starts playing the game music.
    playMusic(0);

    while (!levelState)
    {
        // Resets the level score to the current game score (0 at the start of the game session)
        levelScore = gameScore;

        initMap();

        // Initialise the player values.
        initPlayer(level_start_position[currentMap][0], level_start_position[currentMap][1]);

        // Clear inventory on game session start.
        inventoryItems = 0;

        // Draw the hud on the bottom of the screen.
        hudRedraw = TRUE;

        // Set border to default dark blue.
        cpct_setBorder(4);

        // Reset the water timer before starting the level (including after death).
        waterTimerCount = 0;
        runWaterLogic = FALSE;

        runGame();

        if (levelState == LEVEL_DEATH)
        {
            // Player has died reload the current map.
            levelState = LEVEL_READY;
            gameRunning = TRUE;
        }
        else if (levelState == LEVEL_COMPLETE)
        {
            // If currentMap is the last level then game complete
            levelState = LEVEL_GAME_COMPLETE;

            // Add a 100 (shown as 1000) bonus for completing a level without losing health.
            if (player.health >= playerStartingHealth)
            {
                levelScore += 100;
            }

            // Update the game score to the level score after finishing the level.
            gameScore = levelScore;

            // Update current map to prepare to move to the next level.
            ++currentMap;

            if (currentMap == LEVEL_COUNT)
            {
                // If currentMap is the last level then game complete
                levelState = LEVEL_GAME_COMPLETE;
            }
            else
            {
                // If currentMap is not the last level then move to the next level.
                levelState = LEVEL_READY;
                gameRunning = TRUE;
            }
        }
    }

    // Stop the sound at the end of the level.
    // TODO move further along if it is appropriate.
    stopSound();
}

void runGame()
{
    u8 logicFlag = 0;

    while (gameRunning)
    {
        // Read input from the player.
        readInput();

        // Only run logic every second time.
        if (logicFlag++)
        {
            // Run the game logic which will change what is displayed.
            gameLogic();

            // Reset flag
            logicFlag = 0;
        }

        // Wait for vsync.
        cpct_waitVSYNC();

        // Draws the game on the screen.
        drawGame();
    }
}

void readInput()
{
    // Reads the game controls from the user.
    readGameControls();

    // Check that the exit key has been pressed.
    if (gameControls.controlFlags & CONTROL_EXIT_FLAG)
    {
        // If the player choses to press yes exit the game.
        if (exitDialog())
        {
            gameRunning = FALSE;
            levelState = LEVEL_EXIT;
        }
        else
        {
            // Force a map redraw to cleanup after the dialog is closed.
            mapRedraw = TRUE;
            playerRedraw = TRUE;
        }
    }
}

void drawGame()
{
    // Draws the map grid on the screen.
    drawMap();

    // Draws the enemies on the screen.
    drawEnemies();

    // Draw the player on the screen at their current position.
    drawPlayer();

    // Draw any enemies that have collided with each other preventing them from disappearing.
    drawColidedEnemies();

    // Draw the hud at the bottom of the screen.
    drawHud();
}

void gameLogic()
{
    // Move the player's character.
    movePlayer();

    // Run the logic on the player character.
    logicPlayer();

    // Run the logic for the enemies in the level.
    logicEnemies(player.object.position.x, player.object.position.y);

    // Run the logic for chasing gesture water if it is in the level.
    logicWater();
}

void logicWater()
{
    if (runWaterLogic)
    {
        u8 i = 0;
        u8 waterY = 0;
        u8 waterX = 0;
        u8* waterRegions = level_water_regions[currentMap];
        u8 updatedRow = FALSE;

        for (i = 0; i < level_water_array_size[currentMap]; i += 5)
        {
            u8 y = waterRegions[i + 1];
            u8 waterType = waterRegions[i + 4];

            if (waterType == WATER_TYPE_CHASING)
            {
                // X is waterRegions[i].
                u8 roomX = waterRegions[i] / SCREEN_TILES_X;
                u8 roomY = y / SCREEN_TILES_Y;

                if (roomX == currentRoomX && roomY == currentRoomY)
                {
                    // Height is waterRegion[i + 3].
                    // The -1 prevents it overflowing off the top of the screen.
                    for (waterY = y + waterRegions[i + 3] - 1; waterY >= y; --waterY)
                    {
                        for (waterX = waterRegions[i]; waterX < waterRegions[i] + waterRegions[i + 2]; ++waterX)
                        {
                            u8 cell = mapCell(waterX % SCREEN_TILES_X, waterY % SCREEN_TILES_Y, roomX, roomY);

                            if (cell & CELL_WATER_FLAG)
                            {
                                // This row has already got water in it move up to the next row.
                                break;
                            }
                            else if (cell && cell < CELL_ITEM_FLAG)
                            {
                                // This cell is not empty but does not have an item or interactable so must be a tile.
                                // When it is a tile do nothing and move to the next cell in the row.
                            }
                            else
                            {
                                // This cell should have water as it is not a tile and does not have water currently.
                                // Add the water flag to the cell and move onto the next cell in the row.
                                updateMapCell(waterX % SCREEN_TILES_X, waterY % SCREEN_TILES_Y, roomX, roomY, cell | CELL_WATER_FLAG);

                                // Redraw the cell with water in it.
                                drawCellAtPosition(waterX % SCREEN_TILES_X, waterY % SCREEN_TILES_Y);

                                updatedRow = TRUE;
                            }
                        }

                        // If a row has been updated we don't have to keep going as that can happen the next iteration.
                        if (updatedRow)
                        {
                            break;
                        }
                    }
                }
            }
        }

        // Redraw player and enemies (gesture map doesn't have enemies here but a different map could) on adding water to the row.
        if (updatedRow)
        {
            markEnemiesForRedraw();
            playerRedraw = TRUE;
        }

        runWaterLogic = FALSE;
        waterTimerCount = 0;
    }
}

void debugLevelSelector()
{
    // Should be one less than LEVEL_COUNT.
    drawString(5, 5, "dbg lvl 0 to 8");

    // Make sure that any keypresses have cleared.
    while (cpct_isAnyKeyPressed());
    while (!cpct_isAnyKeyPressed());

    while (1)
    {
        if (cpct_isKeyPressed(Key_0))
        {
            currentMap = 0;
            break;
        }
        else if (cpct_isKeyPressed(Key_1))
        {
            currentMap = 1;
            break;
        }
        else if (cpct_isKeyPressed(Key_2))
        {
            currentMap = 2;
            break;
        }
        else if (cpct_isKeyPressed(Key_3))
        {
            currentMap = 3;
            break;
        }
        else if (cpct_isKeyPressed(Key_4))
        {
            currentMap = 4;
            break;
        }
        else if (cpct_isKeyPressed(Key_5))
        {
            currentMap = 5;
            break;
        }
        else if (cpct_isKeyPressed(Key_6))
        {
            currentMap = 6;
            break;
        }
        else if (cpct_isKeyPressed(Key_7))
        {
            currentMap = 7;
            break;
        }
        else if (cpct_isKeyPressed(Key_8))
        {
            currentMap = 8;
            break;
        }
    }
}

void generatePasscode(u8 mapNumber, u8* code)
{
    const u8 offsets[PASS_CODE_LENGTH] = { 12, 7, 23, 1, 0 };
    u8 i = 0;
    u8 letter = mapNumber * 4;

    cpct_memset(code, 0, 6);

    for (i = 0; i < PASS_CODE_LENGTH; ++i)
    {
        while (letter >= 26)
        {
            letter -= 26;
        }

        code[i] = letter + 'a';
        letter += offsets[i];
    }
}