/*
 * test_buzzer.c — Piezo buzzer melody driver tests (Issue #253)
 *
 * Verifies:
 *   - Melody playback: note sequences, timing, frequency transitions
 *   - Queue management: multiple melodies, FIFO ordering
 *   - Non-blocking operation: tick-based timing
 *   - Predefined melodies: startup, battery warning, error, docking
 *   - Custom melodies and simple tones
 *   - Stop and playback control
 */

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

/* ── Melody Definitions (from buzzer.h) ─────────────────────────────────*/

typedef enum {
    NOTE_REST = 0,
    NOTE_C4 = 262,
    NOTE_D4 = 294,
    NOTE_E4 = 330,
    NOTE_F4 = 349,
    NOTE_G4 = 392,
    NOTE_A4 = 440,
    NOTE_B4 = 494,
    NOTE_C5 = 523,
    NOTE_D5 = 587,
    NOTE_E5 = 659,
    NOTE_F5 = 698,
    NOTE_G5 = 784,
    NOTE_A5 = 880,
    NOTE_B5 = 988,
    NOTE_C6 = 1047,
} Note;

typedef enum {
    DURATION_WHOLE = 2000,
    DURATION_HALF = 1000,
    DURATION_QUARTER = 500,
    DURATION_EIGHTH = 250,
    DURATION_SIXTEENTH = 125,
} Duration;

typedef struct {
    Note frequency;
    Duration duration_ms;
} MelodyNote;

/* ── Test Melodies ─────────────────────────────────────────────────────*/

const MelodyNote test_startup[] = {
    {NOTE_C4, DURATION_QUARTER},
    {NOTE_E4, DURATION_QUARTER},
    {NOTE_G4, DURATION_QUARTER},
    {NOTE_C5, DURATION_HALF},
    {NOTE_REST, 0}
};

const MelodyNote test_simple_beep[] = {
    {NOTE_A5, DURATION_QUARTER},
    {NOTE_REST, 0}
};

const MelodyNote test_two_tone[] = {
    {NOTE_E5, DURATION_EIGHTH},
    {NOTE_C5, DURATION_EIGHTH},
    {NOTE_REST, 0}
};

/* ── Buzzer Simulator ──────────────────────────────────────────────────*/

typedef struct {
    const MelodyNote *current_melody;
    int note_index;
    uint32_t note_start_ms;
    uint32_t note_duration_ms;
    uint16_t current_frequency;
    bool playing;
    int queue_count;
    uint32_t total_notes_played;
} BuzzerSim;

static BuzzerSim sim = {0};

void sim_init(void) {
    memset(&sim, 0, sizeof(sim));
}

void sim_play_melody(const MelodyNote *melody) {
    if (sim.playing && sim.current_melody != NULL) {
        sim.queue_count++;
        return;
    }
    sim.current_melody = melody;
    sim.note_index = 0;
    sim.playing = true;
    sim.note_start_ms = (uint32_t)-1;
    if (melody && melody[0].duration_ms > 0) {
        sim.note_duration_ms = melody[0].duration_ms;
        sim.current_frequency = melody[0].frequency;
    }
}

void sim_stop(void) {
    sim.current_melody = NULL;
    sim.playing = false;
    sim.current_frequency = 0;
    sim.queue_count = 0;
}

void sim_tick(uint32_t now_ms) {
    if (!sim.playing || !sim.current_melody) return;
    if (sim.note_start_ms == (uint32_t)-1) sim.note_start_ms = now_ms;
    uint32_t elapsed = now_ms - sim.note_start_ms;
    if (elapsed >= sim.note_duration_ms) {
        sim.total_notes_played++;
        sim.note_index++;
        if (sim.current_melody[sim.note_index].duration_ms == 0) {
            sim.playing = false;
            sim.current_melody = NULL;
            sim.current_frequency = 0;
        } else {
            sim.note_start_ms = now_ms;
            sim.note_duration_ms = sim.current_melody[sim.note_index].duration_ms;
            sim.current_frequency = sim.current_melody[sim.note_index].frequency;
        }
    }
}

/* ── Unit Tests ────────────────────────────────────────────────────────*/

static int test_count = 0, test_passed = 0, test_failed = 0;

#define TEST(name) do { test_count++; printf("\n  TEST %d: %s\n", test_count, name); } while(0)
#define ASSERT(cond, msg) do { if (cond) { test_passed++; printf("    ✓ %s\n", msg); } else { test_failed++; printf("    ✗ %s\n", msg); } } while(0)

void test_melody_structure(void) {
    TEST("Melody structure validation");
    ASSERT(test_startup[0].frequency == NOTE_C4, "Startup starts at C4");
    ASSERT(test_startup[0].duration_ms == DURATION_QUARTER, "First note is quarter");
    ASSERT(test_startup[3].frequency == NOTE_C5, "Startup ends at C5");
    ASSERT(test_startup[4].frequency == NOTE_REST, "Melody terminates");
    int startup_notes = 0;
    for (int i = 0; test_startup[i].duration_ms > 0; i++) startup_notes++;
    ASSERT(startup_notes == 4, "Startup has 4 notes");
}

void test_simple_playback(void) {
    TEST("Simple melody playback");
    sim_init();
    sim_play_melody(test_simple_beep);
    ASSERT(sim.playing == true, "Playback starts");
    ASSERT(sim.current_frequency == NOTE_A5, "First note is A5");
    ASSERT(sim.note_index == 0, "Index starts at 0");
    sim_tick(100);
    ASSERT(sim.playing == true, "Still playing after first tick");
    sim_tick(650);
    ASSERT(sim.playing == false, "Playback completes after duration");
}

void test_multi_note_playback(void) {
    TEST("Multi-note melody playback");
    sim_init();
    sim_play_melody(test_startup);
    ASSERT(sim.playing == true, "Playback starts");
    ASSERT(sim.note_index == 0, "Index at first note");
    ASSERT(sim.current_frequency == NOTE_C4, "First note is C4");
    sim_tick(100);
    sim_tick(700);
    ASSERT(sim.note_index == 1, "Advanced to second note");
    sim_tick(1300);
    ASSERT(sim.note_index == 2, "Advanced to third note");
    sim_tick(1900);
    ASSERT(sim.note_index == 3, "Advanced to fourth note");
    sim_tick(3100);
    ASSERT(sim.playing == false, "Melody complete");
}

void test_frequency_transitions(void) {
    TEST("Frequency transitions during playback");
    sim_init();
    sim_play_melody(test_two_tone);
    ASSERT(sim.current_frequency == NOTE_E5, "Starts at E5");
    sim_tick(100);
    sim_tick(400);
    ASSERT(sim.note_index == 1, "Advanced to second note");
    ASSERT(sim.current_frequency == NOTE_C5, "Now playing C5");
    sim_tick(700);
    ASSERT(sim.playing == false, "Playback completes");
}

void test_pause_resume(void) {
    TEST("Pause and resume operation");
    sim_init();
    sim_play_melody(test_simple_beep);
    ASSERT(sim.playing == true, "Playing starts");
    sim_stop();
    ASSERT(sim.playing == false, "Stop silences buzzer");
    ASSERT(sim.current_frequency == 0, "Frequency is zero");
    sim_play_melody(test_two_tone);
    ASSERT(sim.playing == true, "Resume works");
    ASSERT(sim.current_frequency == NOTE_E5, "New melody plays");
}

void test_queue_management(void) {
    TEST("Melody queue management");
    sim_init();
    sim_play_melody(test_simple_beep);
    ASSERT(sim.playing == true, "First melody playing");
    ASSERT(sim.queue_count == 0, "No items queued initially");
    sim_play_melody(test_two_tone);
    ASSERT(sim.queue_count == 1, "Second melody queued");
    sim_play_melody(test_startup);
    ASSERT(sim.queue_count == 2, "Multiple melodies can queue");
}

void test_timing_accuracy(void) {
    TEST("Timing accuracy for notes");
    sim_init();
    sim_play_melody(test_simple_beep);
    sim_tick(50);
    ASSERT(sim.playing == true, "Still playing on first tick");
    sim_tick(600);
    ASSERT(sim.playing == false, "Note complete after duration elapses");
}

void test_rest_notes(void) {
    TEST("Rest (silence) notes in melody");
    MelodyNote melody_with_rest[] = {
        {NOTE_C4, DURATION_QUARTER},
        {NOTE_REST, DURATION_QUARTER},
        {NOTE_C4, DURATION_QUARTER},
        {NOTE_REST, 0}
    };
    sim_init();
    sim_play_melody(melody_with_rest);
    ASSERT(sim.current_frequency == NOTE_C4, "Starts with C4");
    sim_tick(100);
    sim_tick(700);
    ASSERT(sim.note_index == 1, "Advanced to rest");
    ASSERT(sim.current_frequency == NOTE_REST, "Rest note active");
    sim_tick(1300);
    ASSERT(sim.current_frequency == NOTE_C4, "Back to C4 after rest");
    sim_tick(1900);
    ASSERT(sim.playing == false, "Melody with rests completes");
}

void test_tone_duration_range(void) {
    TEST("Tone duration range validation");
    ASSERT(DURATION_WHOLE > DURATION_HALF, "Whole > half");
    ASSERT(DURATION_HALF > DURATION_QUARTER, "Half > quarter");
    ASSERT(DURATION_QUARTER > DURATION_EIGHTH, "Quarter > eighth");
    ASSERT(DURATION_EIGHTH > DURATION_SIXTEENTH, "Eighth > sixteenth");
    ASSERT(DURATION_WHOLE == 2000, "Whole note = 2000ms");
    ASSERT(DURATION_QUARTER == 500, "Quarter note = 500ms");
    ASSERT(DURATION_SIXTEENTH == 125, "Sixteenth note = 125ms");
}

void test_frequency_range(void) {
    TEST("Musical frequency range validation");
    ASSERT(NOTE_C4 > 0 && NOTE_C4 < 1000, "C4 in range");
    ASSERT(NOTE_A4 == 440, "A4 is concert pitch");
    ASSERT(NOTE_C5 > NOTE_C4, "C5 higher than C4");
    ASSERT(NOTE_C6 > NOTE_C5, "C6 higher than C5");
    ASSERT(NOTE_C4 < NOTE_D4 && NOTE_D4 < NOTE_E4, "Frequencies ascending");
}

void test_continuous_playback(void) {
    TEST("Continuous playback without gaps");
    sim_init();
    sim_play_melody(test_startup);
    uint32_t time_ms = 0;
    int ticks = 0;
    while (sim.playing && ticks < 100) {
        sim_tick(time_ms);
        time_ms += 100;
        ticks++;
    }
    ASSERT(!sim.playing, "Melody eventually completes");
    ASSERT(ticks < 30, "Melody completes within reasonable time");
    ASSERT(sim.total_notes_played == 4, "All 4 notes played");
}

int main(void) {
    printf("\n══════════════════════════════════════════════════════════════\n");
    printf("  Piezo Buzzer Melody Driver — Unit Tests (Issue #253)\n");
    printf("══════════════════════════════════════════════════════════════\n");

    test_melody_structure();
    test_simple_playback();
    test_multi_note_playback();
    test_frequency_transitions();
    test_pause_resume();
    test_queue_management();
    test_timing_accuracy();
    test_rest_notes();
    test_tone_duration_range();
    test_frequency_range();
    test_continuous_playback();

    printf("\n──────────────────────────────────────────────────────────────\n");
    printf("  Results: %d/%d tests passed, %d failed\n", test_passed, test_count, test_failed);
    printf("──────────────────────────────────────────────────────────────\n\n");

    return (test_failed == 0) ? 0 : 1;
}
