#include "ws2812_status.h"

#include "dcdc_controller.h"
#include "led_strip.h"
#include "esp_check.h"
#include "esp_log.h"
#include "sdkconfig.h"

#ifndef CONFIG_WATCH_WS2812_LED_COUNT
#define CONFIG_WATCH_WS2812_LED_COUNT 5
#endif
#ifndef CONFIG_WATCH_WS2812_GPIO
#define CONFIG_WATCH_WS2812_GPIO 8
#endif
#ifndef CONFIG_WATCH_WS2812_RMT_RESOLUTION
#define CONFIG_WATCH_WS2812_RMT_RESOLUTION (10 * 1000 * 1000)
#endif

#define WS2812_STATUS_GPIO ((gpio_num_t)CONFIG_WATCH_WS2812_GPIO)
#define WS2812_STATUS_RESOLUTION_HZ CONFIG_WATCH_WS2812_RMT_RESOLUTION

static const char *TAG = "ws2812";

static led_strip_handle_t s_strip;
static bool s_led_state[WS2812_STATUS_LED_COUNT];
static bool s_error_state;
static size_t s_active_channel = SIZE_MAX;

static esp_err_t ws2812_status_apply(void)
{
    if (!s_strip) {
        return ESP_ERR_INVALID_STATE;
    }

    for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) {
        uint8_t r = 0, g = 0, b = 0;
        if (s_error_state) {
            r = 40;
        } else if (s_active_channel == i) {
            g = 60;
        } else if (s_led_state[i]) {
            g = 18;
        } else {
            b = 10;
        }
        ESP_RETURN_ON_ERROR(led_strip_set_pixel(s_strip, i, r, g, b), TAG,
                            "led pixel set failed");
    }

    return led_strip_refresh(s_strip);
}

esp_err_t ws2812_status_init(void)
{
    if (s_strip) {
        return ESP_OK;
    }

    led_strip_config_t strip_config = {
        .strip_gpio_num = WS2812_STATUS_GPIO,
        .max_leds = WS2812_STATUS_LED_COUNT,
        .led_model = LED_MODEL_WS2812,
        .flags.invert_out = false,
    };

    led_strip_rmt_config_t rmt_cfg = {
        .clk_src = RMT_CLK_SRC_DEFAULT,
        .resolution_hz = WS2812_STATUS_RESOLUTION_HZ,
        .flags.with_dma = false,
    };

    ESP_RETURN_ON_ERROR(
        led_strip_new_rmt_device(&strip_config, &rmt_cfg, &s_strip),
        TAG,
        "Не вдалося створити RMT пристрій для WS2812");
    ESP_RETURN_ON_ERROR(led_strip_clear(s_strip), TAG, "clear fail");

    for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) {
        s_led_state[i] = false;
    }
    s_active_channel = SIZE_MAX;
    s_error_state = false;

    return ws2812_status_apply();
}

esp_err_t ws2812_status_set_channel_state(size_t channel, bool enabled)
{
    if (channel >= WS2812_STATUS_LED_COUNT) {
        return ESP_ERR_INVALID_ARG;
    }
    s_led_state[channel] = enabled;
    return ws2812_status_apply();
}

esp_err_t ws2812_status_mark_active(size_t channel)
{
    if (channel >= WS2812_STATUS_LED_COUNT) {
        return ESP_ERR_INVALID_ARG;
    }
    s_active_channel = channel;
    return ws2812_status_apply();
}

esp_err_t ws2812_status_clear_active(void)
{
    s_active_channel = SIZE_MAX;
    return ws2812_status_apply();
}

esp_err_t ws2812_status_set_error(bool has_error)
{
    s_error_state = has_error;
    if (has_error) {
        s_active_channel = SIZE_MAX;
    }
    return ws2812_status_apply();
}

esp_err_t ws2812_status_refresh_from_dcdc(void)
{
    const size_t available_channels = dcdc_channel_count();
    const size_t count = available_channels < WS2812_STATUS_LED_COUNT
                             ? available_channels
                             : WS2812_STATUS_LED_COUNT;

    for (size_t i = 0; i < count; ++i) {
        s_led_state[i] = dcdc_get_state(i);
    }
    for (size_t i = count; i < WS2812_STATUS_LED_COUNT; ++i) {
        s_led_state[i] = false;
    }
    return ws2812_status_apply();
}
