#include "Actor.h"

void initActor(Actor* actor, u8 x, u8 y)
{
    u8 roomX = x / SCREEN_TILES_X;
    u8 roomY = y / SCREEN_TILES_Y;

    // Set room and position information based on x,y values.
    actor->object.position.x = (x % SCREEN_TILES_X) * TILE_WIDTH_BYTES;
    actor->object.position.y = (y % SCREEN_TILES_Y) * TILE_HEIGHT;
    actor->object.previousPosition.x = actor->object.position.x;
    actor->object.previousPosition.y = actor->object.position.y;
    actor->object.position.room = roomX + roomY * 2;

    // Set redraw to draw the actor by default.
    actor->redraw = TRUE;
}

#ifdef CPC
void drawActor(Actor* actor, u8* actorSprite)
{
    u8 cellX = actor->object.position.x / TILE_WIDTH_BYTES;
    u8 cellY = actor->object.position.y / TILE_HEIGHT;
    u8 previousCellX = actor->object.previousPosition.x / TILE_WIDTH_BYTES;
    u8 previousCellY = actor->object.previousPosition.y / TILE_HEIGHT;

    // Make sure to calculate the backbuffer width based on previous cell otherwise
    // old data can be displayed on the left side of the screen when it is first drawn
    // when coming into a room from the right.
    backbufferWidth = previousCellX == 19 ? TILE_WIDTH_BYTES : BACKBUFFER_WIDTH;

    // Populate the backbuffer from the previous actor position and draw on the screen.
    if (previousCellX != cellX || previousCellY != cellY)
    {
        populateBackbufferFromMap(previousCellX, previousCellY);
        drawBackbuffer(previousCellX, previousCellY);
    }

    // When at the right edge of the screen (x == 19) the width of the back buffer is only one cell wide.
    backbufferWidth = cellX == 19 ? TILE_WIDTH_BYTES : BACKBUFFER_WIDTH;

    // Add the sprites to the back buffer (we expand x and y to one of the 4 cells in the back buffer but the source is the map grid for the cell value).
    populateBackbufferFromMap(cellX, cellY);

    // Add the actor sprite to the back buffer.
    addSpriteToBackbuffer(actor->object.position.x % TILE_WIDTH_BYTES, actor->object.position.y % TILE_HEIGHT, actorSprite);

    // Draw the back buffer now that the actor sprite has been added to it.
    drawBackbuffer(cellX, cellY);
}

#else

void drawActorAllegro(Actor* actor, ALLEGRO_BITMAP* actorSprite)
{
    // Adjusting x and y by allegro tile height.
    u16 x = actor->object.position.x * 4 * scaleFactor;
    u16 y = actor->object.position.y * scaleFactor;

    // No need to redraw the previous cells if the actor is stationary.
    // This prevents an enemy from drawing over the player if the player is stationary.
    if (actor->object.position.x != actor->object.previousPosition.x || actor->object.position.y != actor->object.previousPosition.y)
    {
        // Redraw the cells on the map where the actor was previously.
        redrawActorPreviousPosition(actor);
    }

    // Draw the actor at its current position.
#ifdef ALLEGRO4
    stretch_sprite(screen, actorSprite, x, y, 16 * scaleFactor, 16 * scaleFactor);
#else
    al_draw_scaled_bitmap(actorSprite, 0, 0, TILE_WIDTH_BYTES * 2, TILE_HEIGHT, x, y, 16 * scaleFactor, 16 * scaleFactor, 0);
#endif
}

void redrawActorPreviousPosition(Actor* actor)
{
    // The tiles that the actor previously occupied.
    u8 occupiedXTileLeft = (actor->object.previousPosition.x) >> 2;
    u8 occupiedXTileRight = (actor->object.previousPosition.x + TILE_WIDTH_BYTES - 1);
    u8 occupiedYTileBottom = (actor->object.previousPosition.y + TILE_HEIGHT - 1);
    u8 occupiedYTileTop = (actor->object.previousPosition.y) >> 4;
    occupiedXTileRight >>= 2;
    occupiedYTileBottom >>= 4;

    drawCellAtPosition(occupiedXTileLeft, occupiedYTileTop);
    drawCellAtPosition(occupiedXTileLeft, occupiedYTileBottom);
    drawCellAtPosition(occupiedXTileRight, occupiedYTileTop);
    drawCellAtPosition(occupiedXTileRight, occupiedYTileBottom);
}

#endif

u8 actorOccuppyingCells(Actor* actor, u8 cellType)
{
    u8 cellDetected = 0;

    // Find the cells that the actor is in.
    // Some of these may be duplicate but we are looking for when the actor occupies all four cells.
    u8 occupiedXTileLeft = (actor->object.position.x) >> 2;
    u8 occupiedXTileRight = (actor->object.position.x + TILE_WIDTH_BYTES - 1);
    u8 occupiedYTileBottom = (actor->object.position.y + TILE_HEIGHT - 1);
    u8 occupiedYTileTop = (actor->object.position.y) >> 4;
    occupiedXTileRight >>= 2;
    occupiedYTileBottom >>= 4;

    // Check that the actor occupies every cell.
    cellDetected += cellContainsType(occupiedXTileLeft, occupiedYTileTop, cellType);
    cellDetected += cellContainsType(occupiedXTileRight, occupiedYTileTop, cellType);
    cellDetected += cellContainsType(occupiedXTileLeft, occupiedYTileBottom, cellType);
    cellDetected += cellContainsType(occupiedXTileRight, occupiedYTileBottom, cellType);

    // This is expected to be 4 if the actor is completely occupying the specified cell type.
    return cellDetected;
}
