#include "veml7700.h"
#include <cstring>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define TAG "VEML7700"

// ============================================================================
// 增益对应的系数表（用于Lux计算）
// ============================================================================
static const float gain_factors[] = {
    1.0f,   // GAIN_1   (x1)
    2.0f,   // GAIN_2   (x2)
    0.125f, // GAIN_D8  (x1/8)
    0.25f,  // GAIN_D4  (x1/4)
};

// 积分时间对应的系数表
// 索引通过 it 枚举值映射
static float GetItFactor(veml7700_it_t it) {
    switch (it) {
        case VEML7700_IT_25MS:  return 0.25f;
        case VEML7700_IT_50MS:  return 0.5f;
        case VEML7700_IT_100MS: return 1.0f;
        case VEML7700_IT_200MS: return 2.0f;
        case VEML7700_IT_400MS: return 4.0f;
        case VEML7700_IT_800MS: return 8.0f;
        default: return 1.0f;
    }
}

// 积分时间对应的等待时间（ms）
static uint32_t GetItDelayMs(veml7700_it_t it) {
    switch (it) {
        case VEML7700_IT_25MS:  return 25;
        case VEML7700_IT_50MS:  return 50;
        case VEML7700_IT_100MS: return 100;
        case VEML7700_IT_200MS: return 200;
        case VEML7700_IT_400MS: return 400;
        case VEML7700_IT_800MS: return 800;
        default: return 100;
    }
}

// ============================================================================
// 构造函数
// ============================================================================
VEML7700::VEML7700(i2c_master_bus_handle_t i2c_bus, uint8_t addr)
    : I2cDevice(i2c_bus, addr),
      gain_(VEML7700_GAIN_1),
      it_(VEML7700_IT_100MS),
      pers_(VEML7700_PERS_1),
      int_enable_(false),
      shutdown_(true),
      als_conf_(0x0001)  // 默认关机状态
{
}

// ============================================================================
// 16-bit 寄存器读写
// VEML7700 协议：写 = [cmd] + [data_low] + [data_high]
//               读 = [cmd] → [data_low] + [data_high]
// ============================================================================
esp_err_t VEML7700::WriteReg16(uint8_t cmd, uint16_t value) {
    uint8_t buffer[3] = {
        cmd,
        (uint8_t)(value & 0xFF),        // 低字节
        (uint8_t)((value >> 8) & 0xFF)  // 高字节
    };
    esp_err_t ret = i2c_master_transmit(i2c_device_, buffer, 3, 100);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "写寄存器 0x%02X 失败: %s", cmd, esp_err_to_name(ret));
    }
    return ret;
}

esp_err_t VEML7700::ReadReg16(uint8_t cmd, uint16_t* value) {
    uint8_t rx[2] = {0};
    esp_err_t ret = i2c_master_transmit_receive(i2c_device_, &cmd, 1, rx, 2, 100);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "读寄存器 0x%02X 失败: %s", cmd, esp_err_to_name(ret));
        *value = 0;
        return ret;
    }
    *value = (uint16_t)(rx[0] | (rx[1] << 8));
    return ESP_OK;
}

// ============================================================================
// 更新ALS配置寄存器
// ALS_CONF 位域：
//   [15:13] 保留
//   [12:11] ALS_GAIN
//   [10]    保留
//   [9:6]   ALS_IT
//   [5:4]   ALS_PERS
//   [3]     保留
//   [2]     ALS_INT_EN
//   [1]     保留
//   [0]     ALS_SD (1=关机, 0=运行)
// ============================================================================
esp_err_t VEML7700::UpdateALSConf() {
    als_conf_ = 0;
    als_conf_ |= ((uint16_t)gain_ & 0x03) << 11;
    als_conf_ |= ((uint16_t)it_ & 0x0F) << 6;
    als_conf_ |= ((uint16_t)pers_ & 0x03) << 4;
    als_conf_ |= int_enable_ ? (1 << 2) : 0;
    als_conf_ |= shutdown_ ? 1 : 0;

    return WriteReg16(VEML7700_REG_ALS_CONF, als_conf_);
}

// ============================================================================
// 初始化
// ============================================================================
esp_err_t VEML7700::Init() {
    ESP_LOGI(TAG, "初始化 VEML7700 环境光传感器...");

    // 先关机再配置（datasheet建议）
    shutdown_ = true;
    esp_err_t ret = UpdateALSConf();
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "VEML7700 初始化失败，I2C通信错误");
        return ret;
    }

    // 设置默认参数：增益x1，积分时间100ms
    gain_ = VEML7700_GAIN_1;
    it_ = VEML7700_IT_100MS;
    pers_ = VEML7700_PERS_1;
    int_enable_ = false;

    // 关闭省电模式
    ret = SetPowerSaving(VEML7700_PSM_1, false);
    if (ret != ESP_OK) return ret;

    // 唤醒传感器
    shutdown_ = false;
    ret = UpdateALSConf();
    if (ret != ESP_OK) return ret;

    // 等待首次采样完成
    vTaskDelay(pdMS_TO_TICKS(GetSampleDelayMs() + 10));

    ESP_LOGI(TAG, "VEML7700 初始化成功 (增益:x1, 积分时间:100ms)");
    return ESP_OK;
}

// ============================================================================
// 配置接口
// ============================================================================
esp_err_t VEML7700::SetGain(veml7700_gain_t gain) {
    gain_ = gain;
    return UpdateALSConf();
}

esp_err_t VEML7700::SetIntegrationTime(veml7700_it_t it) {
    it_ = it;
    return UpdateALSConf();
}

esp_err_t VEML7700::SetPersistence(veml7700_pers_t pers) {
    pers_ = pers;
    return UpdateALSConf();
}

esp_err_t VEML7700::SetInterruptEnable(bool enable) {
    int_enable_ = enable;
    return UpdateALSConf();
}

esp_err_t VEML7700::Shutdown(bool sd) {
    shutdown_ = sd;
    return UpdateALSConf();
}

esp_err_t VEML7700::SetPowerSaving(veml7700_psm_t mode, bool enable) {
    uint16_t psm_val = 0;
    psm_val |= ((uint16_t)mode & 0x03) << 1;
    psm_val |= enable ? 1 : 0;
    return WriteReg16(VEML7700_REG_PSM, psm_val);
}

esp_err_t VEML7700::SetThresholdHigh(uint16_t threshold) {
    return WriteReg16(VEML7700_REG_ALS_WH, threshold);
}

esp_err_t VEML7700::SetThresholdLow(uint16_t threshold) {
    return WriteReg16(VEML7700_REG_ALS_WL, threshold);
}

// ============================================================================
// 数据读取
// ============================================================================
esp_err_t VEML7700::ReadALSRaw(uint16_t* raw) {
    return ReadReg16(VEML7700_REG_ALS, raw);
}

esp_err_t VEML7700::ReadWhiteRaw(uint16_t* raw) {
    return ReadReg16(VEML7700_REG_WHITE, raw);
}

// ============================================================================
// 分辨率计算
// 基准分辨率：增益x1 + 积分时间100ms = 0.0576 lux/count
// 其他配置按比例缩放
// ============================================================================
float VEML7700::GetResolution() const {
    float base_resolution = 0.0576f;  // lux/count @ gain=1, it=100ms
    float g = gain_factors[gain_];
    float t = GetItFactor(it_);
    return base_resolution / (g * t);
}

// ============================================================================
// 非线性校正（Vishay应用笔记推荐的多项式补偿）
// 当 Lux > 1000 时，传感器响应偏离线性，需要校正
// ============================================================================
float VEML7700::CorrectLux(float lux) const {
    // Vishay官方多项式校正公式（Horner法）
    // lux_corrected = lux * (1.0023 + lux * (8.1488e-5 + lux * (-9.3924e-9 + lux * 6.0135e-13)))
    return lux * (1.0023f + lux * (8.1488e-5f + lux * (-9.3924e-9f + lux * 6.0135e-13f)));
}

esp_err_t VEML7700::ReadALSLux(float* lux) {
    uint16_t raw = 0;
    esp_err_t ret = ReadALSRaw(&raw);
    if (ret != ESP_OK) {
        *lux = 0;
        return ret;
    }
    float raw_lux = (float)raw * GetResolution();
    *lux = CorrectLux(raw_lux);
    return ESP_OK;
}

esp_err_t VEML7700::ReadWhiteLux(float* lux) {
    uint16_t raw = 0;
    esp_err_t ret = ReadWhiteRaw(&raw);
    if (ret != ESP_OK) {
        *lux = 0;
        return ret;
    }
    float raw_lux = (float)raw * GetResolution();
    *lux = CorrectLux(raw_lux);
    return ESP_OK;
}

esp_err_t VEML7700::ReadAll(veml7700_data_t* data) {
    esp_err_t ret = ReadALSRaw(&data->als_raw);
    if (ret != ESP_OK) return ret;

    ret = ReadWhiteRaw(&data->white_raw);
    if (ret != ESP_OK) return ret;

    float resolution = GetResolution();
    float als_raw_lux = (float)data->als_raw * resolution;
    float white_raw_lux = (float)data->white_raw * resolution;
    data->als_lux = CorrectLux(als_raw_lux);
    data->white_lux = CorrectLux(white_raw_lux);

    return ESP_OK;
}

// ============================================================================
// 中断状态读取
// ============================================================================
esp_err_t VEML7700::ReadInterruptStatus(bool* high_triggered, bool* low_triggered) {
    uint16_t status = 0;
    esp_err_t ret = ReadReg16(VEML7700_REG_ALS_INT, &status);
    if (ret != ESP_OK) return ret;

    *high_triggered = (status >> 14) & 0x01;
    *low_triggered = (status >> 15) & 0x01;
    return ESP_OK;
}

// ============================================================================
// 获取采样等待时间
// ============================================================================
uint32_t VEML7700::GetSampleDelayMs() const {
    return GetItDelayMs(it_);
}

// ============================================================================
// 自动量程实现
// 算法参考 Vishay 应用笔记和 tedyapo/arduino-VEML7700 驱动
//
// 策略：从高灵敏度（长积分+高增益）开始测量
//   - 如果计数值太高（>10000，接近饱和），降低积分时间
//   - 如果计数值太低（<200，精度不够），提高灵敏度
//   - 找到计数值在 200~10000 之间的最优参数组合
//
// 增益遍历顺序（灵敏度从高到低）: x2 → x1 → x(1/4) → x(1/8)
// 积分时间遍历顺序（从100ms开始往上探，再往下降）
// ============================================================================

// 增益搜索顺序：灵敏度从高到低
static const veml7700_gain_t kGainOrder[] = {
    VEML7700_GAIN_2, VEML7700_GAIN_1, VEML7700_GAIN_D4, VEML7700_GAIN_D8
};
static const int kGainOrderCount = sizeof(kGainOrder) / sizeof(kGainOrder[0]);

// 积分时间搜索顺序：从100ms开始向上到800ms
static const veml7700_it_t kItOrderUp[] = {
    VEML7700_IT_100MS, VEML7700_IT_200MS, VEML7700_IT_400MS, VEML7700_IT_800MS
};
static const int kItOrderUpCount = sizeof(kItOrderUp) / sizeof(kItOrderUp[0]);

// 积分时间搜索顺序：从100ms开始向下到25ms
static const veml7700_it_t kItOrderDown[] = {
    VEML7700_IT_100MS, VEML7700_IT_50MS, VEML7700_IT_25MS
};
static const int kItOrderDownCount = sizeof(kItOrderDown) / sizeof(kItOrderDown[0]);

// 自动量程计数阈值
static const uint16_t kAutoCountLow = 200;      // 低于此值精度不足
static const uint16_t kAutoCountHigh = 10000;    // 高于此值接近饱和

esp_err_t VEML7700::ApplyConfigAndWait(veml7700_gain_t gain, veml7700_it_t it) {
    // 先关机
    shutdown_ = true;
    gain_ = gain;
    it_ = it;
    esp_err_t ret = UpdateALSConf();
    if (ret != ESP_OK) return ret;

    // 开机
    shutdown_ = false;
    ret = UpdateALSConf();
    if (ret != ESP_OK) return ret;

    // 等待 2 倍积分时间确保新数据就绪（datasheet 建议）
    vTaskDelay(pdMS_TO_TICKS(GetItDelayMs(it) * 2 + 10));
    return ESP_OK;
}

esp_err_t VEML7700::AutoRangeMeasure(ReadRawFunc read_func, veml7700_auto_data_t* result) {
    uint16_t counts = 0;
    esp_err_t ret;

    // 阶段1：从中等灵敏度开始（100ms + x2增益），逐步提高积分时间
    for (int it_idx = 0; it_idx < kItOrderUpCount; it_idx++) {
        for (int g_idx = 0; g_idx < kGainOrderCount; g_idx++) {
            ret = ApplyConfigAndWait(kGainOrder[g_idx], kItOrderUp[it_idx]);
            if (ret != ESP_OK) return ret;

            ret = (this->*read_func)(&counts);
            if (ret != ESP_OK) return ret;

            // 计数值在合理范围内，直接返回
            if (counts >= kAutoCountLow && counts <= kAutoCountHigh) {
                goto done;
            }

            // 计数值太高，需要降低灵敏度
            if (counts > kAutoCountHigh) {
                // 尝试降低积分时间
                goto reduce_sensitivity;
            }

            // 计数值太低，尝试下一个更高的增益（循环继续）
        }
    }

    // 用最高灵敏度仍然太低，直接用当前结果（弱光环境）
    goto done;

reduce_sensitivity:
    // 阶段2：计数值过高，逐步缩短积分时间
    for (int it_idx = 1; it_idx < kItOrderDownCount; it_idx++) {
        ret = ApplyConfigAndWait(gain_, kItOrderDown[it_idx]);
        if (ret != ESP_OK) return ret;

        ret = (this->*read_func)(&counts);
        if (ret != ESP_OK) return ret;

        if (counts <= kAutoCountHigh) {
            goto done;
        }
    }

    // 最短积分时间 + 当前增益仍然太高，降低增益
    for (int g_idx = 0; g_idx < kGainOrderCount; g_idx++) {
        if (kGainOrder[g_idx] == gain_) continue;  // 跳过当前增益
        // 只尝试更低灵敏度的增益
        float current_factor = gain_factors[gain_];
        float try_factor = gain_factors[kGainOrder[g_idx]];
        if (try_factor >= current_factor) continue;

        ret = ApplyConfigAndWait(kGainOrder[g_idx], VEML7700_IT_25MS);
        if (ret != ESP_OK) return ret;

        ret = (this->*read_func)(&counts);
        if (ret != ESP_OK) return ret;

        if (counts <= kAutoCountHigh) {
            goto done;
        }
    }

    // 最低灵敏度仍然饱和，用当前结果（极强光环境）

done:
    result->raw = counts;
    result->gain = gain_;
    result->it = it_;

    float resolution = GetResolution();
    float raw_lux = (float)counts * resolution;
    result->lux = CorrectLux(raw_lux);

    ESP_LOGI(TAG, "自动量程完成: gain=%d, it=%d, raw=%u, lux=%.2f",
             gain_, it_, counts, result->lux);

    return ESP_OK;
}

esp_err_t VEML7700::ReadAutoALSLux(veml7700_auto_data_t* result) {
    return AutoRangeMeasure(&VEML7700::ReadALSRaw, result);
}

esp_err_t VEML7700::ReadAutoWhiteLux(veml7700_auto_data_t* result) {
    return AutoRangeMeasure(&VEML7700::ReadWhiteRaw, result);
}

esp_err_t VEML7700::ReadAutoAll(veml7700_auto_data_t* als_result, veml7700_auto_data_t* white_result) {
    // 先用ALS通道做自动量程找到最优参数
    esp_err_t ret = AutoRangeMeasure(&VEML7700::ReadALSRaw, als_result);
    if (ret != ESP_OK) return ret;

    // 用同一组参数直接读取White通道（不再重新调参，避免重复等待）
    uint16_t white_raw = 0;
    ret = ReadWhiteRaw(&white_raw);
    if (ret != ESP_OK) return ret;

    white_result->raw = white_raw;
    white_result->gain = gain_;
    white_result->it = it_;
    float resolution = GetResolution();
    float raw_lux = (float)white_raw * resolution;
    white_result->lux = CorrectLux(raw_lux);

    return ESP_OK;
}
