#include "dcdc_controller.h"

#include "driver/gpio.h"
#include "esp_log.h"
#include "sdkconfig.h"

static const char *TAG = "dcdc";

#ifndef CONFIG_WATCH_DCDC_EN_GPIO_0
#define CONFIG_WATCH_DCDC_EN_GPIO_0 2
#endif
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_1
#define CONFIG_WATCH_DCDC_EN_GPIO_1 4
#endif
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_2
#define CONFIG_WATCH_DCDC_EN_GPIO_2 5
#endif
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_3
#define CONFIG_WATCH_DCDC_EN_GPIO_3 18
#endif
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_4
#define CONFIG_WATCH_DCDC_EN_GPIO_4 19
#endif

static const gpio_num_t s_dcdc_gpio_map[DCDC_CHANNEL_COUNT] = {
    (gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_0,
    (gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_1,
    (gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_2,
    (gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_3,
    (gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_4,
};

static bool s_initialized;
static bool s_channel_state[DCDC_CHANNEL_COUNT];

static bool dcdc_is_channel_valid(dcdc_channel_t channel)
{
    return channel >= DCDC_CHANNEL_0 && channel < DCDC_CHANNEL_COUNT;
}

esp_err_t dcdc_init(void)
{
    if (s_initialized) {
        return ESP_OK;
    }

    gpio_config_t cfg = {
        .intr_type = GPIO_INTR_DISABLE,
        .mode = GPIO_MODE_OUTPUT,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pin_bit_mask = 0,
    };

    for (int i = 0; i < DCDC_CHANNEL_COUNT; ++i) {
        cfg.pin_bit_mask = 1ULL << s_dcdc_gpio_map[i];
        esp_err_t err = gpio_config(&cfg);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Не вдалося налаштувати GPIO %d (канал %d): %s",
                     (int)s_dcdc_gpio_map[i], i, esp_err_to_name(err));
            return err;
        }
        ESP_ERROR_CHECK_WITHOUT_ABORT(gpio_set_level(s_dcdc_gpio_map[i], 0));
        s_channel_state[i] = false;
    }

    s_initialized = true;
    ESP_LOGI(TAG, "DCDC контролер ініціалізовано, каналів: %d", DCDC_CHANNEL_COUNT);
    return ESP_OK;
}

esp_err_t dcdc_set_state(dcdc_channel_t channel, bool enabled)
{
    if (!dcdc_is_channel_valid(channel)) {
        return ESP_ERR_INVALID_ARG;
    }

    esp_err_t err = gpio_set_level(s_dcdc_gpio_map[channel], enabled ? 1 : 0);
    if (err == ESP_OK) {
        s_channel_state[channel] = enabled;
        ESP_LOGI(TAG, "Канал %d -> %s", channel, enabled ? "ON" : "OFF");
    }
    return err;
}

esp_err_t dcdc_enable(dcdc_channel_t channel)
{
    return dcdc_set_state(channel, true);
}

esp_err_t dcdc_disable(dcdc_channel_t channel)
{
    return dcdc_set_state(channel, false);
}

esp_err_t dcdc_toggle(dcdc_channel_t channel)
{
    if (!dcdc_is_channel_valid(channel)) {
        return ESP_ERR_INVALID_ARG;
    }
    return dcdc_set_state(channel, !s_channel_state[channel]);
}

bool dcdc_get_state(dcdc_channel_t channel)
{
    if (!dcdc_is_channel_valid(channel)) {
        return false;
    }
    return s_channel_state[channel];
}

size_t dcdc_channel_count(void)
{
    return DCDC_CHANNEL_COUNT;
}

gpio_num_t dcdc_get_gpio(dcdc_channel_t channel)
{
    if (!dcdc_is_channel_valid(channel)) {
        return GPIO_NUM_NC;
    }
    return s_dcdc_gpio_map[channel];
}
