#include "buzzer.h"
#include "stm32f7xx_hal.h"
#include "config.h"
#include <string.h>

/* ================================================================
 * Buzzer Hardware Configuration
 * ================================================================ */

#define BUZZER_PIN          GPIO_PIN_8
#define BUZZER_PORT         GPIOA
#define BUZZER_TIM          TIM1
#define BUZZER_TIM_CHANNEL  TIM_CHANNEL_1
#define BUZZER_BASE_FREQ_HZ 1000    /* Base PWM frequency (1kHz) */

/* ================================================================
 * Predefined Melodies
 * ================================================================ */

/* Startup jingle: C-E-G ascending pattern */
const MelodyNote melody_startup[] = {
    {NOTE_C4, DURATION_QUARTER},
    {NOTE_E4, DURATION_QUARTER},
    {NOTE_G4, DURATION_QUARTER},
    {NOTE_C5, DURATION_HALF},
    {NOTE_REST, 0}  /* Terminator */
};

/* Low battery warning: two descending beeps */
const MelodyNote melody_low_battery[] = {
    {NOTE_A5, DURATION_EIGHTH},
    {NOTE_REST, DURATION_EIGHTH},
    {NOTE_A5, DURATION_EIGHTH},
    {NOTE_REST, DURATION_EIGHTH},
    {NOTE_F5, DURATION_EIGHTH},
    {NOTE_REST, DURATION_EIGHTH},
    {NOTE_F5, DURATION_EIGHTH},
    {NOTE_REST, 0}
};

/* Error alert: rapid repeating tone */
const MelodyNote melody_error[] = {
    {NOTE_E5, DURATION_SIXTEENTH},
    {NOTE_REST, DURATION_SIXTEENTH},
    {NOTE_E5, DURATION_SIXTEENTH},
    {NOTE_REST, DURATION_SIXTEENTH},
    {NOTE_E5, DURATION_SIXTEENTH},
    {NOTE_REST, DURATION_SIXTEENTH},
    {NOTE_REST, 0}
};

/* Docking complete: cheerful ascending chime */
const MelodyNote melody_docking_complete[] = {
    {NOTE_C4, DURATION_EIGHTH},
    {NOTE_E4, DURATION_EIGHTH},
    {NOTE_G4, DURATION_EIGHTH},
    {NOTE_C5, DURATION_QUARTER},
    {NOTE_REST, DURATION_QUARTER},
    {NOTE_G4, DURATION_EIGHTH},
    {NOTE_C5, DURATION_HALF},
    {NOTE_REST, 0}
};

/* ================================================================
 * Melody Queue
 * ================================================================ */

#define MELODY_QUEUE_SIZE   4

typedef struct {
    const MelodyNote *notes;    /* Melody sequence pointer */
    uint16_t note_index;        /* Current note in sequence */
    uint32_t note_start_ms;     /* When current note started */
    uint32_t note_duration_ms;  /* Duration of current note */
    uint16_t current_frequency; /* Current tone frequency (Hz) */
    bool is_custom;             /* Is this a custom melody? */
} MelodyPlayback;

typedef struct {
    MelodyPlayback queue[MELODY_QUEUE_SIZE];
    uint8_t write_index;
    uint8_t read_index;
    uint8_t count;
} MelodyQueue;

static MelodyQueue s_queue = {0};
static MelodyPlayback s_current = {0};
static uint32_t s_last_tick_ms = 0;

/* ================================================================
 * Hardware Initialization
 * ================================================================ */

void buzzer_init(void)
{
    /* Enable GPIO and timer clocks */
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_TIM1_CLK_ENABLE();

    /* Configure PA8 as TIM1_CH1 PWM output */
    GPIO_InitTypeDef gpio_init = {0};
    gpio_init.Pin = BUZZER_PIN;
    gpio_init.Mode = GPIO_MODE_AF_PP;
    gpio_init.Pull = GPIO_NOPULL;
    gpio_init.Speed = GPIO_SPEED_HIGH;
    gpio_init.Alternate = GPIO_AF1_TIM1;
    HAL_GPIO_Init(BUZZER_PORT, &gpio_init);

    /* Configure TIM1 for PWM:
     * Clock: 216MHz / PSC = output frequency
     * For 1kHz base frequency: PSC = 216, ARR = 1000
     * Duty cycle = CCR / ARR (e.g., 500/1000 = 50%)
     */
    TIM_HandleTypeDef htim1 = {0};
    htim1.Instance = BUZZER_TIM;
    htim1.Init.Prescaler = 216 - 1;           /* 216MHz / 216 = 1MHz clock */
    htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim1.Init.Period = (1000000 / BUZZER_BASE_FREQ_HZ) - 1;  /* 1kHz = 1000 counts */
    htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim1.Init.RepetitionCounter = 0;
    HAL_TIM_PWM_Init(&htim1);

    /* Configure PWM on CH1: 50% duty cycle initially (silence will be 0%) */
    TIM_OC_InitTypeDef oc_init = {0};
    oc_init.OCMode = TIM_OCMODE_PWM1;
    oc_init.Pulse = 0;                        /* Start at 0% duty (silence) */
    oc_init.OCPolarity = TIM_OCPOLARITY_HIGH;
    oc_init.OCFastMode = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&htim1, &oc_init, BUZZER_TIM_CHANNEL);

    /* Start PWM generation */
    HAL_TIM_PWM_Start(BUZZER_TIM, BUZZER_TIM_CHANNEL);

    /* Initialize queue */
    memset(&s_queue, 0, sizeof(s_queue));
    memset(&s_current, 0, sizeof(s_current));
    s_last_tick_ms = 0;
}

/* ================================================================
 * Public API
 * ================================================================ */

bool buzzer_play_melody(MelodyType melody_type)
{
    const MelodyNote *notes = NULL;

    switch (melody_type) {
        case MELODY_STARTUP:
            notes = melody_startup;
            break;
        case MELODY_LOW_BATTERY:
            notes = melody_low_battery;
            break;
        case MELODY_ERROR:
            notes = melody_error;
            break;
        case MELODY_DOCKING_COMPLETE:
            notes = melody_docking_complete;
            break;
        default:
            return false;
    }

    return buzzer_play_custom(notes);
}

bool buzzer_play_custom(const MelodyNote *notes)
{
    if (!notes || s_queue.count >= MELODY_QUEUE_SIZE) {
        return false;
    }

    MelodyPlayback *playback = &s_queue.queue[s_queue.write_index];
    memset(playback, 0, sizeof(*playback));
    playback->notes = notes;
    playback->note_index = 0;
    playback->is_custom = true;

    s_queue.write_index = (s_queue.write_index + 1) % MELODY_QUEUE_SIZE;
    s_queue.count++;

    return true;
}

bool buzzer_play_tone(uint16_t frequency, uint16_t duration_ms)
{
    if (s_queue.count >= MELODY_QUEUE_SIZE) {
        return false;
    }

    /* Create a simple 2-note melody: tone + rest */
    static MelodyNote temp_notes[3];
    temp_notes[0].frequency = frequency;
    temp_notes[0].duration_ms = duration_ms;
    temp_notes[1].frequency = NOTE_REST;
    temp_notes[1].duration_ms = 0;

    MelodyPlayback *playback = &s_queue.queue[s_queue.write_index];
    memset(playback, 0, sizeof(*playback));
    playback->notes = temp_notes;
    playback->note_index = 0;
    playback->is_custom = true;

    s_queue.write_index = (s_queue.write_index + 1) % MELODY_QUEUE_SIZE;
    s_queue.count++;

    return true;
}

void buzzer_stop(void)
{
    /* Clear queue and current playback */
    memset(&s_queue, 0, sizeof(s_queue));
    memset(&s_current, 0, sizeof(s_current));

    /* Silence buzzer (0% duty cycle) */
    TIM1->CCR1 = 0;
}

bool buzzer_is_playing(void)
{
    return (s_current.notes != NULL) || (s_queue.count > 0);
}

/* ================================================================
 * Timer Update and PWM Frequency Control
 * ================================================================ */

static void buzzer_set_frequency(uint16_t frequency)
{
    if (frequency == 0) {
        /* Silence: 0% duty cycle */
        TIM1->CCR1 = 0;
        return;
    }

    /* Set PWM frequency and 50% duty cycle
     * TIM1 clock: 1MHz (after prescaler)
     * ARR = 1MHz / frequency
     * CCR1 = ARR / 2 (50% duty)
     */
    uint32_t arr = (1000000 / frequency);
    if (arr > 65535) arr = 65535;  /* Clamp to 16-bit */

    TIM1->ARR = arr - 1;
    TIM1->CCR1 = arr / 2;  /* 50% duty cycle for all tones */
}

void buzzer_tick(uint32_t now_ms)
{
    /* Check if current note has finished */
    if (s_current.notes != NULL) {
        uint32_t elapsed = now_ms - s_current.note_start_ms;

        if (elapsed >= s_current.note_duration_ms) {
            /* Move to next note */
            s_current.note_index++;

            if (s_current.notes[s_current.note_index].duration_ms == 0) {
                /* End of melody sequence */
                s_current.notes = NULL;
                buzzer_set_frequency(0);

                /* Start next queued melody if available */
                if (s_queue.count > 0) {
                    s_current = s_queue.queue[s_queue.read_index];
                    s_queue.read_index = (s_queue.read_index + 1) % MELODY_QUEUE_SIZE;
                    s_queue.count--;
                    s_current.note_start_ms = now_ms;
                    s_current.note_duration_ms = s_current.notes[0].duration_ms;
                    buzzer_set_frequency(s_current.notes[0].frequency);
                }
            } else {
                /* Play next note */
                s_current.note_start_ms = now_ms;
                s_current.note_duration_ms = s_current.notes[s_current.note_index].duration_ms;
                uint16_t frequency = s_current.notes[s_current.note_index].frequency;
                buzzer_set_frequency(frequency);
            }
        }
    } else if (s_queue.count > 0 && s_current.notes == NULL) {
        /* Start first queued melody */
        s_current = s_queue.queue[s_queue.read_index];
        s_queue.read_index = (s_queue.read_index + 1) % MELODY_QUEUE_SIZE;
        s_queue.count--;
        s_current.note_start_ms = now_ms;
        s_current.note_duration_ms = s_current.notes[0].duration_ms;
        buzzer_set_frequency(s_current.notes[0].frequency);
    }

    s_last_tick_ms = now_ms;
}
