#include "SpriteUtil.h"

u8 displayToPixel0(u8 displayByte)
{
    // Information about mapping display bits to the pen value can be found here:
    // http://cpctech.cpc-live.com/docs/graphics.html
    u8 pixel = 0;

    if (displayByte & 2)
    {
        // Set bit 3 of pixel to display bit 1.
        pixel += (1 << 3);
    }

    if (displayByte & 32)
    {
        // Set bit 2 of pixel to display bit 5.
        pixel += (1 << 2);
    }

    if (displayByte & 8)
    {
        // Set bit 1 of pixel to display bit 3.
        pixel += (1 << 1);
    }

    if (displayByte & 128)
    {
        // Set bit 0 of pixel to display bit 7.
        pixel += (1 << 0);
    }

    return pixel;
}

u8 displayToPixel1(u8 displayByte)
{
    // Information about mapping display bits to the pen value can be found here:
    // http://cpctech.cpc-live.com/docs/graphics.html
    u8 pixel = 0;

    if (displayByte & 1)
    {
        // Set bit 3 of pixel to display bit 0.
        pixel += (1 << 3);
    }

    if (displayByte & 16)
    {
        // Set bit 2 of pixel to display bit 4.
        pixel += (1 << 2);
    }

    if (displayByte & 4)
    {
        // Set bit 1 of pixel to display bit 2.
        pixel += (1 << 1);
    }

    if (displayByte & 64)
    {
        // Set bit 0 of pixel to display bit 6.
        pixel += (1 << 0);
    }

    return pixel;
}

u8 hardwareToFirmwareColour(u8 hardwareColour)
{
    switch (hardwareColour)
    {
        // HW_BLACK -> FW_BLACK
    case 0x14:
        return 0;

        // HW_BLUE -> FW_BLUE
    case 0x04:
        return 1;

        // HW_BRIGHT_BLUE -> FW_BRIGHT_BLUE
    case 0x15:
        return 2;

        // HW_RED -> FW_RED
    case 0x1C:
        return 3;

        // HW_MAGENTA -> FW_MAGENTA
    case 0x18:
        return 4;

        // HW_MAUVE -> FW_MAUVE
    case 0x1D:
        return 5;

        // HW_BRIGHT_RED -> FW_BRIGHT_RED
    case 0x0C:
        return 6;

        // HW_PURPLE -> FW_PURPLE
    case 0x05:
        return 7;

        // HW_BRIGHT_MAGENTA -> FW_BRIGHT_MAGENTA
    case 0x0D:
        return 8;

        // HW_GREEN -> FW_GREEN
    case 0x16:
        return 9;

        // HW_CYAN -> FW_CYAN
    case 0x06:
        return 10;

        // HW_SKY_BLUE -> FW_SKY_BLUE
    case 0x17:
        return 11;

        // HW_YELLOW -> FW_YELLOW
    case 0x1E:
        return 12;

        // HW_WHITE -> FW_WHITE
    case 0x00:
        return 13;

        // HW_PASTEL_BLUE -> FW_PASTEL_BLUE
    case 0x1F:
        return 14;

        // HW_ORANGE -> FW_ORANGE
    case 0x0E:
        return 15;

        // HW_PINK -> FW_PINK
    case 0x07:
        return 16;

        // HW_PASTEL_MAGENTA -> FW_PASTEL_MAGENTA
    case 0x0F:
        return 17;

        // HW_BRIGHT_GREEN -> FW_BRIGHT_GREEN
    case 0x12:
        return 18;

        // HW_SEA_GREEN -> FW_SEA_GREEN
    case 0x02:
        return 19;

        // HW_BRIGHT_CYAN -> FW_BRIGHT_CYAN
    case 0x13:
        return 20;

        // HW_LIME -> FW_LIME
    case 0x1A:
        return 21;

        // HW_PASTEL_GREEN -> FW_PASTEL_GREEN
    case 0x19:
        return 22;

        // HW_PASTEL_CYAN -> FW_PASTEL_CYAN
    case 0x1B:
        return 23;

        // HW_BRIGHT_YELLOW -> FW_BRIGHT_YELLOW
    case 0x0A:
        return 24;

        // HW_PASTEL_YELLOW -> FW_PASTEL_YELLOW
    case 0x03:
        return 25;

        // HW_BRIGHT_WHITE -> FW_BRIGHT_WHITE
    case 0x0B:
        return 26;
    }

    // Default to FW_BLACK for any other colour (to fix the warning).
    return 0;
}

ALLEGRO_COLOR firmwareToAllegroColour(u8 firmwareColour)
{
    switch (firmwareColour)
    {
        // Black
    case 0x00:
        return al_map_rgb(0, 0, 0);

        // Blue
    case 0x01:
        return al_map_rgb(0, 0, 102);

        // Bright Blue
    case 0x02:
        return al_map_rgb(0, 0, 255);

        // Red
    case 0x03:
        return al_map_rgb(102, 0, 0);

        // Magenta
    case 0x04:
        return al_map_rgb(102, 0, 102);

        // Mauve
    case 0x05:
        return al_map_rgb(102, 0, 255);

        // Bright Red
    case 0x06:
        return al_map_rgb(255, 0, 0);

        // Purple
    case 0x07:
        return al_map_rgb(255, 0, 102);

        // Bright Magenta
    case 0x08:
        return al_map_rgb(255, 0, 255);

        // Green
    case 0x09:
        return al_map_rgb(0, 102, 0);

        // Cyan
    case 0x0A:
        return al_map_rgb(0, 102, 102);

        // Sky Blue
    case 0x0B:
        return al_map_rgb(0, 102, 255);

        // Yellow
    case 0x0C:
        return al_map_rgb(102, 102, 0);

        // White (grey)
    case 0x0D:
        return al_map_rgb(102, 102, 102);

        // Pastel Blue
    case 0x0E:
        return al_map_rgb(102, 102, 255);

        // Orange
    case 0x0F:
        return al_map_rgb(255, 102, 0);

        // Pink
    case 0x10:
        return al_map_rgb(255, 102, 102);

        // Pastel Magenta
    case 0x11:
        return al_map_rgb(255, 102, 255);

        // Bright Green
    case 0x12:
        return al_map_rgb(0, 255, 0);

        // Sea Green
    case 0x13:
        return al_map_rgb(0, 255, 102);

        // Bright Cyan
    case 0x14:
        return al_map_rgb(0, 255, 255);

        // Lime
    case 0x15:
        return al_map_rgb(102, 255, 0);

        // Pastel Green
    case 0x16:
        return al_map_rgb(102, 255, 102);

        // Pastel Cyan
    case 0x17:
        return al_map_rgb(102, 255, 255);

        // Bright Yellow
    case 0x18:
        return al_map_rgb(255, 255, 0);

        // Pastel Yellow
    case 0x19:
        return al_map_rgb(255, 255, 102);

        // Bright White
    case 0x1A:
        return al_map_rgb(255, 255, 255);

        // Everything else can default to black.
    default:
        return al_map_rgb(0, 0, 0);
    }
}

ALLEGRO_BITMAP* constructBitmap(const u8* displayBytes, u8 displayByteCount, u8 width, u8 height, u8 targetWidth, const u8* palette, u8 transparencyType)
{
    // Create a memory bitmap (making sure to use the target width which should be double the width in the display bytes.
    ALLEGRO_BITMAP* bitmap = al_create_bitmap(targetWidth, height);

    // Save the existing target bitmap before working on it.
    ALLEGRO_BITMAP* currentTarget = al_get_target_bitmap();

    u8 i = 0;

    // Set the target to the new bitmap.
    al_set_target_bitmap(bitmap);

    // Each display byte is converted to pixels.
    for (i = 0; i < displayByteCount; ++i)
    {
        u8 x = i % width;
        u8 y = i / width;

        // Convert from the display bytes into two pixels (0 and 1) which will match the pen values in the palette.
        u8 pixel0 = displayToPixel0(displayBytes[i]);
        u8 pixel1 = displayToPixel1(displayBytes[i]);

        // Convert from pen values in the palette to ALLEGRO_COLOR and then write it to the target bitmap.
        al_put_pixel(x * 2, y, penToColour(pixel0, palette));
        al_put_pixel(1 + x * 2, y, penToColour(pixel1, palette));
    }

    // Restore the target bitmap.
    al_set_target_bitmap(currentTarget);

    // Convert magic colour (bright magenta) to alpha for transparency.
    if (transparencyType == 2)
    {
        al_convert_mask_to_alpha(bitmap, al_map_rgb(255, 0, 255));
    }

    return bitmap;
}

ALLEGRO_COLOR penToColour(u8 pen, const u8* palette)
{
    // Retrieve the hardware colour from the palette for the given pen value (0-15).
    u8 paletteValue = *(palette + pen);

    // Convert from the hardware colour value in the palette to a firmware colour.
    u8 firmwareColour = hardwareToFirmwareColour(paletteValue);

    // Convert the firmware colour value to an ALLEGRO_COLOR.
    return firmwareToAllegroColour(firmwareColour);
}