#include "status_outputs.h"

#include <stdbool.h>

#include "config.h"
#include "driver/ledc.h"
#include "driver/rmt_encoder.h"
#include "driver/rmt_tx.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

static const char *TAG = "status_outputs";

#define BUZZER_LEDC_TIMER LEDC_TIMER_0
#define BUZZER_LEDC_CHANNEL LEDC_CHANNEL_0
#define BUZZER_LEDC_MODE LEDC_LOW_SPEED_MODE
#define BUZZER_DUTY_RES LEDC_TIMER_10_BIT
#define BUZZER_DUTY_ON 512
#define WS2812_RMT_RESOLUTION_HZ 10000000

static guguji_status_mode_t g_last_mode = GUGUJI_STATUS_BOOT;
static uint32_t g_last_beep_ms = 0;
static bool g_has_mode = false;
static rmt_channel_handle_t g_ws2812_channel = NULL;
static rmt_encoder_handle_t g_ws2812_encoder = NULL;

static const char *status_name(guguji_status_mode_t mode)
{
    switch (mode) {
    case GUGUJI_STATUS_BOOT:
        return "boot";
    case GUGUJI_STATUS_WAITING_HOST:
        return "waiting_host";
    case GUGUJI_STATUS_DISARMED:
        return "disarmed";
    case GUGUJI_STATUS_ARMED:
        return "armed";
    case GUGUJI_STATUS_TIMEOUT:
        return "timeout";
    case GUGUJI_STATUS_ESTOP:
        return "estop";
    case GUGUJI_STATUS_FAULT:
        return "fault";
    case GUGUJI_STATUS_SENSOR_WARN:
        return "sensor_warn";
    default:
        return "unknown";
    }
}

static void buzzer_silence(void)
{
    ledc_set_duty(BUZZER_LEDC_MODE, BUZZER_LEDC_CHANNEL, 0);
    ledc_update_duty(BUZZER_LEDC_MODE, BUZZER_LEDC_CHANNEL);
}

static void buzzer_beep(uint32_t frequency_hz, uint32_t duration_ms)
{
    ledc_set_freq(BUZZER_LEDC_MODE, BUZZER_LEDC_TIMER, frequency_hz);
    ledc_set_duty(BUZZER_LEDC_MODE, BUZZER_LEDC_CHANNEL, BUZZER_DUTY_ON);
    ledc_update_duty(BUZZER_LEDC_MODE, BUZZER_LEDC_CHANNEL);
    vTaskDelay(pdMS_TO_TICKS(duration_ms));
    buzzer_silence();
}

static void buzzer_pattern_for_transition(guguji_status_mode_t mode)
{
    switch (mode) {
    case GUGUJI_STATUS_ARMED:
        buzzer_beep(2200, 70);
        vTaskDelay(pdMS_TO_TICKS(45));
        buzzer_beep(2600, 70);
        break;
    case GUGUJI_STATUS_DISARMED:
        buzzer_beep(1200, 80);
        break;
    case GUGUJI_STATUS_TIMEOUT:
        buzzer_beep(1500, 80);
        vTaskDelay(pdMS_TO_TICKS(60));
        buzzer_beep(1500, 80);
        break;
    case GUGUJI_STATUS_ESTOP:
    case GUGUJI_STATUS_FAULT:
        buzzer_beep(900, 260);
        break;
    case GUGUJI_STATUS_SENSOR_WARN:
        buzzer_beep(1800, 60);
        vTaskDelay(pdMS_TO_TICKS(40));
        buzzer_beep(1000, 60);
        break;
    default:
        break;
    }
}

static void ws2812_write_rgb(uint8_t red, uint8_t green, uint8_t blue)
{
    const uint8_t grb[3] = {green, red, blue};
    const rmt_transmit_config_t tx_config = {
        .loop_count = 0,
        .flags.eot_level = 0,
    };
    ESP_ERROR_CHECK(rmt_transmit(g_ws2812_channel, g_ws2812_encoder, grb, sizeof(grb), &tx_config));
    ESP_ERROR_CHECK(rmt_tx_wait_all_done(g_ws2812_channel, 20));
    // WS2812 需要至少 50us 的低电平复位间隔，状态任务 1ms 足够。
    vTaskDelay(pdMS_TO_TICKS(1));
}

static void led_for_mode(guguji_status_mode_t mode, uint32_t now_ms)
{
    const bool slow_on = ((now_ms / 600) % 2) == 0;
    const bool fast_on = ((now_ms / 180) % 2) == 0;
    const bool double_flash = (now_ms % 1200) < 120 || ((now_ms + 220) % 1200) < 120;

    switch (mode) {
    case GUGUJI_STATUS_BOOT:
        ws2812_write_rgb(24, 24, 24);
        break;
    case GUGUJI_STATUS_WAITING_HOST:
        ws2812_write_rgb(0, 0, slow_on ? 40 : 6);
        break;
    case GUGUJI_STATUS_DISARMED:
        ws2812_write_rgb(0, slow_on ? 26 : 4, slow_on ? 26 : 4);
        break;
    case GUGUJI_STATUS_ARMED:
        ws2812_write_rgb(0, 42, 0);
        break;
    case GUGUJI_STATUS_TIMEOUT:
        ws2812_write_rgb(double_flash ? 45 : 0, double_flash ? 24 : 0, 0);
        break;
    case GUGUJI_STATUS_ESTOP:
    case GUGUJI_STATUS_FAULT:
        ws2812_write_rgb(fast_on ? 55 : 0, 0, 0);
        break;
    case GUGUJI_STATUS_SENSOR_WARN:
        ws2812_write_rgb(slow_on ? 28 : 3, 0, slow_on ? 42 : 6);
        break;
    default:
        ws2812_write_rgb(0, 0, 0);
        break;
    }
}

esp_err_t guguji_status_outputs_init(void)
{
    ledc_timer_config_t timer_config = {
        .speed_mode = BUZZER_LEDC_MODE,
        .duty_resolution = BUZZER_DUTY_RES,
        .timer_num = BUZZER_LEDC_TIMER,
        .freq_hz = 2000,
        .clk_cfg = LEDC_AUTO_CLK,
    };
    ESP_ERROR_CHECK(ledc_timer_config(&timer_config));

    ledc_channel_config_t channel_config = {
        .gpio_num = GUGUJI_BUZZER_GPIO,
        .speed_mode = BUZZER_LEDC_MODE,
        .channel = BUZZER_LEDC_CHANNEL,
        .timer_sel = BUZZER_LEDC_TIMER,
        .duty = 0,
        .hpoint = 0,
    };
    ESP_ERROR_CHECK(ledc_channel_config(&channel_config));

    rmt_tx_channel_config_t ws2812_channel_config = {
        .gpio_num = GUGUJI_WS2812_GPIO,
        .clk_src = RMT_CLK_SRC_DEFAULT,
        .resolution_hz = WS2812_RMT_RESOLUTION_HZ,
        .mem_block_symbols = 64,
        .trans_queue_depth = 2,
    };
    ESP_ERROR_CHECK(rmt_new_tx_channel(&ws2812_channel_config, &g_ws2812_channel));

    rmt_bytes_encoder_config_t ws2812_encoder_config = {
        .bit0 = {
            .level0 = 1,
            .duration0 = 3, // 0.3us
            .level1 = 0,
            .duration1 = 9, // 0.9us
        },
        .bit1 = {
            .level0 = 1,
            .duration0 = 9, // 0.9us
            .level1 = 0,
            .duration1 = 3, // 0.3us
        },
        .flags.msb_first = 1,
    };
    ESP_ERROR_CHECK(rmt_new_bytes_encoder(&ws2812_encoder_config, &g_ws2812_encoder));
    ESP_ERROR_CHECK(rmt_enable(g_ws2812_channel));

    ws2812_write_rgb(0, 0, 0);
    buzzer_beep(1600, 70);
    vTaskDelay(pdMS_TO_TICKS(45));
    buzzer_beep(2200, 70);
    ESP_LOGI(TAG, "蜂鸣器 GPIO%d 与 WS2812 GPIO%d 初始化完成", GUGUJI_BUZZER_GPIO, GUGUJI_WS2812_GPIO);
    return ESP_OK;
}

void guguji_status_outputs_apply(guguji_status_mode_t mode, uint32_t now_ms)
{
    if (!g_has_mode || mode != g_last_mode) {
        ESP_LOGI(TAG, "状态提示切换: %s -> %s", g_has_mode ? status_name(g_last_mode) : "none", status_name(mode));
        buzzer_pattern_for_transition(mode);
        g_last_mode = mode;
        g_has_mode = true;
        g_last_beep_ms = now_ms;
    }

    if ((mode == GUGUJI_STATUS_FAULT || mode == GUGUJI_STATUS_ESTOP) && now_ms - g_last_beep_ms > 1500) {
        buzzer_beep(900, 120);
        g_last_beep_ms = now_ms;
    } else if (mode == GUGUJI_STATUS_TIMEOUT && now_ms - g_last_beep_ms > 2500) {
        buzzer_beep(1500, 70);
        vTaskDelay(pdMS_TO_TICKS(50));
        buzzer_beep(1500, 70);
        g_last_beep_ms = now_ms;
    }

    led_for_mode(mode, now_ms);
}
