// Generic portable-C op defaults -- the new joeylib's "generic backend" (the
// joey.c-equivalent reference). A machine that does not override an op (no
// JL_HAS_<OP> defined for it in include/joey/platform.h) links these. An
// override may also defer edge cases here, e.g. the IIgs stage-only fast clear
// falls back to jlpGenericSurfaceClear for non-stage surfaces.
//
// These functions are always compiled into every target; the jlp<Op> dispatch
// in src/core/port.h routes to either a machine override or, by default, the
// matching generic here.

#include <string.h>

#include "surfaceInternal.h"   // full jlSurfaceT (s->pixels) + SURFACE_PIXELS_SIZE
#include "port.h"               // jlpGenericSurfaceClear prototype


void jlpGenericSurfaceClear(jlSurfaceT *s, uint8_t doubled) {
    if (s->pixels != NULL) {
        memset(s->pixels, doubled, SURFACE_PIXELS_SIZE);
    }
}


// Chunky 4bpp on-surface rect fill. Caller has proven the rect is fully on
// surface (x,y >= 0; x+w <= WIDTH; y+h <= HEIGHT; w,h > 0). Planar ports have
// NULL s->pixels and override jlpFillRect with their plane fill, so this is the
// chunky default (IIgs non-stage / DOS). The span geometry (edge nibbles +
// whole-byte middle run) depends only on x/w, so it is derived once up front
// and the row loop just advances one pointer by the row stride (PERF-AUDIT #49).
void jlpGenericFillRect(jlSurfaceT *s, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t colorIndex) {
    uint8_t  nibble   = colorIndex & 0x0F;
    uint8_t  doubled  = (uint8_t)((nibble << 4) | nibble);
    uint16_t pxStart  = (uint16_t)x;
    uint16_t pxEnd    = (uint16_t)(x + w);
    bool     hasLeft  = false;
    bool     hasRight = false;
    uint16_t leftByte;
    uint16_t midByte;
    uint16_t midBytes;
    uint16_t rightByte;
    uint8_t *line;
    int16_t  row;

    if (s->pixels == NULL) {
        return;
    }
    leftByte = (uint16_t)(pxStart >> 1);
    if (pxStart & 1u) {
        hasLeft = true;
        pxStart++;
    }
    midByte  = (uint16_t)(pxStart >> 1);
    midBytes = (uint16_t)((pxEnd - pxStart) >> 1);
    pxStart  = (uint16_t)(pxStart + (midBytes << 1));
    if (pxStart < pxEnd) {
        hasRight = true;
    }
    rightByte = (uint16_t)(pxStart >> 1);
    line      = &s->pixels[SURFACE_ROW_OFFSET(y)];
    for (row = 0; row < h; row++) {
        if (hasLeft) {
            line[leftByte] = (uint8_t)((line[leftByte] & 0xF0) | nibble);
        }
        if (midBytes > 0u) {
            memset(&line[midByte], doubled, (size_t)midBytes);
        }
        if (hasRight) {
            line[rightByte] = (uint8_t)((line[rightByte] & 0x0F) | (nibble << 4));
        }
        line += SURFACE_BYTES_PER_ROW;
    }
}


// Chunky 4bpp nibble read-modify-write. Caller (plotPixelNoMark / jlDrawPixel)
// has already done the NULL + bounds check.
void jlpGenericDrawPixel(jlSurfaceT *s, uint16_t x, uint16_t y, uint8_t colorIndex) {
    uint8_t *byte;
    uint8_t  nibble;

    if (s->pixels == NULL) {
        return;
    }
    byte   = &s->pixels[SURFACE_ROW_OFFSET(y) + (x >> 1)];
    nibble = colorIndex & 0x0F;
    if (x & 1) {
        *byte = (uint8_t)((*byte & 0xF0) | nibble);
    } else {
        *byte = (uint8_t)((*byte & 0x0F) | (nibble << 4));
    }
}
