// ── bitmap_spin.h ─────────────────────────────────────────────────────────────
// Stamps a 9×9 bitmap on X=4 & X=5 (the YZ plane), then rotates it 360°.
//
//  The bitmap is treated as a physical flat card sitting in the YZ plane.
//  Forward-mapping moves each lit pixel to its true 3D LED position after
//  rotation, so the card genuinely sweeps through the cube rather than just
//  foreshortening.
//
//  Rotation behaviour:
//    AXIS_X  →  in-plane 2D spin in YZ  (stays on X=4 & X=5)
//    AXIS_Y  →  revolving door in XZ    (right side sweeps toward viewer)
//    AXIS_Z  →  forward somersault in XY (top sweeps toward viewer)
//
//  Rotation matrices (right-hand, bitmap starts at dx=0 so second term drops):
//    AXIS_X:  ledY = dY·c/128 − dZ·s/128 + 4      ledZ = dY·s/128 + dZ·c/128 + 4
//    AXIS_Y:  ledX = dZ·s/128 + 4                  ledZ = dZ·c/128 + 4
//    AXIS_Z:  ledX = dY·s/128 + 4                  ledY = dY·c/128 + 4
//
//  Trig: cos8(a)/sin8(a) → 0–255; subtract 128 → −128..+127 ≈ 128·cos/sin(θ)
//        +64 before >>7 gives correct integer rounding.
//
//  Usage:
//    bitmap_spin        (BITMAP_DIAMOND, 20, 60, CRGB::Cyan,   AXIS_X);
//    bitmap_spin        (BITMAP_SMILEY,  20, 60, CRGB::Yellow, AXIS_Y);
//    bitmap_spin        (BITMAP_HEART,   20, 60, CRGB::Red,    AXIS_Z);
//    bitmap_spin_rainbow(BITMAP_STAR,    20, 60, AXIS_Y);
//    bitmap_spin_repeat (BITMAP_RING,    20, 60, CRGB::Blue, 3, AXIS_Z);
//
//  Dependencies (provided by Draw.h):
//    DrawPixel(), FastLED, CUBE_SIZE, AXIS_X, AXIS_Y, AXIS_Z, sin8(), cos8()
// ─────────────────────────────────────────────────────────────────────────────

#ifndef BITMAP_SPIN_H
#define BITMAP_SPIN_H

#include <stdint.h>
// Draw.h already included via Effects.h

static const int8_t  BS_CENTER   = 4;   // rotation centre in Y and Z (and initial X)
static const uint8_t BS_PLANE_LO = 4;   // lower X plane for AXIS_X stamps
static const uint8_t BS_PLANE_HI = 5;   // upper X plane for AXIS_X stamps

// ── Sample bitmaps ────────────────────────────────────────────────────────────
// uint16_t[9]: bmp[0] = top row (Y=8), bmp[8] = bottom row (Y=0)
// bit 0 = Z=0 (left), bit 8 = Z=8 (right)

// Diamond
static const uint16_t BITMAP_DIAMOND[9] = {
    0b000010000,   // Y=8  tip
    0b000111000,
    0b001111100,
    0b011111110,
    0b111111111,   // Y=4  widest
    0b011111110,
    0b001111100,
    0b000111000,
    0b000010000,   // Y=0  tip
};

// Heart ♥
static const uint16_t BITMAP_HEART[9] = {
    0b011000110,   // Y=8  two bumps
    0b111101111,
    0b111111111,
    0b111111111,
    0b011111110,
    0b001111100,
    0b000111000,
    0b000010000,   // Y=1  point
    0b000000000,   // Y=0
};

// 6-pointed star ✦
static const uint16_t BITMAP_STAR[9] = {
    0b000010000,   // Y=8  top point
    0b000010000,
    0b100010001,
    0b010111010,
    0b001111100,   // Y=4  centre
    0b010111010,
    0b100010001,
    0b000010000,
    0b000010000,   // Y=0  bottom point
};

// Ring / circle outline
static const uint16_t BITMAP_RING[9] = {
    0b000111000,
    0b001000100,
    0b010000010,
    0b100000001,
    0b100000001,
    0b100000001,
    0b010000010,
    0b001000100,
    0b000111000,
};

// Smiley — eyes + smile only, no outline
//
//    Z: 0 1 2 3 4 5 6 7 8
//    Y=8 row0: . . . . . . . . .
//    Y=7 row1: . . . . . . . . .
//    Y=6 row2: . . * . . . * . .   ← eyes (Z=2, Z=6)
//    Y=5 row3: . . * . . . * . .   ← eyes continued
//    Y=4 row4: . . . . . . . . .
//    Y=3 row5: . * . . . . . * .   ← smile corners (Z=1, Z=7)
//    Y=2 row6: . . * * * * * . .   ← smile arc (Z=2–Z=6)
//    Y=1 row7: . . . . . . . . .
//    Y=0 row8: . . . . . . . . .
//
static const uint16_t BITMAP_SMILEY[9] = {
    0b000000000,   // row 0  (Y=8)
    0b000000000,   // row 1  (Y=7)
    0b001000100,   // row 2  (Y=6)  left eye col2, right eye col6
    0b001000100,   // row 3  (Y=5)  eyes continued
    0b000000000,   // row 4  (Y=4)
    0b010000010,   // row 5  (Y=3)  smile corners col1 & col7
    0b001111100,   // row 6  (Y=2)  smile arc col2–col6
    0b000000000,   // row 7  (Y=1)
    0b000000000,   // row 8  (Y=0)
};

static const uint16_t BITMAP_ARROW[9] = {
    0b000010000,   // row 0  (Y=8)
    0b000011000,   // row 1  (Y=7)
    0b111110100,   // row 2  (Y=6)  
    0b100000001,   // row 3  (Y=5)  
    0b100000001,   // row 4  (Y=4)
    0b111110100,   // row 5  (Y=3)  
    0b000011000,   // row 6  (Y=2)  
    0b000010000,   // row 7  (Y=1)
    0b000000000,   // row 8  (Y=0)
};

// ── Core render step ──────────────────────────────────────────────────────────
// Forward-maps every lit bitmap pixel to its 3D LED coordinate after rotation.
// FastLED.clear() is called first so no ghost pixels accumulate between steps.
//
// AXIS_X: stamp sits on X=4 & X=5 throughout (in-plane YZ rotation)
// AXIS_Y: sweeps in XZ plane — each srcZ column maps to (ledX, ledZ)
// AXIS_Z: sweeps in XY plane — each srcY row maps to (ledX, ledY)
//
static void _bs_render_step(const uint16_t bmp[9], uint8_t angle8,
                             uint8_t rotAxis, CRGB color)
{
    // Fixed-point trig: c = 128·cos(θ), s = 128·sin(θ), range −128..+127
    int16_t c = (int16_t)cos8(angle8) - 128;
    int16_t s = (int16_t)sin8(angle8) - 128;

    FastLED.clear();   // clear whole cube — forward mapping lands anywhere

    for (int8_t srcRow = 0; srcRow < 9; srcRow++) {

        // bitmap row 0 = top of glyph = Y=8 on the cube
        int8_t  srcY = (int8_t)(8 - srcRow);
        int16_t dY   = (int16_t)(srcY - BS_CENTER);  // centred Y offset

        for (int8_t srcZ = 0; srcZ < 9; srcZ++) {
            if (!((bmp[srcRow] >> (uint8_t)srcZ) & 0x01)) continue;

            int16_t dZ = (int16_t)(srcZ - BS_CENTER);  // centred Z offset

            int8_t lX, lY, lZ;

            switch (rotAxis) {

                // ── AXIS_X — in-plane 2D rotation in YZ ─────────────────────
                // Rotation around X: dy′ = dY·c − dZ·s
                //                    dz′ = dY·s + dZ·c
                // Bitmap stays on X=4 & X=5.
                // +64 before >>7 gives correct rounding.
                case AXIS_X:
                default:
                    lY = (int8_t)(((dY * c - dZ * s + 64) >> 7) + BS_CENTER);
                    lZ = (int8_t)(((dY * s + dZ * c + 64) >> 7) + BS_CENTER);
                    if (lY >= 0 && lY < CUBE_SIZE && lZ >= 0 && lZ < CUBE_SIZE) {
                        DrawPixel(BS_PLANE_LO, (uint8_t)lY, (uint8_t)lZ, color);
                        DrawPixel(BS_PLANE_HI, (uint8_t)lY, (uint8_t)lZ, color);
                    }
                    break;

                // ── AXIS_Y — revolving door, sweeps in XZ plane ──────────────
                // Rotation around Y: dx′ = dZ·s  (dx was 0 — bitmap at X=4)
                //                    dz′ = dZ·c
                // Y coordinate is unchanged (it's the rotation axis).
                // At  0°: ledX=4, ledZ=srcZ        (face-on, normal view)
                // At 90°: ledX=4±(srcZ−4), ledZ=4  (seen edge-on from above)
                // At180°: ledX=4, ledZ=8−srcZ+0    (back face, Z mirrored)
                case AXIS_Y:
                    lX = (int8_t)(((dZ * s + 64) >> 7) + BS_CENTER);
                    lY = srcY;   // Y unchanged — rotation axis
                    lZ = (int8_t)(((dZ * c + 64) >> 7) + BS_CENTER);
                    if (lX >= 0 && lX < CUBE_SIZE &&
                        lY >= 0 && lY < CUBE_SIZE &&
                        lZ >= 0 && lZ < CUBE_SIZE) {
                        DrawPixel((uint8_t)lX, (uint8_t)lY, (uint8_t)lZ, color);
                    }
                    break;

                // ── AXIS_Z — forward somersault, sweeps in XY plane ──────────
                // Rotation around Z: dx′ = dY·s  (dx was 0 — bitmap at X=4)
                //                    dy′ = dY·c
                // Z coordinate is unchanged (it's the rotation axis).
                // At  0°: ledX=4, ledY=srcY        (face-on, normal view)
                // At 90°: ledX=4+(srcY−4), ledY=4  (card now lies flat in XZ)
                // At180°: ledX=4, ledY=8−srcY+0    (back face, Y mirrored)
                case AXIS_Z:
                    lX = (int8_t)(((dY * s + 64) >> 7) + BS_CENTER);
                    lY = (int8_t)(((dY * c + 64) >> 7) + BS_CENTER);
                    lZ = srcZ;   // Z unchanged — rotation axis
                    if (lX >= 0 && lX < CUBE_SIZE &&
                        lY >= 0 && lY < CUBE_SIZE &&
                        lZ >= 0 && lZ < CUBE_SIZE) {
                        DrawPixel((uint8_t)lX, (uint8_t)lY, (uint8_t)lZ, color);
                    }
                    break;
            }
        }
    }
}

// ── Public API ────────────────────────────────────────────────────────────────

// Rotate bitmap 360° in `steps` steps, fixed colour.
//   steps=20  → 18° per step  (smooth on a 10×10 cube)
//   rotAxis   → AXIS_X (pinwheel), AXIS_Y (revolving door), AXIS_Z (somersault)
void bitmap_spin(const uint16_t bmp[9],
                 uint8_t   steps,
                 uint16_t  stepDelay,
                 CRGB      color,
                 uint8_t   rotAxis = AXIS_X)
{
    for (uint8_t i = 0; i < steps; i++) {
        uint8_t angle8 = (uint8_t)((uint16_t)i * 256 / steps);
        _bs_render_step(bmp, angle8, rotAxis, color);
        FastLED.show();
        FastLED.delay(stepDelay);
    }
    FastLED.clear();
    FastLED.show();
}

// Rotate 360° with hue tracking the rotation angle.
//   startHue : initial HSV hue (0–255)
void bitmap_spin_rainbow(const uint16_t bmp[9],
                         uint8_t  steps,
                         uint16_t stepDelay,
                         uint8_t  rotAxis  = AXIS_X,
                         uint8_t  startHue = 0)
{
    for (uint8_t i = 0; i < steps; i++) {
        uint8_t angle8 = (uint8_t)((uint16_t)i * 256 / steps);
        CRGB    color  = CHSV(startHue + angle8, 255, 220);
        _bs_render_step(bmp, angle8, rotAxis, color);
        FastLED.show();
        FastLED.delay(stepDelay);
    }
    FastLED.clear();
    FastLED.show();
}

// Rotate `revolutions` full turns, fixed colour.
void bitmap_spin_repeat(const uint16_t bmp[9],
                        uint8_t  steps,
                        uint16_t stepDelay,
                        CRGB     color,
                        uint8_t  revolutions = 1,
                        uint8_t  rotAxis     = AXIS_X)
{
    for (uint8_t r = 0; r < revolutions; r++)
        bitmap_spin(bmp, steps, stepDelay, color, rotAxis);
}

void bitmap_spin_rainbow_repeat(const uint16_t bmp[9],
                         uint8_t  steps,
                         uint16_t stepDelay,
                         uint8_t  rotAxis  = AXIS_X,
                         uint8_t  revolutions = 1,
                         uint8_t  startHue = 0)
{
    for (uint8_t i = 0; i < steps; i++) {
        uint8_t angle8 = (uint8_t)((uint16_t)i * 256 / steps);
        CRGB    color  = CHSV(startHue + angle8, 255, 220);
        _bs_render_step(bmp, angle8, rotAxis, color);
        FastLED.show();
        FastLED.delay(stepDelay);
    }
    FastLED.clear();
    FastLED.show();
}


#endif // BITMAP_SPIN_H
