#include "Interactable.h"

u8 isInteractableReady(u8 interactable)
{
    switch (interactable)
    {
        // Make sure the red door has a matching red key.
        case INTERACTABLE_DOOR_RED:
        {
            if (inventoryItems & INVENTORY_ITEM_KEY_RED)
            {
                // Remove the red key from the inventory on use.
                inventoryItems &= ~INVENTORY_ITEM_KEY_RED;

                return TRUE;
            }
        }
        break;

        // Make sure the green door has a matching green key.
        case INTERACTABLE_DOOR_GREEN:
        {
            if (inventoryItems & INVENTORY_ITEM_KEY_GREEN)
            {
                // Remove the green key from the inventory on use.
                inventoryItems &= ~INVENTORY_ITEM_KEY_GREEN;

                return TRUE;
            }
        }
        break;

        // Make sure the yellow door has a matching yellow key.
        case INTERACTABLE_DOOR_YELLOW:
        {
            if (inventoryItems & INVENTORY_ITEM_KEY_YELLOW)
            {
                // Remove the green key from the inventory on use.
                inventoryItems &= ~INVENTORY_ITEM_KEY_YELLOW;

                return TRUE;
            }
        }
        break;

        // Make sure that the player has a pick before letting them through a cracked tile.
        case INTERACTABLE_CRACKED_TILE_SAND:
        case INTERACTABLE_CRACKED_TILE_STONE:
        case INTERACTABLE_CRACKED_TILE_DIRT:

        {
            if (player.pickCount)
            {
                // Remove a pick on using one on the cracked tile.
                --player.pickCount;
                return TRUE;
            }
        }
        break;

        // These interactables are always ready and therefore not solid.
        case INTERACTABLE_PLATFORM:
        case INTERACTABLE_CPC:
        case INTERACTABLE_SPIKE_UP:
        case INTERACTABLE_SPIKE_DOWN:
        case INTERACTABLE_SPIKE_LEFT:
        case INTERACTABLE_SPIKE_RIGHT:
        case INTERACTABLE_SPIKE_UP_LAND:
        case INTERACTABLE_SWITCH_ON:
        case INTERACTABLE_SWITCH_OFF:
        case INTERACTABLE_VALVE_ON:
        case INTERACTABLE_VALVE_OFF:
        case INTERACTABLE_OXYGEN_POINT:
        case INTERACTABLE_EXIT:
        case INTERACTABLE_PUMP_PART1_BROKEN:
        case INTERACTABLE_PUMP_PART1_WORKING:
        case INTERACTABLE_PUMP_PART2:
        case INTERACTABLE_TELEPORTER_ON:
        case INTERACTABLE_TELEPORTER_OFF:
        case INTERACTABLE_PIPE_LOCATION:
        case INTERACTABLE_TANK_BROKEN:
        case INTERACTABLE_TANK_FIXED:
            return TRUE;
            break;
    }

    // None of the conditions met.
    return FALSE;
}

u8 activateInteractable(u8 x, u8 y, u8 roomX, u8 roomY, u8 sourceInteractbleType)
{
    u8 cell = mapCell(x, y, roomX, roomY);

    // Only activate a cell if it is an interactable.
    if (cell & CELL_INTERACTABLE_FLAG)
    {
        if ((cell & CELL_GFX_RANGE) == INTERACTABLE_SWITCH_ON)
        {
            // Change to off switch and apply water flag if currently underwater.
            updateMapCell(x, y, roomX, roomY, INTERACTABLE_SWITCH_OFF | CELL_INTERACTABLE_FLAG | (cell & CELL_WATER_FLAG));
            drawCellAtPosition(x, y);

            // Play the switch sound on activating the switch.
            playSoundEffect(SFX_SWITCH, CHANNEL_A, MAX_VOL);

            // Redraw the player so they don't disappear behind the switch.
            playerRedraw = TRUE;

            activateLinkedInteractable(x, y);
        }
        else if ((cell & CELL_GFX_RANGE) == INTERACTABLE_SWITCH_OFF)
        {
            // Change to on switch and apply water flag if currently underwater.
            updateMapCell(x, y, roomX, roomY, INTERACTABLE_SWITCH_ON | CELL_INTERACTABLE_FLAG | (cell & CELL_WATER_FLAG));
            drawCellAtPosition(x, y);

            // Play the switch sound on activating the switch.
            playSoundEffect(SFX_SWITCH, CHANNEL_A, MAX_VOL);

            // Redraw the player so they don't disappear behind the switch.
            playerRedraw = TRUE;

            activateLinkedInteractable(x, y);
        }
        else if ((cell & CELL_GFX_RANGE) == INTERACTABLE_VALVE_ON)
        {
            // Change to off valve and apply water flag if currently underwater.
            updateMapCell(x, y, roomX, roomY, INTERACTABLE_VALVE_OFF | CELL_INTERACTABLE_FLAG | (cell & CELL_WATER_FLAG));
            drawCellAtPosition(x, y);

            // Play the switch sound on activating the valve.
            playSoundEffect(SFX_SWITCH, CHANNEL_A, MAX_VOL);

            // Redraw the player so they don't disappear behind the valve.
            playerRedraw = TRUE;

            activateLinkedInteractable(x, y);
        }
        else if ((cell & CELL_GFX_RANGE) == INTERACTABLE_VALVE_OFF)
        {
            // Change to on valve and apply water flag if currently underwater.
            updateMapCell(x, y, roomX, roomY, INTERACTABLE_VALVE_ON | CELL_INTERACTABLE_FLAG | (cell & CELL_WATER_FLAG));
            drawCellAtPosition(x, y);

            // Play the switch sound on activating the valve.
            playSoundEffect(SFX_SWITCH, CHANNEL_A, MAX_VOL);

            // Only count down the map objective when turning a valve from off to on.
            if (mapObjective == OBJECTIVE_TYPE_TURN_VALVES)
            {
                reduceObjectiveActionCount();

                // Redraw hud with the updated action count.
                hudRedraw = TRUE;
            }

            // Redraw the player so they don't disappear behind the valve.
            playerRedraw = TRUE;

            activateLinkedInteractable(x, y);
        }
        else if ((cell & CELL_GFX_RANGE) == INTERACTABLE_ELECTRIC_DOOR_1 || (cell & CELL_GFX_RANGE) == INTERACTABLE_ELECTRIC_DOOR_2)
        {
            // Clear the gate (electric door on activation which is only possible via link).
            // This sets the cell to empty or with water (depending on the AND operation) so the electric door interactable is removed from the map grid.
            updateMapCell(x, y, roomX, roomY, cell & CELL_WATER_FLAG);
            drawCellAtPosition(x, y);
        }
        else if ((cell & CELL_GFX_RANGE) == INTERACTABLE_OXYGEN_POINT)
        {
            // Only add oxygen if scuba gear is equipped.
            if (inventoryItems & INVENTORY_ITEM_SCUBA)
            {
                // We are limiting the amount of oxygen you can get at an oxygen point so that it isn't too good.
                if (player.oxygen < maxOxygenPointAmount)
                {
                    player.oxygen = maxOxygenPointAmount;

                    // Play oxygen sound effect on using oxygen point to increase oxygen.
                    playSoundEffect(SFX_OXYGEN, CHANNEL_A, MAX_VOL);
                }

                hudRedraw = TRUE;
            }

            // Redraw the player so they don't disappear behind the oxygen point.
            playerRedraw = TRUE;
        }
        else if ((cell & CELL_GFX_RANGE) == INTERACTABLE_EXIT)
        {
            // Only exit if the objectives have been completed.
            if (!objectiveActionCount)
            {
                // End the level.
                levelState = LEVEL_COMPLETE;
                gameRunning = FALSE;
            }
            else
            {
                objectiveDialog();
            }
        }
        else if ((cell & CELL_GFX_RANGE) == INTERACTABLE_TELEPORTER_ON)
        {
            // Check that the source interactable is from a player activated interactble
            // where it will be 0.
            if (sourceInteractbleType == 0)
            {
                // The room matches the current room so this is the source teleporter.
                activateLinkedInteractable(x, y);
            }
            else if (sourceInteractbleType == INTERACTABLE_TELEPORTER_ON)
            {
                // The if statement checked that the teleporter is linked to another active teleporter.
                // This is the destination of the link (in a separate room) so we teleport the player to the new location.
                teleportPlayer(x, y, roomX, roomY);
            }
        }
        else if ((cell & CELL_GFX_RANGE) == INTERACTABLE_TELEPORTER_OFF)
        {
            // An active switch and the CPC interactable can turn an inactive teleporter on.
            if (sourceInteractbleType == INTERACTABLE_SWITCH_ON || sourceInteractbleType == INTERACTABLE_CPC_ACTIVATED)
            {
                updateMapCell(x, y, roomX, roomY, INTERACTABLE_TELEPORTER_ON | CELL_INTERACTABLE_FLAG);
                drawCellAtPosition(x, y);
            }

            // Make sure the player is not hidden if they attempt to activate an inactive teleporter.
            playerRedraw = TRUE;
        }
        else if ((cell & CELL_GFX_RANGE) == INTERACTABLE_CPC)
        {
            // Make sure the player is carrying a disk or a tape.
            if (inventoryItems & INVENTORY_ITEM_DISK || inventoryItems & INVENTORY_ITEM_TAPE)
            {
                updateMapCell(x, y, roomX, roomY, INTERACTABLE_CPC_ACTIVATED | CELL_INTERACTABLE_FLAG);
                drawCellAtPosition(x, y);

                if (inventoryItems & INVENTORY_ITEM_DISK)
                {
                    // Remove the disk from the inventory on use.
                    inventoryItems &= ~INVENTORY_ITEM_DISK;
                }
                else
                {
                    // Remove the tape from the inventory on use.
                    inventoryItems &= ~INVENTORY_ITEM_TAPE;
                }

                // Update hud following removing item from inventory.
                hudRedraw = TRUE;

                // Player the computer sound effect when using the CPC.
                playSoundEffect(SFX_COMPUTER, CHANNEL_A, MAX_VOL);

                // Redraw the player so they don't disappear behind the CPC.
                playerRedraw = TRUE;

                activateLinkedInteractable(x, y);
            }
        }

#ifdef WINCPCTELERA
        // Doing a wait provides a better equivalent to the CPC version below.
        wincpct_wait(400);
#else
        while (cpct_isAnyKeyPressed());
#endif

        return TRUE;
    }
    else if (sourceInteractbleType && (cell & CELL_WATER_FLAG || cell == CELL_EMPTY))
    {
        // We checked that sourceInteractableType is not 0 so that water regions can only be activated from a source interactable.

        u8 i = 0;

        // Adjust the room based destination (x,y) position to the actual (x,y) position in the map.
        u8 mapX = x + SCREEN_TILES_X * roomX;
        u8 mapY = y + SCREEN_TILES_Y * roomY;
        u8* waterRegions = level_water_regions[currentMap];

        // Iterate through all the water regions in the map.
        for (i = 0; i < level_water_array_size[currentMap]; i += 5)
        {
            // Find the map (x,y) position that matches the top left hand corner of a water region.
            if (mapX == waterRegions[i] && mapY == waterRegions[i + 1])
            {
                u8 j = 0;
                u8 k = 0;
                u8 cell = mapCell(x, y, roomX, roomY);
                u8 waterType = waterRegions[i + 4];

                // Set all the cells in the water region to empty.
                for (j = x; j < x + waterRegions[i + 2]; ++j)
                {
                    for (k = y; k < y + waterRegions[i + 3]; ++k)
                    {
                        cell = mapCell(j, k, roomX, roomY);

                        // CELL_WATER_FLAG + CELL_ITEM_FLAG
                        if (cell >= 0xA0)
                        {
                            // The cell has water as well as either item or interactable.
                            // Remove the water flag from the cell (leaving other flags intact).
                            updateMapCell(j, k, roomX, roomY, cell & ~CELL_WATER_FLAG);
                        }
                        else if (cell & CELL_WATER_FLAG)
                        {
                            // Only water flag in the cell so set cell to empty.
                            updateMapCell(j, k, roomX, roomY, CELL_EMPTY);
                        }
                        else if (cell & CELL_ITEM_FLAG || cell & CELL_INTERACTABLE_FLAG)
                        {
                            // Add the water flag to a cell if it is an item or an iteractable.
                            updateMapCell(j, k, roomX, roomY, cell | CELL_WATER_FLAG);
                        }
                        else if (cell)
                        {
                            // Cells without water but have a graphic value are tiles.
                            // In this case we do not want to change the cell.
                        }
                        else
                        {
                            // No item or interactable or tile so set the water type to the one for the water region.
                            updateMapCell(j, k, roomX, roomY, CELL_WATER_FLAG | waterType);
                        }
                    }
                }

                // Redraw the map following updating the water (whether it is on screen or not).
                mapRedraw = TRUE;

                break;
            }
        }
    }

    return FALSE;
}

void activateLinkedInteractable(u8 sourceX, u8 sourceY)
{
    u8 i = 0;

    // Get the source interactable type (without the interactable flag) from the map.
    u8 sourceInteractable = mapCell(sourceX, sourceY, currentRoomX, currentRoomY) & ~CELL_INTERACTABLE_FLAG;;

    sourceX += SCREEN_TILES_X * currentRoomX;
    sourceY += SCREEN_TILES_Y * currentRoomY;

    for (i = 0; i < level_links_array_size[currentMap]; i += 4)
    {
        u8 x = level_links[currentMap][i];
        u8 y = level_links[currentMap][i + 1];

        if (sourceX == x && sourceY == y)
        {
            u8 destinationX = level_links[currentMap][i + 2];
            u8 destinationY = level_links[currentMap][i + 3];
            u8 linkedRoomX = destinationX / SCREEN_TILES_X;
            u8 linkedRoomY = destinationY / SCREEN_TILES_Y;

            // The destination should be inside the room rather than the full size.
            destinationX %= SCREEN_TILES_X;
            destinationY %= SCREEN_TILES_Y;

            activateInteractable(destinationX, destinationY, linkedRoomX, linkedRoomY, sourceInteractable);
        }
    }
}