/**
 * @file analog_loop_driver.c
 * @brief Driver implementation for current loop analog signal processing.
 */

#include "analog_loop_driver.h"

// --- Static Variables ---
static ADC_HandleTypeDef* s_hadc = NULL;
static uint32_t s_raw_value = 0;
static int16_t s_voltage_mV = 0;
static int16_t s_current_uA = 0;

/**
 * @brief Initializes the analog loop driver.
 *
 * @param hadc Pointer to the ADC handle structure.
 * @return HAL_StatusTypeDef HAL status.
 */
HAL_StatusTypeDef AnalogLoop_Init(ADC_HandleTypeDef *hadc)
{
    if (hadc == NULL)
    {
        return HAL_ERROR;
    }

    s_hadc = hadc;

    // Reset stored values
    s_raw_value = 0;
    s_voltage_mV = 0;
    s_current_uA = 0;

    return HAL_OK;
}

/**
 * @brief Gets the raw ADC value from the analog loop.
 *
 * @return uint32_t Raw ADC value.
 */
uint32_t AnalogLoop_GetRawValue(void)
{
    return s_raw_value;
}

/**
 * @brief Calculates and returns the current in microamps.
 *
 * @return int16_t Current in uA.
 */
int16_t AnalogLoop_GetCurrent_uA(void)
{
    // Convert raw ADC value to voltage (mV)
    uint32_t voltage_mV = (s_raw_value * ANALOG_LOOP_VREF_MV) / 4095U;

    // Apply calibration and convert to current (uA)
    // V_out = I * R2 / R1 => I = (V_out * R1) / R2
    // Convert to uA: I(uA) = (V_out(mV) * R1(ohms) * 1000) / R2(ohms)
    uint32_t current_uA = (voltage_mV * ANALOG_LOOP_R1_OHMS * 1000U) / ANALOG_LOOP_R2_OHMS;

    // Clamp to valid range
    if (current_uA < ANALOG_LOOP_CURRENT_MIN_UA)
    {
        current_uA = ANALOG_LOOP_CURRENT_MIN_UA;
    }
    else if (current_uA > ANALOG_LOOP_CURRENT_MAX_UA)
    {
        current_uA = ANALOG_LOOP_CURRENT_MAX_UA;
    }

    s_current_uA = (int16_t)current_uA;

    return s_current_uA;
}

/**
 * @brief Calculates and returns the voltage in millivolts.
 *
 * @return int16_t Voltage in mV.
 */
int16_t AnalogLoop_GetVoltage_mV(void)
{
    // Convert raw ADC value to voltage (mV)
    uint32_t voltage_mV = (s_raw_value * ANALOG_LOOP_VREF_MV) / 4095U;

    s_voltage_mV = (int16_t)voltage_mV;

    return s_voltage_mV;
}

/**
 * @brief Updates the internal ADC reading.
 *
 * This function should be called periodically or when new ADC data is available.
 *
 * @return HAL_StatusTypeDef HAL status.
 */
HAL_StatusTypeDef AnalogLoop_Update(void)
{
    if (s_hadc == NULL)
    {
        return HAL_ERROR;
    }

    // Perform ADC conversion
    HAL_StatusTypeDef status = HAL_ADC_Start(s_hadc);
    if (status != HAL_OK)
    {
        return status;
    }

    // Wait for conversion to complete
    status = HAL_ADC_PollForConversion(s_hadc, HAL_MAX_DELAY);
    if (status != HAL_OK)
    {
        return status;
    }

    // Get the raw ADC value
    s_raw_value = HAL_ADC_GetValue(s_hadc);

    // Stop ADC conversion
    HAL_ADC_Stop(s_hadc);

    return HAL_OK;
}
