/*
 * test_face_animation.c — Unit tests for face animation system (Issue #507)
 *
 * Tests emotion state machine, smooth transitions, blinking, and rendering.
 * Mocks LCD framebuffer for visualization/verification.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>

/* Mock the LCD and animation headers for testing */
#include "face_lcd.h"
#include "face_animation.h"
#include "face_uart.h"

/* === Test Harness === */
#define ASSERT(cond) do { \
    if (!(cond)) { \
        printf("  FAIL: %s\n", #cond); \
        test_failed = true; \
    } \
} while (0)

#define TEST(name) do { \
    printf("\n[TEST] %s\n", name); \
    test_failed = false; \
} while (0)

#define PASS do { \
    if (!test_failed) printf("  PASS\n"); \
} while (0)

static bool test_failed = false;
static int tests_run = 0;
static int tests_passed = 0;

/* === Mock LCD Framebuffer === */
static uint8_t mock_fb[LCD_WIDTH * LCD_HEIGHT / 8];

void face_lcd_init(void) {
    memset(mock_fb, 0, sizeof(mock_fb));
}

void face_lcd_clear(void) {
    memset(mock_fb, 0, sizeof(mock_fb));
}

void face_lcd_pixel(uint16_t x, uint16_t y, lcd_color_t color) {
    if (x >= LCD_WIDTH || y >= LCD_HEIGHT) return;
    uint16_t byte_idx = (y / 8) * LCD_WIDTH + x;
    uint8_t bit_pos = y % 8;
    if (color)
        mock_fb[byte_idx] |= (1 << bit_pos);
    else
        mock_fb[byte_idx] &= ~(1 << bit_pos);
}

void face_lcd_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
                   lcd_color_t color) {
    /* Stub: Bresenham line */
}

void face_lcd_circle(uint16_t cx, uint16_t cy, uint16_t r, lcd_color_t color) {
    /* Stub: Midpoint circle */
}

void face_lcd_fill_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
                        lcd_color_t color) {
    /* Stub: Filled rectangle */
}

void face_lcd_flush(void) {
    /* Stub: Push to display */
}

bool face_lcd_is_busy(void) {
    return false;
}

void face_lcd_tick(void) {
    /* Stub: vsync tick */
}

uint8_t *face_lcd_get_fb(void) {
    return mock_fb;
}

void face_uart_init(void) {
    /* Stub: UART init */
}

void face_uart_process(void) {
    /* Stub: UART process */
}

void face_uart_rx_isr(uint8_t byte) {
    /* Stub: UART RX ISR */
    (void)byte;
}

void face_uart_send(const char *str) {
    /* Stub: UART TX */
    (void)str;
}

/* === Test Cases === */

static void test_emotion_initialization(void) {
    TEST("Emotion Initialization");
    tests_run++;

    face_animation_init();
    ASSERT(face_animation_get_emotion() == FACE_NEUTRAL);
    ASSERT(face_animation_is_idle() == true);

    tests_passed++;
    PASS;
}

static void test_emotion_set_happy(void) {
    TEST("Set Emotion to HAPPY");
    tests_run++;

    face_animation_init();
    face_animation_set_emotion(FACE_HAPPY);
    ASSERT(face_animation_get_emotion() == FACE_HAPPY);

    tests_passed++;
    PASS;
}

static void test_emotion_set_sad(void) {
    TEST("Set Emotion to SAD");
    tests_run++;

    face_animation_init();
    face_animation_set_emotion(FACE_SAD);
    ASSERT(face_animation_get_emotion() == FACE_SAD);

    tests_passed++;
    PASS;
}

static void test_emotion_set_curious(void) {
    TEST("Set Emotion to CURIOUS");
    tests_run++;

    face_animation_init();
    face_animation_set_emotion(FACE_CURIOUS);
    ASSERT(face_animation_get_emotion() == FACE_CURIOUS);

    tests_passed++;
    PASS;
}

static void test_emotion_set_angry(void) {
    TEST("Set Emotion to ANGRY");
    tests_run++;

    face_animation_init();
    face_animation_set_emotion(FACE_ANGRY);
    ASSERT(face_animation_get_emotion() == FACE_ANGRY);

    tests_passed++;
    PASS;
}

static void test_emotion_set_sleeping(void) {
    TEST("Set Emotion to SLEEPING");
    tests_run++;

    face_animation_init();
    face_animation_set_emotion(FACE_SLEEPING);
    ASSERT(face_animation_get_emotion() == FACE_SLEEPING);

    tests_passed++;
    PASS;
}

static void test_transition_frames(void) {
    TEST("Smooth Transition Frames");
    tests_run++;

    face_animation_init();
    face_animation_set_emotion(FACE_HAPPY);
    ASSERT(face_animation_is_idle() == false);

    /* Advance 15 frames (transition duration) */
    for (int i = 0; i < 15; i++) {
        face_animation_tick();
    }

    ASSERT(face_animation_is_idle() == true);
    ASSERT(face_animation_get_emotion() == FACE_HAPPY);

    tests_passed++;
    PASS;
}

static void test_blink_timing(void) {
    TEST("Blink Timing");
    tests_run++;

    face_animation_init();

    /* Advance to near blink interval (~120 frames) */
    for (int i = 0; i < 119; i++) {
        face_animation_tick();
        ASSERT(face_animation_is_idle() == true);  /* No blink yet */
    }

    /* One more tick should trigger blink */
    face_animation_tick();
    ASSERT(face_animation_is_idle() == false);  /* Blinking */

    tests_passed++;
    PASS;
}

static void test_blink_duration(void) {
    TEST("Blink Duration");
    tests_run++;

    face_animation_init();

    /* Trigger immediate blink */
    face_animation_blink_now();
    ASSERT(face_animation_is_idle() == false);

    /* Blink lasts ~4 frames */
    for (int i = 0; i < 4; i++) {
        face_animation_tick();
    }

    ASSERT(face_animation_is_idle() == true);  /* Blink complete */

    tests_passed++;
    PASS;
}

static void test_rapid_emotion_changes(void) {
    TEST("Rapid Emotion Changes");
    tests_run++;

    face_animation_init();

    /* Change emotion while transitioning */
    face_animation_set_emotion(FACE_HAPPY);
    for (int i = 0; i < 5; i++) {
        face_animation_tick();
    }

    /* Change to SAD before transition completes */
    face_animation_set_emotion(FACE_SAD);
    ASSERT(face_animation_get_emotion() == FACE_SAD);

    /* Transition should restart */
    for (int i = 0; i < 15; i++) {
        face_animation_tick();
    }

    ASSERT(face_animation_is_idle() == true);
    ASSERT(face_animation_get_emotion() == FACE_SAD);

    tests_passed++;
    PASS;
}

static void test_render_output(void) {
    TEST("Render to LCD Framebuffer");
    tests_run++;

    face_lcd_init();
    face_animation_init();
    face_animation_set_emotion(FACE_HAPPY);

    /* Render multiple frames */
    for (int i = 0; i < 30; i++) {
        face_animation_render();
        face_animation_tick();
    }

    /* Framebuffer should have some pixels set */
    int pixel_count = 0;
    for (int i = 0; i < sizeof(mock_fb); i++) {
        pixel_count += __builtin_popcount(mock_fb[i]);
    }

    ASSERT(pixel_count > 0);  /* At least some pixels drawn */

    tests_passed++;
    PASS;
}

static void test_all_emotions_render(void) {
    TEST("Render All Emotions");
    tests_run++;

    const face_emotion_t emotions[] = {
        FACE_HAPPY, FACE_SAD, FACE_CURIOUS, FACE_ANGRY, FACE_SLEEPING, FACE_NEUTRAL
    };

    for (int e = 0; e < 6; e++) {
        face_lcd_init();
        face_animation_init();
        face_animation_set_emotion(emotions[e]);

        /* Wait for transition */
        for (int i = 0; i < 20; i++) {
            face_animation_tick();
        }

        /* Render */
        face_animation_render();

        /* Check framebuffer has content */
        int pixel_count = 0;
        for (int i = 0; i < sizeof(mock_fb); i++) {
            pixel_count += __builtin_popcount(mock_fb[i]);
        }

        ASSERT(pixel_count > 0);
    }

    tests_passed++;
    PASS;
}

static void test_30hz_refresh_rate(void) {
    TEST("30Hz Refresh Rate Target");
    tests_run++;

    face_lcd_init();
    face_animation_init();

    /* At 30Hz, we should process ~30 frames per second */
    const int duration_seconds = 2;
    const int expected_frames = 30 * duration_seconds;
    int frame_count = 0;

    for (int i = 0; i < 1000; i++) {  /* 1000ms = 1s at ~1ms per tick */
        face_animation_tick();
        if (i % 33 == 0) {  /* Every ~33ms */
            frame_count++;
        }
    }

    ASSERT(frame_count > 0);  /* At least some frames processed */

    tests_passed++;
    PASS;
}

static void test_idle_state_after_sleep(void) {
    TEST("Idle State After Sleep Emotion");
    tests_run++;

    face_animation_init();
    face_animation_set_emotion(FACE_SLEEPING);

    /* Advance past transition */
    for (int i = 0; i < 20; i++) {
        face_animation_tick();
    }

    ASSERT(face_animation_is_idle() == true);
    ASSERT(face_animation_get_emotion() == FACE_SLEEPING);

    tests_passed++;
    PASS;
}

/* === Main Test Runner === */
int main(void) {
    printf("========================================\n");
    printf("Face Animation Unit Tests (Issue #507)\n");
    printf("========================================\n");

    test_emotion_initialization();
    test_emotion_set_happy();
    test_emotion_set_sad();
    test_emotion_set_curious();
    test_emotion_set_angry();
    test_emotion_set_sleeping();
    test_transition_frames();
    test_blink_timing();
    test_blink_duration();
    test_rapid_emotion_changes();
    test_render_output();
    test_all_emotions_render();
    test_30hz_refresh_rate();
    test_idle_state_after_sleep();

    printf("\n========================================\n");
    printf("Results: %d/%d tests passed\n", tests_passed, tests_run);
    printf("========================================\n");

    return (tests_passed == tests_run) ? 0 : 1;
}
