#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "board_config.h"
#include "buzzer.h"
#include "evse_api.h"

static gpio_num_t buzzer_gpio = GPIO_NUM_NC;
static evse_state_t last_buzzer_state = -1;
static QueueHandle_t buzzer_queue = NULL;

void buzzer_on(void) {
    if (buzzer_gpio != GPIO_NUM_NC)
        gpio_set_level(buzzer_gpio, 1);
}

void buzzer_off(void) {
    if (buzzer_gpio != GPIO_NUM_NC)
        gpio_set_level(buzzer_gpio, 0);
}

// ----------------------
// Padrões de Buzzer
// ----------------------

typedef struct {
    uint16_t on_ms;
    uint16_t off_ms;
} buzzer_pattern_step_t;

typedef enum {
    BUZZER_PATTERN_NONE = 0,
    BUZZER_PATTERN_PLUGGED,
    BUZZER_PATTERN_UNPLUGGED,
    BUZZER_PATTERN_CHARGING,
} buzzer_pattern_id_t;

static const buzzer_pattern_step_t pattern_plugged[] = {
    {100, 100}, {200, 0}
};

static const buzzer_pattern_step_t pattern_unplugged[] = {
    {150, 150}, {150, 150}, {150, 0}
};

static const buzzer_pattern_step_t pattern_charging[] = {
    {80, 150}, {100, 120}, {120, 100}, {140, 0}
};

// ----------------------
// Executor de padrões
// ----------------------

static void buzzer_execute_pattern(buzzer_pattern_id_t pattern_id) {
    const buzzer_pattern_step_t *pattern = NULL;
    size_t length = 0;

    switch (pattern_id) {
        case BUZZER_PATTERN_PLUGGED:
            pattern = pattern_plugged;
            length = sizeof(pattern_plugged) / sizeof(pattern_plugged[0]);
            break;
        case BUZZER_PATTERN_UNPLUGGED:
            pattern = pattern_unplugged;
            length = sizeof(pattern_unplugged) / sizeof(pattern_unplugged[0]);
            break;
        case BUZZER_PATTERN_CHARGING:
            pattern = pattern_charging;
            length = sizeof(pattern_charging) / sizeof(pattern_charging[0]);
            break;
        default:
            return;
    }

    for (size_t i = 0; i < length; i++) {
        buzzer_on();
        vTaskDelay(pdMS_TO_TICKS(pattern[i].on_ms));
        buzzer_off();
        if (pattern[i].off_ms > 0)
            vTaskDelay(pdMS_TO_TICKS(pattern[i].off_ms));
    }
}

// ----------------------
// Task que toca o buzzer
// ----------------------

static void buzzer_worker_task(void *arg) {
    buzzer_pattern_id_t pattern_id;

    while (true) {
        if (xQueueReceive(buzzer_queue, &pattern_id, portMAX_DELAY)) {
            buzzer_execute_pattern(pattern_id);
        }
    }
}

// ----------------------
// Task de monitoramento
// ----------------------

static void buzzer_monitor_task(void *arg) {
    while (true) {
        evse_state_t current = evse_get_state();

        if (current != last_buzzer_state) {
            buzzer_pattern_id_t pattern_id = BUZZER_PATTERN_NONE;

            switch (current) {
                case EVSE_STATE_A:
                    if (last_buzzer_state != EVSE_STATE_A)
                        pattern_id = BUZZER_PATTERN_UNPLUGGED;
                    break;
                case EVSE_STATE_B1:
                case EVSE_STATE_B2:
                    if (last_buzzer_state != EVSE_STATE_B1 && last_buzzer_state != EVSE_STATE_B2)
                        pattern_id = BUZZER_PATTERN_PLUGGED;
                    break;
                case EVSE_STATE_C2:
                case EVSE_STATE_D2:
                    if (last_buzzer_state != EVSE_STATE_C2 && last_buzzer_state != EVSE_STATE_D2)
                        pattern_id = BUZZER_PATTERN_CHARGING;
                    break;
                default:
                    break;
            }

            if (pattern_id != BUZZER_PATTERN_NONE) {
                xQueueSend(buzzer_queue, &pattern_id, 0);  // Não bloqueia
            }

            last_buzzer_state = current;
        }

        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

// ----------------------
// Inicialização
// ----------------------

void buzzer_init(void) {
    if (board_config.buzzer) {
        buzzer_gpio = board_config.buzzer_gpio;

        gpio_config_t io_conf = {
            .pin_bit_mask = BIT64(buzzer_gpio),
            .mode = GPIO_MODE_OUTPUT,
            .pull_down_en = GPIO_PULLDOWN_ENABLE,
            .pull_up_en = GPIO_PULLUP_DISABLE,
            .intr_type = GPIO_INTR_DISABLE
        };
        gpio_config(&io_conf);
        gpio_set_level(buzzer_gpio, 0);
    }

    buzzer_queue = xQueueCreate(4, sizeof(buzzer_pattern_id_t));

    xTaskCreate(buzzer_monitor_task, "buzzer_monitor", 2048, NULL, 5, NULL);
    xTaskCreate(buzzer_worker_task, "buzzer_worker", 2048, NULL, 5, NULL);
}
