// Drawing primitive smoke test. Lays out a 2x2 grid of cells, each
// exercising one family of primitives. On screen each cell should show
// a clear visual signal that the underlying inner loops (C or ASM)
// produced the expected pixel pattern.
//
//   TL: jlDrawPixel + jlDrawLine (8-octant fan from cell center; pixel
//       row of all 16 colors along the cell's bottom edge).
//   TR: jlDrawRect + jlFillRect (concentric outlines + filled blocks at
//       deliberately odd x / odd width to catch nibble-edge bugs).
//   BL: jlDrawCircle + jlFillCircle (concentric outlines + a small filled
//       disk at center).
//   BR: jlTileCopy / jlTileCopyMasked / jlTileSnap+jlTilePaste / jlFloodFill.
//
// Holds the frame until the user presses ESC / RETURN / SPACE.

#include <stdio.h>

#include <joey/joey.h>

#define CELL_W             160
#define CELL_H             100

// Color slots (palette 0). Color 0 is the library-forced black.
#define C_BG               0
#define C_BORDER           1   // white
#define C_RED              2
#define C_GREEN            3
#define C_BLUE             4
#define C_YELLOW           5
#define C_CYAN             6
#define C_MAGENTA          7
#define C_ORANGE           8
#define C_GRAY             9

static void buildPalette(jlSurfaceT *screen);
static void drawCellBorder(jlSurfaceT *screen, int16_t cx, int16_t cy);
static void drawAllCellBorders(jlSurfaceT *screen);
static void drawPrimitivesPixelLine(jlSurfaceT *screen);
static void drawPrimitivesRect(jlSurfaceT *screen);
static void drawPrimitivesCircle(jlSurfaceT *screen);
static void drawPrimitivesTileFlood(jlSurfaceT *screen);
static void waitForKey(void);


static void buildPalette(jlSurfaceT *screen) {
    uint16_t colors[SURFACE_COLORS_PER_PALETTE];

    // 16 distinct $0RGB entries. Index 0 is forced to black anyway.
    colors[0]  = 0x000;
    colors[1]  = 0xFFF;     // white
    colors[2]  = 0xF00;     // red
    colors[3]  = 0x0F0;     // green
    colors[4]  = 0x00F;     // blue
    colors[5]  = 0xFF0;     // yellow
    colors[6]  = 0x0FF;     // cyan
    colors[7]  = 0xF0F;     // magenta
    colors[8]  = 0xF80;     // orange
    colors[9]  = 0x888;     // mid gray
    colors[10] = 0x800;
    colors[11] = 0x080;
    colors[12] = 0x008;
    colors[13] = 0x880;
    colors[14] = 0x088;
    colors[15] = 0x808;
    jlPaletteSet(screen, 0, colors);
    jlScbSetRange(screen, 0, SURFACE_HEIGHT - 1, 0);
}


static void drawCellBorder(jlSurfaceT *screen, int16_t cx, int16_t cy) {
    jlDrawRect(screen, cx, cy, CELL_W, CELL_H, C_BORDER);
}


static void drawAllCellBorders(jlSurfaceT *screen) {
    drawCellBorder(screen, 0,        0);
    drawCellBorder(screen, CELL_W,   0);
    drawCellBorder(screen, 0,        CELL_H);
    drawCellBorder(screen, CELL_W,   CELL_H);
}


// Top-left cell: jlDrawPixel + jlDrawLine.
//
// 8 lines fan out from the cell center (80, 50). Four are diagonal
// (the new ASM Bresenham path) and four are axis-aligned (jlDrawLine
// routes those to jlFillRect). A horizontal row of 14 pixels along the
// cell's bottom verifies jlDrawPixel: each pixel uses a different color
// index so the leftmost ones at color 0 are invisible (they are bg)
// and 1..13 progress through the palette.
static void drawPrimitivesPixelLine(jlSurfaceT *screen) {
    int16_t cx;
    int16_t cy;
    int16_t i;

    cx = CELL_W / 2;       // 80
    cy = CELL_H / 2;       // 50

    jlDrawLine(screen, cx, cy, cx + 70, cy,        C_RED);     // E (horizontal)
    jlDrawLine(screen, cx, cy, cx + 60, cy - 40,   C_GREEN);   // NE (diagonal)
    jlDrawLine(screen, cx, cy, cx,      cy - 45,   C_BLUE);    // N (vertical)
    jlDrawLine(screen, cx, cy, cx - 60, cy - 40,   C_YELLOW);  // NW
    jlDrawLine(screen, cx, cy, cx - 70, cy,        C_CYAN);    // W
    jlDrawLine(screen, cx, cy, cx - 60, cy + 40,   C_MAGENTA); // SW
    jlDrawLine(screen, cx, cy, cx,      cy + 45,   C_ORANGE);  // S
    jlDrawLine(screen, cx, cy, cx + 60, cy + 40,   C_GRAY);    // SE

    // Pixel row: 14 single-pixel writes at consecutive x to exercise
    // both odd and even nibble paths.
    for (i = 0; i < 14; i++) {
        jlDrawPixel(screen, (int16_t)(10 + i * 10), (int16_t)(CELL_H - 6),
                  (uint8_t)((i + 1) & 0x0F));
    }
}


// Top-right cell: jlDrawRect + jlFillRect.
//
// Four nested rectangles with deliberately odd x/y/w/h to exercise
// the partial-byte (nibble) edge handling in halFastFillRect. The
// outermost is filled, the next outline-only, then filled with odd
// width, then a 1-pixel-wide vertical bar (jlDrawRect collapses to a
// line via jlFillRect's 1-wide path).
static void drawPrimitivesRect(jlSurfaceT *screen) {
    int16_t ox;

    ox = CELL_W;           // cell origin x

    // Outer fill, even-aligned.
    jlFillRect(screen, ox + 8, 8, 144, 84, C_RED);
    // Inner outline, odd x to test partial-nibble edges.
    jlDrawRect(screen, ox + 17, 17, 124, 64, C_YELLOW);
    // Odd width fill, odd x.
    jlFillRect(screen, ox + 25, 25, 35, 48, C_GREEN);
    // 1-pixel vertical bar (degenerate rect through jlFillRect 1-wide path).
    jlFillRect(screen, ox + 100, 25, 1, 48, C_BORDER);
    // Odd-x odd-w narrow bar to specifically hit hasLeading + hasTrailing
    // in halFastFillRect.
    jlFillRect(screen, ox + 75, 25, 7, 48, C_CYAN);
}


// Bottom-left cell: jlDrawCircle + jlFillCircle.
//
// Concentric outlines at decreasing radii, alternating colors, plus a
// small filled disk at the center. Center is at the cell midpoint.
static void drawPrimitivesCircle(jlSurfaceT *screen) {
    int16_t cx;
    int16_t cy;

    cx = CELL_W / 2;
    cy = CELL_H + CELL_H / 2;

    jlDrawCircle(screen, cx, cy, 45, C_BORDER);
    jlDrawCircle(screen, cx, cy, 35, C_GREEN);
    jlDrawCircle(screen, cx, cy, 25, C_YELLOW);
    jlDrawCircle(screen, cx, cy, 15, C_CYAN);
    jlFillCircle(screen, cx, cy, 8,  C_MAGENTA);
}


// Bottom-right cell: tile + flood fill.
//
// Top portion: a 16x16 colored block, then jlTileSnap one of its 8x8
// quadrants and jlTilePaste the captured tile to a neighbor block;
// also jlTileCopy the same source quadrant to a third location to
// exercise the full surface-to-surface path. Then a jlTileCopyMasked
// case: paint a 2x1 (16x8) "stripe" containing a transparent color 0
// pattern interleaved with color, paste it over a solid backdrop with
// transparent=0; the backdrop should show through the transparent
// nibbles.
//
// Bottom portion: jlDrawRect outlines a closed region, jlFloodFillBounded
// fills its interior with a different color, stopping at the outline.
static void drawPrimitivesTileFlood(jlSurfaceT *screen) {
    int16_t  ox;
    int16_t  oy;
    int16_t  bx;
    int16_t  by;
    int16_t  i;
    int16_t  px;
    jlTileT    snapBuf;

    ox = CELL_W;           // 160
    oy = CELL_H;           // 100

    // Source 16x16 block at (168, 108): a 4-quadrant pattern.
    jlFillRect(screen, ox + 8,  oy + 8,  8, 8, C_RED);
    jlFillRect(screen, ox + 16, oy + 8,  8, 8, C_GREEN);
    jlFillRect(screen, ox + 8,  oy + 16, 8, 8, C_BLUE);
    jlFillRect(screen, ox + 16, oy + 16, 8, 8, C_YELLOW);

    // jlTileSnap the top-left red quadrant (block bx=21, by=13) and
    // jlTilePaste it next to the 16x16 source as the 5th quadrant.
    bx = (int16_t)((ox + 8) / 8);
    by = (int16_t)((oy + 8) / 8);
    jlTileSnap(screen, (uint8_t)bx, (uint8_t)by, &snapBuf);
    jlTilePaste(screen, (uint8_t)(bx + 4), (uint8_t)by, &snapBuf);

    // jlTileCopy from the green quadrant onto a fresh location below.
    jlTileCopy(screen, (uint8_t)(bx + 4), (uint8_t)(by + 1),
                     screen, (uint8_t)(bx + 1), (uint8_t)by);

    // jlTileCopyMasked test: build a "transparent" striped pattern at
    // (208, 132). The tile's source has color 0 in alternating
    // nibbles. Paste it onto a solid orange backdrop so transparent
    // nibbles let the orange show through.
    jlFillRect(screen, ox + 80, oy + 32, 16, 8, C_ORANGE);  // backdrop
    // Build a vertical-stripe source at (240, 132): col-pixel = (px % 2 ? color : 0)
    for (i = 0; i < 8; i++) {
        for (px = 0; px < 16; px++) {
            jlDrawPixel(screen, (int16_t)(ox + 112 + px), (int16_t)(oy + 32 + i),
                      (uint8_t)((px & 1) ? C_MAGENTA : 0));
        }
    }
    // jlTileCopyMasked: source at block (ox+112)/8 = 34..35, by 16
    // -> dst at backdrop block (ox+80)/8 = 30..31, by 16
    jlTileCopyMasked(screen, (uint8_t)((ox + 80) / 8), (uint8_t)((oy + 32) / 8),
                           screen, (uint8_t)((ox + 112) / 8), (uint8_t)((oy + 32) / 8),
                           0);

    // Flood-fill region: a small bordered rectangle in the cell's
    // lower portion. Outline drawn in C_BORDER; jlFloodFillBounded
    // from a point inside should fill with C_CYAN, stopping at the
    // border.
    jlDrawRect(screen, ox + 16, oy + 60, 64, 32, C_BORDER);
    jlFloodFillBounded(screen, (int16_t)(ox + 48), (int16_t)(oy + 76),
                     C_CYAN, C_BORDER);

    // Plain jlFloodFill: solid block then re-fill to a new color.
    jlFillRect(screen, ox + 96, oy + 60, 48, 32, C_GREEN);
    jlFloodFill(screen, (int16_t)(ox + 120), (int16_t)(oy + 76), C_GRAY);
}


static void waitForKey(void) {
    jlWaitForAnyKey();
}


int main(void) {
    jlConfigT  config;
    jlSurfaceT    *screen;

    config.codegenBytes = 8 * 1024;
    config.audioBytes   = 64UL * 1024;

    if (!jlInit(&config)) {
        fprintf(stderr, "jlInit failed: %s\n", jlLastError());
        return 1;
    }

    screen = jlStageGet();
    if (screen == NULL) {
        fprintf(stderr, "jlStageGet returned NULL\n");
        jlShutdown();
        return 1;
    }

    buildPalette(screen);
    jlSurfaceClear(screen, C_BG);
    drawAllCellBorders(screen);
    drawPrimitivesPixelLine(screen);
    drawPrimitivesRect(screen);
    drawPrimitivesCircle(screen);
    drawPrimitivesTileFlood(screen);
    jlStagePresent();

    waitForKey();

    jlShutdown();
    return 0;
}
