#include "betteryread.h"

#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_timer.h"
#include <stdbool.h>

static const char *tag_s = "BATT_ADC";

static adc_oneshot_unit_handle_t adc_handle_s;
static adc_cali_handle_t adc_cali_s;
static adc_channel_t adc_channel_s;
static adc_unit_t adc_unit_s;
static bool adc_ready_s;
static bool adc_cali_ready_s;
static float voltage_cache_v_s;
static int64_t voltage_cache_ts_us_s;
static bool voltage_cache_valid_s;

#define BATT_READ_MIN_INTERVAL_US  (2500LL * 1000LL)
#define BATT_FILTER_ALPHA          (0.35f)

esp_err_t board_adc_init(void)
{
    esp_err_t err = adc_oneshot_io_to_channel(ADC_GET_VOLTAGE_GPIO, &adc_unit_s, &adc_channel_s);
    if (err != ESP_OK) {
        ESP_LOGE(tag_s, "gpio%d no ADC channel: %s", ADC_GET_VOLTAGE_GPIO, esp_err_to_name(err));
        return err;
    }

    adc_oneshot_unit_init_cfg_t unit_cfg = {
        .unit_id = adc_unit_s,
        .ulp_mode = ADC_ULP_MODE_DISABLE,
    };
    err = adc_oneshot_new_unit(&unit_cfg, &adc_handle_s);
    if (err != ESP_OK) {
        ESP_LOGE(tag_s, "adc new unit fail: %s", esp_err_to_name(err));
        return err;
    }

    adc_oneshot_chan_cfg_t chan_cfg = {
        .atten = ADC_ATTEN_DB_12,
        .bitwidth = ADC_BITWIDTH_DEFAULT,
    };
    err = adc_oneshot_config_channel(adc_handle_s, adc_channel_s, &chan_cfg);
    if (err != ESP_OK) {
        ESP_LOGE(tag_s, "adc cfg channel fail: %s", esp_err_to_name(err));
        return err;
    }

    adc_cali_curve_fitting_config_t cali_cfg = {
        .unit_id = adc_unit_s,
        .atten = chan_cfg.atten,
        .bitwidth = chan_cfg.bitwidth,
    };
    err = adc_cali_create_scheme_curve_fitting(&cali_cfg, &adc_cali_s);
    if (err == ESP_OK) {
        adc_cali_ready_s = true;
    } else {
        adc_cali_ready_s = false;
        ESP_LOGW(tag_s, "adc calibration unavailable, fallback raw");
    }

    adc_ready_s = true;
    ESP_LOGI(tag_s, "ADC ready gpio=%d unit=%d ch=%d", ADC_GET_VOLTAGE_GPIO, adc_unit_s, adc_channel_s);
    return ESP_OK;
}

float board_get_voltage_v(void)
{
    if (!adc_ready_s) {
        return 0.0f;
    }

    int64_t now_us = esp_timer_get_time();
    if (voltage_cache_valid_s && (now_us - voltage_cache_ts_us_s) < BATT_READ_MIN_INTERVAL_US) {
        return voltage_cache_v_s;
    }

    const int sample_count = 16;
    int raw_sum = 0;
    int valid_count = 0;
    for (int i = 0; i < sample_count; ++i) {
        int raw = 0;
        if (adc_oneshot_read(adc_handle_s, adc_channel_s, &raw) != ESP_OK) {
            continue;
        }
        raw_sum += raw;
        ++valid_count;
    }
    if (valid_count == 0) {
        return 0.0f;
    }
    int raw_avg = raw_sum / valid_count;

    int adc_mv = 0;
    if (adc_cali_ready_s) {
        if (adc_cali_raw_to_voltage(adc_cali_s, raw_avg, &adc_mv) != ESP_OK) {
            adc_mv = (raw_avg * 3300) / 4095;
        }
    } else {
        adc_mv = (raw_avg * 3300) / 4095;
    }

    const float v_adc = (float)adc_mv / 1000.0f;
    /* 分压系数 = (R上拉 + R下拉) / R下拉 = (100k + 10k) / 10k = 11 */
    float measured_v = v_adc * BOARD_VOLTAGE_DIVIDER_GAIN;

    if (!voltage_cache_valid_s) {
        voltage_cache_v_s = measured_v;
        voltage_cache_valid_s = true;
    } else {
        voltage_cache_v_s = (BATT_FILTER_ALPHA * measured_v) +
                            ((1.0f - BATT_FILTER_ALPHA) * voltage_cache_v_s);
    }
    voltage_cache_ts_us_s = now_us;
    return voltage_cache_v_s;
}