/*
 * @Author: mypx
 * @Date: 2025-09-24 11:10:45
 * @LastEditTime: 2025-09-24 13:32:18
 * @LastEditors: mypx mypx_coder@163.com
 * @Description: 
 */
#include "pm_params.h"
#include "et_log.h"

static pm_params_t *pm_params = NULL;

/**
 * @brief Calculate actual rotation (measured value - zero point)
 *
 * @param params Pointer to measurement parameters
 * @return float Actual rotation value (°)
 */
float pm_calculate_actual_rotation(pm_params_t *params)
{
    return params->measured.measured_value - params->measured.zero_point;
}

/**
 * @brief Calculate specific rotation
 *
 * @param params Pointer to measurement parameters
 * @return float Specific rotation ([α], °)
 */
float pm_calculate_specific_rotation(pm_params_t *params)
{
    float actual_rotation = pm_calculate_actual_rotation(params);

    /* Specific rotation formula: [α] = (100 × α) / (L × C)
     * L: tube length (dm) from config
     * C: concentration (g/100ml) from measured
     */
    float L = params->config.tube_length;
    float C = params->measured.concentration;

    if (L == 0.0f || C == 0.0f)
    {
        ET_ERR("Error: tube length or concentration must not be zero when calculating specific rotation\n");
        return NAN;
    }

    return (100.0f * actual_rotation) / (L * C);
}

/**
 * @brief Calculate concentration
 *
 * @param params Pointer to measurement parameters
 * @return float Concentration (g/100ml)
 */
float pm_calculate_concentration(pm_params_t *params)
{
    float actual_rotation = pm_calculate_actual_rotation(params);

    /* Concentration formula: C = (100 × α) / (L × [α])
     * L: tube length from config
     * [α]: specific rotation from measured
     */
    float L     = params->config.tube_length;
    float alpha = params->measured.specific_rotation;

    if (L == 0.0f || alpha == 0.0f)
    {
        ET_ERR("Error: tube length or specific rotation must not be zero when calculating concentration\n");
        return NAN;
    }

    return (100.0f * actual_rotation) / (L * alpha);
}

/**
 * @brief Calculate international sugar scale (°Z)
 *
 * @param params Pointer to measurement parameters
 * @return float Sugar scale value (°Z)
 */
float pm_calculate_sugar_scale(pm_params_t *params)
{
    float actual_rotation = pm_calculate_actual_rotation(params);

    // International sugar scale formula: °Z = α × 2.888
    return actual_rotation * 2.888f;
}

/**
 * @brief Perform calculation based on selected measurement mode
 *
 * @param params Pointer to measurement parameters
 * @return float Calculation result
 */
float pm_calculate_measurement(pm_params_t *params)
{
    switch (params->config.mode)
    {
    case PM_MEAS_MODE_OPTICAL_ROTATION:
        return pm_calculate_actual_rotation(params);

    case PM_MEAS_MODE_SPECIFIC_ROTATION:
        return pm_calculate_specific_rotation(params);

    case PM_MEAS_MODE_CONCENTRATION:
        return pm_calculate_concentration(params);

    case PM_MEAS_MODE_SUGAR_SCALE:
        return pm_calculate_sugar_scale(params);

    default:
        ET_ERR("Error: invalid measurement mode\n");
        return NAN;
    }
}

/**
 * @brief Get the name of the measurement mode
 *
 * @param mode Measurement mode
 * @return const char* Mode name string
 */
const char *pm_get_mode_name(PM_MeasMode_t mode)
{
    switch (mode)
    {
    case PM_MEAS_MODE_OPTICAL_ROTATION:
        return "Optical rotation";
    case PM_MEAS_MODE_SPECIFIC_ROTATION:
        return "Specific rotation";
    case PM_MEAS_MODE_CONCENTRATION:
        return "Concentration";
    case PM_MEAS_MODE_SUGAR_SCALE:
        return "International sugar scale";
    default:
        return "Unknown mode";
    }
}

/**
 * @brief Print measurement results
 *
 * @param params Pointer to measurement parameters
 * @param result Calculation result
 */
void pm_print_measurement_result(pm_params_t *params, float result)
{
    ET_INFO("=== Polarimeter Measurement Result ===\n");
    ET_INFO("Mode: %s\n", pm_get_mode_name(params->config.mode));
    ET_INFO("Zero point: %.4f °\n", params->measured.zero_point);
    ET_INFO("Measured value: %.4f °\n", params->measured.measured_value);
    ET_INFO("Actual rotation: %.4f °\n", pm_calculate_actual_rotation(params));
    ET_INFO("Tube length: %.1f dm\n", params->config.tube_length);
    ET_INFO("Temperature: %.1f °C\n", params->measured.temperature);
    ET_INFO("Wavelength: %.0f nm\n", params->config.wavelength);

    if (!isnan(result))
    {
        switch (params->config.mode)
        {
        case PM_MEAS_MODE_OPTICAL_ROTATION:
            ET_INFO("Result: %.4f °\n", result);
            break;

        case PM_MEAS_MODE_SPECIFIC_ROTATION:
            ET_INFO("Concentration: %.2f g/100ml\n", params->measured.concentration);
            ET_INFO("Result: [α] = %.4f °\n", result);
            break;

        case PM_MEAS_MODE_CONCENTRATION:
            ET_INFO("Specific rotation: %.2f °\n", params->measured.specific_rotation);
            ET_INFO("Result: %.4f g/100ml\n", result);
            break;

        case PM_MEAS_MODE_SUGAR_SCALE:
            ET_INFO("Result: %.4f °Z\n", result);
            break;
        }
    }
    else
    {
        ET_ERR("Calculation failed!\n");
    }
    ET_INFO("===========================\n\n");
}

void pm_params_recompute(pm_params_t *params)
{
    if (params == NULL)
        return;

    params->computed.actual_rotation = pm_calculate_actual_rotation(params);

    /* Compute specific rotation if concentration provided */
    if (params->measured.concentration != 0.0f)
    {
        params->computed.specific_rotation = pm_calculate_specific_rotation(params);
    }
    else
    {
        params->computed.specific_rotation = params->measured.specific_rotation;
    }

    /* Compute concentration if specific rotation provided */
    if (params->measured.specific_rotation != 0.0f)
    {
        params->computed.concentration = pm_calculate_concentration(params);
    }
    else
    {
        params->computed.concentration = params->measured.concentration;
    }

    params->computed.sugar_scale = pm_calculate_sugar_scale(params);
}

void pm_params_reset(pm_params_t *params)
{
    if (params == NULL)
        return;
    /* measured */
    params->measured.zero_point        = 0.0f;
    params->measured.measured_value    = 0.0f;
    params->measured.concentration     = 10.0f;       /* default 10 g/100ml */
    params->measured.specific_rotation = 66.5f;       /* default for sucrose */
    params->measured.temperature       = PM_TEMP_DEF; /* default 20°C */

    pm_params_recompute(params);
}

pm_params_t *pm_params_get_instance(void)
{
    return pm_params;
}

void pm_params_init(pm_params_t *params)
{
    if (params == NULL)
        return;
    pm_params = params;
    /* measured */
    params->measured.zero_point        = 0.0f;
    params->measured.measured_value    = 0.0f;
    params->measured.concentration     = 0.0f;  
    params->measured.specific_rotation = 0.0f;      
    params->measured.temperature       = PM_TEMP_DEF;

    /* config */
    params->config.tube_length = PM_TUBE_LENGTH_DEF;               /* default 1 dm */
    params->config.wavelength  = PM_WAVE_LENGTH_DEF; /* sodium D */
    params->config.mode        = PM_MEAS_MODE_DEF;

    pm_params_recompute(params);
}

