// Shared emit-time source-pixel helpers for the chunky 4bpp sprite
// emitters -- spriteEmitX86.c (DOS) and spriteEmitIigs.c. These two
// functions ARE the transparency/shift contract that the emitted
// routines must share with the interpreted walkers bit-for-bit, so
// they exist exactly once: a fix applied here reaches both CPUs.
//
// Emit-time only (jlSpriteCompile), never on a draw hot path, so plain
// static functions are acceptable here despite the general
// macros-over-inline preference for shared headers. Tile geometry
// comes from joey/tile.h; TRANSPARENT_NIBBLE from spriteInternal.h.

#ifndef JOEYLIB_SPRITE_EMIT_CHUNKY_H
#define JOEYLIB_SPRITE_EMIT_CHUNKY_H

#include "joey/sprite.h"
#include "joey/tile.h"
#include "spriteInternal.h"


static void    shiftedByteAt(const jlSpriteT *sp, uint16_t row, uint16_t col, uint8_t shift, uint16_t spriteBytesPerRow, uint8_t *outValue, uint8_t *outOpaqueMask);
static uint8_t spriteSourceByte(const jlSpriteT *sp, uint16_t row, uint16_t col);


// Decompose a destination byte's contribution from the sprite into
// (value, opaqueMask) for shift in {0, 1}. opaqueMask high nibble
// 0xF0 means high dest nibble is opaque; 0x0F means low is opaque;
// 0x00 means both transparent. value's transparent nibbles are 0.
static void shiftedByteAt(const jlSpriteT *sp, uint16_t row, uint16_t col, uint8_t shift, uint16_t spriteBytesPerRow, uint8_t *outValue, uint8_t *outOpaqueMask) {
    uint8_t srcByte;
    uint8_t hi;
    uint8_t lo;
    bool    hasLeft;
    bool    hasRight;

    *outValue      = 0;
    *outOpaqueMask = 0;

    if (shift == 0) {
        if (col >= spriteBytesPerRow) {
            return;
        }
        srcByte = spriteSourceByte(sp, row, col);
        hi      = (uint8_t)((srcByte >> 4) & 0x0Fu);
        lo      = (uint8_t)(srcByte & 0x0Fu);
        if (hi != TRANSPARENT_NIBBLE) {
            *outValue      |= (uint8_t)(hi << 4);
            *outOpaqueMask |= 0xF0u;
        }
        if (lo != TRANSPARENT_NIBBLE) {
            *outValue      |= lo;
            *outOpaqueMask |= 0x0Fu;
        }
        return;
    }
    // shift = 1
    hasLeft  = (col >= 1) && ((uint16_t)(col - 1) < spriteBytesPerRow);
    hasRight = (col < spriteBytesPerRow);
    if (hasLeft) {
        srcByte = spriteSourceByte(sp, row, (uint16_t)(col - 1));
        hi      = (uint8_t)(srcByte & 0x0Fu);          // sprite byte's LOW nibble
        if (hi != TRANSPARENT_NIBBLE) {
            *outValue      |= (uint8_t)(hi << 4);
            *outOpaqueMask |= 0xF0u;
        }
    }
    if (hasRight) {
        srcByte = spriteSourceByte(sp, row, col);
        lo      = (uint8_t)((srcByte >> 4) & 0x0Fu);   // sprite byte's HIGH nibble
        if (lo != TRANSPARENT_NIBBLE) {
            *outValue      |= lo;
            *outOpaqueMask |= 0x0Fu;
        }
    }
}


// Sample a sprite tile-data byte at (row, col) where col is in
// sprite-byte coordinates (0..spriteBytesPerRow-1).
static uint8_t spriteSourceByte(const jlSpriteT *sp, uint16_t row, uint16_t col) {
    uint16_t       tileX;
    uint16_t       tileY;
    uint16_t       inTileX;
    uint16_t       inTileY;
    const uint8_t *tile;

    tileX   = (uint16_t)(col / TILE_BYTES_PER_ROW);
    tileY   = (uint16_t)(row / TILE_PIXELS_PER_SIDE);
    inTileX = (uint16_t)(col & (TILE_BYTES_PER_ROW - 1));
    inTileY = (uint16_t)(row & (TILE_PIXELS_PER_SIDE - 1));
    tile    = sp->tileData + ((uint32_t)(tileY * sp->widthTiles + tileX)) * TILE_BYTES;
    return tile[inTileY * TILE_BYTES_PER_ROW + inTileX];
}

#endif
