#include "app_calibration.h"
#include "flash_port.h"

#include <stdio.h>
#include <string.h>

#undef MX_LOGD
#undef MX_LOGI
#undef MX_LOGW
#undef MX_LOGE
#define MX_LOGD APP_CALI_LOGD
#define MX_LOGI APP_CALI_LOGI
#define MX_LOGW APP_CALI_LOGW
#define MX_LOGE APP_CALI_LOGE

#define CALI_TAG "APP_CALIBRATION"

#include "app_mozen_handler.h"
#include "lib_cal_creep_resistance.h"

/*************************** Public Parameters **********************/
volatile bool g_check_cali = false;
volatile bool g_reset_takeEffect = false;
volatile bool g_is_active_reporting = true; 
volatile bool g_is_creep_enable = true;

pressure_params_t* pressure_params = NULL;
/********************************************************************/

static app_device_status_t g_device_status = {0};
static CALI_INFO_T cali_info = {0};
static app_math_cali_t s_temp_cali = {0};
static app_creep_params s_temp_creep = {5U, 20U};
static uint16_t s_map_matrix[APP_MAP_MATRIX_WORDS] = {0};

static uint8_t sensor_data_payload[APP_SENSOR_FRAME_PAYLOAD_MAX_BYTES];
static volatile bool s_is_payload_packing = false;

#define APP_CREEP_DEFAULT_STRENGTH  2U
#define APP_CREEP_DEFAULT_LEVEL     10U

/**
 * @brief Get legacy device status
 * @return Pointer to legacy status structure
 */
app_device_status_t* app_device_status_get(void)
{
    return &g_device_status;
}

/**
 * @brief Set legacy device status
 * @param status Pointer to status data to apply
 */
void app_device_status_set(const app_device_status_t *status)
{
    if (status != NULL) {
        g_device_status = *status;
    }
}

void app_device_status_init(void)
{
    memset(&g_device_status, 0x00, sizeof(g_device_status));
    g_device_status.sensor_tx_sw = 1;
    g_device_status.tx_stopping = 0;
    g_device_status.output_pick = 0;
}

/**
 * @brief Parse math calibration parameters from payload
 * @param data Input byte buffer
 * @param len Length of data
 * @param out_params Output structure
 * @return 1 on success, 0 on failure
 */
static uint8_t app_math_parse_payload(const uint8_t *data, uint16_t len, app_math_cali_t *out_params)
{
    if ((data == NULL) || (out_params == NULL) || (len < APP_MATH_PARAM_FRAME_SIZE)) {
        return 0;
    }

    out_params->max_trigger_res_value = (uint16_t)(((uint16_t)data[1] << 8) | data[0]);
    out_params->min_trigger_res_value = (uint16_t)(((uint16_t)data[3] << 8) | data[2]);
    out_params->div_trigger_res_value = (uint16_t)(((uint16_t)data[5] << 8) | data[4]);
    out_params->max_display_value = (uint16_t)(((uint16_t)data[7] << 8) | data[6]);

    if (out_params->min_trigger_res_value >= out_params->max_trigger_res_value) {
        return 0;
    }
    return 1;
}
uint16_t g_buf[5];
/**
 * @brief Apply math calibration parameters to runtime algorithms
 * @param params Parameter structure
 */
static void app_math_apply_runtime(const app_math_cali_t *params)
{
    if (params == NULL) {
        return;
    }

    MX_LOGI(CALI_TAG, "Math apply runtime: min=%u max=%u div=%u max_display=%u",
           (unsigned int)params->min_trigger_res_value,
           (unsigned int)params->max_trigger_res_value,
           (unsigned int)params->div_trigger_res_value,
           (unsigned int)params->max_display_value);
	g_buf[0] = params->max_trigger_res_value;
	g_buf[1] = params->min_trigger_res_value;
	g_buf[2] = params->div_trigger_res_value;
	g_buf[3] = MAX_DISPLAY_ADC_VALUE;
	g_buf[4] = params->max_display_value;
    math_resi_init(params->max_trigger_res_value,
                   params->min_trigger_res_value,
                   params->div_trigger_res_value,
                   MAX_DISPLAY_ADC_VALUE,
                   params->max_display_value);
	
		  
}

/**
 * @brief Set default creep parameters
 * @param params Parameter structure
 */
static void app_creep_set_default(app_creep_params *params)
{
    if (params == NULL) {
        return;
    }

    params->creep_strength = APP_CREEP_DEFAULT_STRENGTH;
    params->creep_level = APP_CREEP_DEFAULT_LEVEL;
}

/**
 * @brief Apply creep parameters to runtime algorithms
 * @param params Parameter structure
 */
static void app_creep_apply_runtime(const app_creep_params *params)
{
    if (params == NULL) {
        return;
    }

    MX_LOGI(CALI_TAG, "Creep apply runtime: strength=%u level=%u",
           (unsigned int)params->creep_strength,
           (unsigned int)params->creep_level);
           
    cal_creep_resistance_init(params->creep_strength, params->creep_level);
}

/**
 * @brief Set default device info
 * @param dev_info Device info structure
 */
static void app_device_info_set_default(app_dev_info *dev_info)
{
    char sensor_size_text[16] = {0};

    if (dev_info == NULL) {
        return;
    }

    memset(dev_info, 0, sizeof(app_dev_info));
    memcpy(dev_info->pn, DEFAULT_PN, sizeof(DEFAULT_PN) - 1);
    memcpy(dev_info->sn, DEFAULT_SN, sizeof(DEFAULT_SN) - 1);
    memcpy(dev_info->sw_ver, DEFAULT_SW_VER, sizeof(DEFAULT_SW_VER) - 1);
    memcpy(dev_info->hw_ver, DEFAULT_HW_VER, sizeof(DEFAULT_HW_VER) - 1);

    (void)snprintf(sensor_size_text,
                   sizeof(sensor_size_text),
                   "%u,%u",
                   (unsigned int)MT_AX_NUM,
                   (unsigned int)MT_AY_NUM);
    memcpy(dev_info->sensor_size, sensor_size_text, strlen(sensor_size_text));
    memcpy(dev_info->protocol_ver, APP_DEVICE_PROTOCOL_VER, strlen(APP_DEVICE_PROTOCOL_VER));
}

/**
 * @brief Ensure device info strings are properly null-terminated
 * @param dev_info Device info structure
 */
static void app_device_info_normalize(app_dev_info *dev_info)
{
    if (dev_info == NULL) {
        return;
    }

    dev_info->pn[sizeof(dev_info->pn) - 1U] = '\0';
    dev_info->sn[sizeof(dev_info->sn) - 1U] = '\0';
    dev_info->sw_ver[sizeof(dev_info->sw_ver) - 1U] = '\0';
    dev_info->hw_ver[sizeof(dev_info->hw_ver) - 1U] = '\0';
    dev_info->sensor_size[sizeof(dev_info->sensor_size) - 1U] = '\0';
    dev_info->protocol_ver[sizeof(dev_info->protocol_ver) - 1U] = '\0';
}

/**
 * @brief Validate device info structure fields
 * @param dev_info Device info structure
 * @return 1 if valid, 0 otherwise
 */
static uint8_t app_device_info_is_valid(const app_dev_info *dev_info)
{
    if (dev_info == NULL) {
        return 0;
    }

    if ((dev_info->pn[0] == '\0') || (dev_info->sn[0] == '\0') ||
        (dev_info->sw_ver[0] == '\0') || (dev_info->hw_ver[0] == '\0')) {
        return 0;
    }

    if (((uint8_t)dev_info->pn[0] == 0xFFU) ||
        ((uint8_t)dev_info->sn[0] == 0xFFU) ||
        ((uint8_t)dev_info->sw_ver[0] == 0xFFU) ||
        ((uint8_t)dev_info->hw_ver[0] == 0xFFU)) {
        return 0;
    }

    if ((memchr(dev_info->pn, '\0', sizeof(dev_info->pn)) == NULL) ||
        (memchr(dev_info->sn, '\0', sizeof(dev_info->sn)) == NULL) ||
        (memchr(dev_info->sw_ver, '\0', sizeof(dev_info->sw_ver)) == NULL) ||
        (memchr(dev_info->hw_ver, '\0', sizeof(dev_info->hw_ver)) == NULL)) {
        return 0;
    }

    return 1;
}

/**
 * @brief Build 8-bit sensor payload
 * @param sensor_data 2D array of sensor data
 * @param payload Output buffer
 * @param payload_size Output buffer size
 * @param payload_len Actual payload length
 * @return 1 on success, 0 on failure
 */
uint8_t app_calibration_build_sensor8bit_payload(const uint8_t (*sensor_data)[MT_AY_NUM],                                    
                                             uint8_t *payload,
                                             uint16_t payload_size,
                                             uint16_t *payload_len)
{
    uint16_t matrix_count = (uint16_t)(MT_AX_NUM * MT_AY_NUM);
    uint16_t need_len;

    if ((sensor_data == NULL) || (payload == NULL) || (payload_len == NULL)) {
        return 0U;
    }

	need_len = (uint16_t)(1U + matrix_count);
	if (payload_size < need_len) {
		return 0U;
	}

	payload[0] = 0x01U;
	memcpy(&payload[1], sensor_data, (size_t)(need_len - 1U));
	*payload_len = need_len;
	return 1U;
}

/**
 * @brief Build 16-bit sensor payload
 * @param sensor_data 2D array of sensor data
 * @param payload Output buffer
 * @param payload_size Output buffer size
 * @param payload_len Actual payload length
 * @return 1 on success, 0 on failure
 */
uint8_t app_calibration_build_sensor16bit_payload(const uint16_t (*sensor_data)[MT_AY_NUM],                    
                                             uint8_t *payload,
                                             uint16_t payload_size,
                                             uint16_t *payload_len)
{
    uint16_t matrix_count = (uint16_t)(MT_AX_NUM * MT_AY_NUM);
    uint16_t need_len;

    if ((sensor_data == NULL) || (payload == NULL) || (payload_len == NULL)) {
        return 0U;
    }

	need_len = (uint16_t)(1U + (matrix_count * (uint16_t)sizeof(uint16_t)));
	if (payload_size < need_len) {
		return 0U;
	}

	payload[0] = 0x02U;
	memcpy(&payload[1], sensor_data, (size_t)(need_len - 1U));
	*payload_len = need_len;
	return 1U;
}

/**
 * @brief Initialize math parameters
 * @return 1 on success, 0 on failure
 */
uint8_t app_math_init(uint16_t *min_trigger_res_value,
                      uint16_t *max_trigger_res_value,
                      uint16_t *div_trigger_res_value,
                      uint16_t *max_display_value,
                      uint16_t *sensor_data_type)
{
    app_math_cali_t flash_params;

    if ((min_trigger_res_value == NULL) ||
        (max_trigger_res_value == NULL) ||
        (div_trigger_res_value == NULL) ||
        (max_display_value == NULL) ||
        (sensor_data_type == NULL)) {
        MX_LOGE(CALI_TAG, "Math init bad output pointers");
        return 0;
    }

    if (!flash_port_math_load(&flash_params)) {
        MX_LOGE(CALI_TAG, "Math init load flash params failed");
        return 0;
    }

    if (flash_params.check_cail != APP_CALI_STATUS_CALIBRATED) {
        MX_LOGW(CALI_TAG, "Math init skipped: not calibrated");
        return 0;
    }

    if (flash_params.min_trigger_res_value >= flash_params.max_trigger_res_value) {
        MX_LOGE(CALI_TAG, "Math init invalid range, min=%u max=%u",
                (unsigned int)flash_params.min_trigger_res_value,
                (unsigned int)flash_params.max_trigger_res_value);
        return 0;
    }
    
    *min_trigger_res_value = flash_params.min_trigger_res_value;
    *max_trigger_res_value = flash_params.max_trigger_res_value;
    *div_trigger_res_value = flash_params.div_trigger_res_value;
    *max_display_value = flash_params.max_display_value;
    *sensor_data_type = flash_params.sensor_data_type;

    app_math_apply_runtime(&flash_params);
    MX_LOGI(CALI_TAG, "Math init from calibration flash params");
    return 1;
}

/**
 * @brief Get current temporary math parameters
 * @param out_params Output parameter structure pointer
 * @return 1 on success, 0 on failure
 */
uint8_t app_math_get_temp_params(app_math_cali_t *out_params)
{
    if (out_params == NULL) {
        MX_LOGE(CALI_TAG, "Math get temp bad arg");
        return 0;
    }
	out_params->max_trigger_res_value = s_temp_cali.max_trigger_res_value;
	
    out_params->min_trigger_res_value = s_temp_cali.min_trigger_res_value;
    out_params->div_trigger_res_value = s_temp_cali.div_trigger_res_value;
    out_params->max_display_value = s_temp_cali.max_display_value;
    out_params->sensor_data_type = s_temp_cali.sensor_data_type;

    return 1;
}

/**
 * @brief Set current temporary math parameters
 * @param params Input parameter structure pointer
 * @return 1 on success, 0 on failure
 */
uint8_t app_math_set_temp_params(const app_math_cali_t *params)
{
    if (params == NULL) {
        MX_LOGE(CALI_TAG, "Math set temp bad arg");
        return 0;
    }

    s_temp_cali.min_trigger_res_value = params->min_trigger_res_value;
    s_temp_cali.max_trigger_res_value = params->max_trigger_res_value;
    s_temp_cali.div_trigger_res_value = params->div_trigger_res_value;
    s_temp_cali.max_display_value = params->max_display_value;
    s_temp_cali.sensor_data_type = params->sensor_data_type;
    
    app_math_apply_runtime(&s_temp_cali);

    // Persist the effective sensor_data_type together with runtime params
    if (!flash_port_math_save(&s_temp_cali)) {
        MX_LOGE(CALI_TAG, "Save math params failed");
        return 0;
    }

    return 1;
}

/**
 * @brief Apply temporary math parameters immediately from a byte buffer
 * @param data Input data buffer pointer
 * @param len Length of the buffer
 * @return 1 on success, 0 on failure
 */
uint8_t app_math_write_temp_params(const uint8_t *data, uint16_t len)
{
    app_math_cali_t params;

    if (!app_math_parse_payload(data, len, &params)) {
        MX_LOGE(CALI_TAG, "Math temp parse failed, len=%u", (unsigned int)len);
        return 0;
    }

    s_temp_cali.min_trigger_res_value = params.min_trigger_res_value;
    s_temp_cali.max_trigger_res_value = params.max_trigger_res_value;
    s_temp_cali.div_trigger_res_value = params.div_trigger_res_value;
    s_temp_cali.max_display_value = params.max_display_value;

    app_math_apply_runtime(&s_temp_cali);
    return 1;
}

/**
 * @param data Input data buffer pointer
 * @param len Length of the buffer
 * @return 1 on success, 0 on failure
 */
uint8_t app_math_save_params(const uint8_t *data, uint16_t len)
{
    app_math_cali_t flash_params;

    if (!app_math_write_temp_params(data, len)) {
        return 0;
    }

    if (flash_port_math_load(&flash_params)) {
        s_temp_cali.check_cail = flash_params.check_cail;
    } else {
        s_temp_cali.check_cail = APP_CALI_STATUS_UNCALIBRATED;
    }

    if (!flash_port_math_save(&s_temp_cali)) {
        MX_LOGE(CALI_TAG, "Save math params failed");
        return 0;
    }

    if (!flash_port_math_load(&flash_params)) {
        MX_LOGE(CALI_TAG, "Verify math params failed");
    }

    app_math_apply_runtime(&flash_params);
    return 1;
}

/**
 * @brief Clear math parameters to default values
 * @return 1 on success, 0 on failure
 */
uint8_t app_math_clear_params(void)
{
    app_math_cali_t default_params = {0};

    default_params.min_trigger_res_value = DEFAULT_MIN_TRIGGER_RES_VALUE;
    default_params.max_trigger_res_value = DEFAULT_MAX_TRIGGER_RES_VALUE;
    default_params.div_trigger_res_value = DEFAULT_DIV_TRIGGER_RES_VALUE;
    default_params.max_display_value = DEFAULT_MAX_DISPLAY_VALUE;
    default_params.check_cail = APP_CALI_STATUS_UNCALIBRATED;

    app_math_apply_runtime(&default_params);

    s_temp_cali.check_cail = APP_CALI_STATUS_UNCALIBRATED;
    s_temp_cali.min_trigger_res_value = default_params.min_trigger_res_value;
    s_temp_cali.max_trigger_res_value = default_params.max_trigger_res_value;
    s_temp_cali.div_trigger_res_value = default_params.div_trigger_res_value;
    s_temp_cali.max_display_value = default_params.max_display_value;

    return flash_port_math_save(&s_temp_cali);
}

/**
 * @brief Read temporary math parameters into response buffer
 * @param response_buf Output buffer
 * @param response_len Output length pointer
 * @return 1 on success, 0 on failure
 */
uint8_t app_math_read_temp_params(uint8_t *response_buf, uint16_t *response_len)
{
    if ((response_buf == NULL) || (response_len == NULL)) {
        MX_LOGE(CALI_TAG, "Read temp math params bad arg");
        return 0;
    }

    response_buf[0] = (uint8_t)(s_temp_cali.max_trigger_res_value & 0xFFU);
    response_buf[1] = (uint8_t)((s_temp_cali.max_trigger_res_value >> 8) & 0xFFU);
    response_buf[2] = (uint8_t)(s_temp_cali.min_trigger_res_value & 0xFFU);
    response_buf[3] = (uint8_t)((s_temp_cali.min_trigger_res_value >> 8) & 0xFFU);
    response_buf[4] = (uint8_t)(s_temp_cali.div_trigger_res_value & 0xFFU);
    response_buf[5] = (uint8_t)((s_temp_cali.div_trigger_res_value >> 8) & 0xFFU);
    response_buf[6] = (uint8_t)(s_temp_cali.max_display_value & 0xFFU);
    response_buf[7] = (uint8_t)((s_temp_cali.max_display_value >> 8) & 0xFFU);

    *response_len = APP_MATH_PARAM_FRAME_SIZE;
    return 1;
}

/**
 * @brief Read solidified math parameters from Flash
 * @param response_buf Output buffer
 * @param response_len Output length pointer
 * @return 1 on success, 0 on failure
 */
uint8_t app_math_read_solidified_params(uint8_t *response_buf, uint16_t *response_len)
{
    app_math_cali_t flash_params;

    if ((response_buf == NULL) || (response_len == NULL)) {
        MX_LOGE(CALI_TAG, "Read flash math params bad arg");
        return 0;
    }

    if (!flash_port_math_load(&flash_params)) {
        MX_LOGE(CALI_TAG, "Load flash math params failed");
        memset(response_buf, 0xFF, APP_MATH_PARAM_FRAME_SIZE);
        *response_len = APP_MATH_PARAM_FRAME_SIZE;
        return 0;
    }

    response_buf[0] = (uint8_t)(flash_params.max_trigger_res_value & 0xFFU);
    response_buf[1] = (uint8_t)((flash_params.max_trigger_res_value >> 8) & 0xFFU);
    response_buf[2] = (uint8_t)(flash_params.min_trigger_res_value & 0xFFU);
    response_buf[3] = (uint8_t)((flash_params.min_trigger_res_value >> 8) & 0xFFU);
    response_buf[4] = (uint8_t)(flash_params.div_trigger_res_value & 0xFFU);
    response_buf[5] = (uint8_t)((flash_params.div_trigger_res_value >> 8) & 0xFFU);
    response_buf[6] = (uint8_t)(flash_params.max_display_value & 0xFFU);
    response_buf[7] = (uint8_t)((flash_params.max_display_value >> 8) & 0xFFU);

    *response_len = APP_MATH_PARAM_FRAME_SIZE;
    return 1;
}

/**
 * @brief Initialize pressure parameters from Flash
 * @return Pointer to pressure_params_t or NULL if failed
 */
pressure_params_t *app_pressure_init(void)
{
    if (!flash_port_pressure_load(&s_temp_cali)) {
        s_temp_cali.check_cail = APP_CALI_STATUS_UNCALIBRATED;
        s_temp_cali.min_trigger_res_value = DEFAULT_MIN_TRIGGER_RES_VALUE;
        s_temp_cali.max_trigger_res_value = DEFAULT_MAX_TRIGGER_RES_VALUE;
        s_temp_cali.div_trigger_res_value = DEFAULT_DIV_TRIGGER_RES_VALUE;
        s_temp_cali.max_display_value = DEFAULT_MAX_DISPLAY_VALUE;
        s_temp_cali.num_points = 0;
        memset(s_temp_cali.pressure_points, 0, sizeof(s_temp_cali.pressure_points));
        return NULL;
    }

    return &s_temp_cali;
}

/**
 * @brief Write temporary pressure parameters from buffer
 * @param data Buffer pointer
 * @param len Length of the data
 * @return 1 on success, 0 on failure
 */
uint8_t app_pressure_write_temp_params(const uint8_t *data, uint16_t len)
{
    uint16_t num_points;

    if ((data == NULL) || (len < 2U)) {
        MX_LOGE(CALI_TAG, "Pressure temp write bad arg, len=%u", (unsigned int)len);
        return 0;
    }
//    mx_log_hex_frame(CALI_TAG, data, len);

    num_points = (uint16_t)data[0];
    MX_LOGD(CALI_TAG, "Pressure temp write num_points=%u len=%u", (unsigned int)num_points, (unsigned int)len);
    
    if (num_points > MAX_PRESSURE_POINTS) {
        MX_LOGE(CALI_TAG, "Pressure temp write invalid points=%u len=%u",
                (unsigned int)num_points, (unsigned int)len);
        return 0;
    }

    s_temp_cali.num_points = num_points;
    memset(s_temp_cali.pressure_points, 0, sizeof(s_temp_cali.pressure_points));
    memcpy(s_temp_cali.pressure_points, &data[1], (size_t)num_points * sizeof(uint16_t));

    return 1;
}

/**
 * @brief Read temporary pressure parameters
 * @param response_buf Output buffer
 * @param response_len Output length pointer
 * @return 1 on success, 0 on failure
 */
uint8_t app_pressure_read_temp_params(uint8_t *response_buf, uint16_t *response_len)
{
    if ((response_buf == NULL) || (response_len == NULL)) {
        MX_LOGE(CALI_TAG, "Read temp pressure params bad arg");
        return 0;
    }

    response_buf[0] = (uint8_t)(s_temp_cali.num_points & 0xFFU);
    response_buf[1] = (uint8_t)((s_temp_cali.num_points >> 8) & 0xFFU);
    memcpy(&response_buf[2],
           s_temp_cali.pressure_points,
           (size_t)s_temp_cali.num_points * sizeof(uint16_t));

    *response_len = (uint16_t)(2U + (s_temp_cali.num_points * 2U));
    return 1;
}

/**
 * @brief Save pressure parameters to Flash
 * @param data Input byte buffer
 * @param len Length of the data
 * @return 1 on success, 0 on failure
 */
uint8_t app_pressure_save_params(const uint8_t *data, uint16_t len)
{
    if (!app_pressure_write_temp_params(data, len)) {
        MX_LOGE(CALI_TAG, "Write temp pressure params failed");
        return 0;
    }

    if (!flash_port_pressure_save(&s_temp_cali)) {
        MX_LOGE(CALI_TAG, "Save pressure params failed");
        return 0;
    }

    return 1;
}

/**
 * @brief Read solidified pressure parameters from Flash
 * @param response_buf Output buffer
 * @param response_len Output length pointer
 * @return 1 on success, 0 on failure
 */
uint8_t app_pressure_read_solidified_params(uint8_t *response_buf, uint16_t *response_len)
{
    app_math_cali_t flash_params;

    if ((response_buf == NULL) || (response_len == NULL)) {
        MX_LOGE(CALI_TAG, "Read flash pressure params bad arg");
        return 0;
    }

    if (!flash_port_pressure_load(&flash_params)) {
        MX_LOGE(CALI_TAG, "Load flash pressure params failed");
        *response_len = 0;
        return 0;
    }

    response_buf[0] = (uint8_t)(flash_params.num_points & 0xFFU);
    response_buf[1] = (uint8_t)((flash_params.num_points >> 8) & 0xFFU);
    memcpy(&response_buf[2],
           flash_params.pressure_points,
           (size_t)flash_params.num_points * sizeof(uint16_t));

    *response_len = (uint16_t)(2U + (flash_params.num_points * 2U));
    return 1;
}

/**
 * @brief Clear pressure parameters
 * @return 1 on success, 0 on failure
 */
uint8_t app_pressure_clear_params(void)
{
    s_temp_cali.num_points = 0;
    memset(s_temp_cali.pressure_points, 0, sizeof(s_temp_cali.pressure_points));
    
    if (!flash_port_pressure_save(&s_temp_cali)) {
        MX_LOGE(CALI_TAG, "Clear pressure params failed");
        return 0;
    }

    return 1;
}

/**
 * @brief Initialize map matrix from Flash
 * @return 1 on success, 0 on failure
 */
uint8_t app_map_init(void)
{
    if (!flash_port_map_load(s_map_matrix, APP_MAP_MATRIX_WORDS)) {
        memset(s_map_matrix, 0, sizeof(s_map_matrix));
        return 0;
    }
    return 1;
}

/**
 * @brief Write temporary map parameters
 * @param data Input byte buffer
 * @param len Length of data
 * @return 1 on success, 0 on failure
 */
uint8_t app_map_write_temp_params(const uint8_t *data, uint16_t len)
{
    if ((data == NULL) || (len != (uint16_t)APP_MAP_MATRIX_BYTES)) {
        MX_LOGE(CALI_TAG, "Map temp write bad arg, len=%u", (unsigned int)len);
        return 0;
    }

    memcpy(s_map_matrix, data, APP_MAP_MATRIX_BYTES);
    return 1;
}

/**
 * @brief Save map parameters to Flash
 * @param data Input byte buffer
 * @param len Length of data
 * @return 1 on success, 0 on failure
 */
uint8_t app_map_save_params(const uint8_t *data, uint16_t len)
{
    if (!app_map_write_temp_params(data, len)) {
        MX_LOGE(CALI_TAG, "Map param length invalid: %u", (unsigned int)len);
        return 0;
    }

    if (!flash_port_map_save(s_map_matrix, APP_MAP_MATRIX_WORDS)) {
        MX_LOGE(CALI_TAG, "Save map params failed");
        return 0;
    }

    if (!flash_port_map_load(s_map_matrix, APP_MAP_MATRIX_WORDS)) {
        MX_LOGE(CALI_TAG, "Verify map params failed");
        return 0;
    }

    if (!app_calibration_set(1U)) {
        MX_LOGE(CALI_TAG, "Set calibrated state failed");
        return 0;
    }

    return 1;
}

/**
 * @brief Clear map parameters from Flash
 * @return 1 on success, 0 on failure
 */
uint8_t app_map_clear_params(void)
{
    memset(s_map_matrix, 0, sizeof(s_map_matrix));
    if (!flash_port_map_clear()) {
        MX_LOGE(CALI_TAG, "Clear map params failed");
        return 0;
    }

    if (!app_calibration_set(0U)) {
        MX_LOGE(CALI_TAG, "Clear calibrated state failed");
        return 0;
    }

    return 1;
}

/**
 * @brief Read temporary map parameters
 * @param response_buf Output buffer
 * @param response_len Output length pointer
 * @return 1 on success, 0 on failure
 */
uint8_t app_map_read_temp_params(uint8_t *response_buf, uint16_t *response_len)
{
    if ((response_buf == NULL) || (response_len == NULL)) {
        MX_LOGE(CALI_TAG, "Read temp map params bad arg");
        return 0;
    }

    *response_len = (uint16_t)APP_MAP_MATRIX_BYTES;
    memcpy(response_buf, s_map_matrix, *response_len);
    return 1;
}

/**
 * @brief Get pointer to map data buffer
 * @return Pointer to map data
 */
uint8_t *app_map_data_ptr(void)
{
    return (uint8_t *)s_map_matrix;
}

/**
 * @brief Get map data size
 * @return Size of map data in bytes
 */
uint32_t app_map_data_size(void)
{
    return (uint32_t)APP_MAP_MATRIX_BYTES;
}

/**
 * @brief Write temporary creep parameters
 * @param creep_params Input parameter structure
 * @return 1 on success, 0 on failure
 */
uint8_t app_creep_write_temp_params(app_creep_params *creep_params)
{
    if (creep_params == NULL) {
        MX_LOGE(CALI_TAG, "Creep temp write bad arg");
        return 0;
    }

    s_temp_creep.creep_strength = creep_params->creep_strength;
    s_temp_creep.creep_level = creep_params->creep_level;
    app_creep_apply_runtime(&s_temp_creep);

    MX_LOGI(CALI_TAG, "Creep temp updated: strength=%u level=%u",
            (unsigned int)s_temp_creep.creep_strength,
            (unsigned int)s_temp_creep.creep_level);
    return 1;
}

/**
 * @brief Save creep parameters to Flash
 * @param creep_params Input parameter structure
 * @return 1 on success, 0 on failure
 */
uint8_t app_creep_save_params(app_creep_params *creep_params)
{
    if (!app_creep_write_temp_params(creep_params)) {
        return 0;
    }

    if (!flash_port_creep_save(&s_temp_creep)) {
        return 0;
    }

    return 1;
}

/**
 * @brief Clear creep parameters
 * @return 1 on success, 0 on failure
 */
uint8_t app_creep_clear_params(void)
{
    app_creep_set_default(&s_temp_creep);
    app_creep_apply_runtime(&s_temp_creep);

    if (!flash_port_creep_clear()) {
        MX_LOGE(CALI_TAG, "Creep clear failed");
        return 0;
    }

    return 1;
}

/**
 * @brief Read device information from Flash
 * @param dev_info Output structure
 * @return 1 on success, 0 on failure
 */
uint8_t app_device_get_info(app_dev_info *dev_info)
{
    if (dev_info == NULL) {
        MX_LOGE(CALI_TAG, "Device info get bad arg");
        return 0;
    }

    if (!flash_port_device_info_load(dev_info)) 
    {
        app_device_info_set_default(dev_info);
        if (!app_device_set_info(dev_info)) {
            MX_LOGW(CALI_TAG, "Device info load default save failed");
        }
        return 1;
    }

    app_device_info_normalize(dev_info);
    if (!app_device_info_is_valid(dev_info)) 
    {
        app_device_info_set_default(dev_info);
        if (!app_device_set_info(dev_info)) {
            MX_LOGW(CALI_TAG, "Device info repair save failed");
        }
    }

    return 1;
}

/**
 * @brief Write device information to Flash
 * @param dev_info Input structure
 * @return 1 on success, 0 on failure
 */
uint8_t app_device_set_info(const app_dev_info *dev_info)
{
    app_dev_info flash_info;

    if (dev_info == NULL) {
        MX_LOGE(CALI_TAG, "Device info set bad arg");
        return 0;
    }

    memcpy(&flash_info, dev_info, sizeof(flash_info));
    app_device_info_normalize(&flash_info);
    if (!app_device_info_is_valid(&flash_info)) {
        MX_LOGE(CALI_TAG, "Device info set invalid data");
        return 0;
    }

    if (!flash_port_device_info_save(&flash_info)) {
        MX_LOGE(CALI_TAG, "Device info save failed");
        return 0;
    }

    return 1;
}

/**
 * @brief Read creep parameters from Flash, load defaults if invalid
 * @param creep_params Output structure
 * @return 1 on success, 0 on failure
 */
uint8_t app_creep_read_params(app_creep_params *creep_params)
{
    if (creep_params == NULL) {
        MX_LOGE(CALI_TAG, "Creep read bad arg");
        return 0;
    }

    if (!flash_port_creep_load(creep_params)) {
        app_creep_set_default(creep_params);
        if (!flash_port_creep_save(creep_params)) {
            MX_LOGE(CALI_TAG, "Creep read default save failed");
            return 0;
        }
    }

    s_temp_creep = *creep_params;
    app_creep_apply_runtime(&s_temp_creep);
    return 1;
}

/**
 * @brief Invalidate calibration runtime state
 */
void app_calibration_invalidate_runtime(void)
{
#if APP_CALI_USE_PROJECT_RUNTIME_FLAGS
    pressure_params = NULL;
    g_check_cali = false;
    g_is_creep_enable = false;
#endif
}

/**
 * @brief Request reset of runtime calibration logic
 * This will be applied after the next pressure measurementis taken.
 * 
 */
void app_calibration_request_reset_effect(void)
{
#if APP_CALI_USE_PROJECT_RUNTIME_FLAGS
    g_reset_takeEffect = true;
    g_is_creep_enable = false;
#endif
}

/**
 * @brief Check if device is calibrated
 * @return 1 if calibrated, 0 otherwise
 */
uint8_t app_calibration_check(void)
{
    app_math_cali_t flash_params;

    if (!flash_port_math_load(&flash_params)) 
    {
        MX_LOGW(CALI_TAG, "Check calibration state failed, treat as uncalibrated");
        return 0;
    }

    return (uint8_t)(flash_params.check_cail == APP_CALI_STATUS_CALIBRATED);
}

/**
 * @brief Get global calibration status
 * @return Value of g_check_cali
 */
uint8_t app_calibration_get_cail_status(void)
{
    return g_check_cali;
}

/**
 * @brief Set calibration status
 * @param calibrated 1 to mark as calibrated, 0 otherwise
 * @return 1 on success, 0 on failure
 */
uint8_t app_calibration_set(uint8_t calibrated)
{
    s_temp_cali.check_cail = calibrated ? APP_CALI_STATUS_CALIBRATED : APP_CALI_STATUS_UNCALIBRATED;
    if (!flash_port_math_save(&s_temp_cali)) {
        return 0;
    }
    g_check_cali = (calibrated != 0);
    return 1;
}

/**
 * @brief Initialize the calibration module and underlying flash port.
 * @param flash_ops Pointer to flash operations mapping.
 */
void app_calibration_init(const struct flash_port_ops_t *flash_ops)
{
    if (flash_ops != NULL) {
        flash_port_init((const flash_port_ops_t *)flash_ops);
    }
    
    app_calibration_reset_init();
}

/**
 * @brief Initialize all calibration components and states
 */
void app_calibration_reset_init(void)
{
    uint16_t min_trig, max_trig, div_trig, max_disp, sensor_type;
    app_math_cali_t cali_param;

    g_check_cali = (app_calibration_check() != 0);
    g_is_creep_enable = true;

    if (g_check_cali) {
        if (!app_math_init(&min_trig, &max_trig, &div_trig, &max_disp, &sensor_type)) {
            MX_LOGW(CALI_TAG, "Math init failed, fallback to default");
            (void)app_math_clear_params();
            g_check_cali = false;
        }

        pressure_params = app_pressure_init();
        if (pressure_params == NULL || pressure_params->num_points < 2) {
            MX_LOGE(CALI_TAG, "Pressure params invalid");
            g_check_cali = false;
        }

        if (!app_map_init()) {
            MX_LOGE(CALI_TAG, "Map init failed");
            g_check_cali = false;
        }
    } else {
        (void)app_math_clear_params();
        pressure_params = NULL;
    }

    (void)app_creep_read_params(&s_temp_creep);

    app_math_get_temp_params(&cali_param);

    if (g_check_cali) {
        g_sensor_data_type = cali_param.sensor_data_type;
    } else {
        g_sensor_data_type = SENSOR_BIT_8;
    }
    
    // Default override to 8bit (from original code)
    g_sensor_data_type = SENSOR_BIT_8;
    cali_param.max_display_value = 255; 
	
	g_buf[0] = cali_param.max_trigger_res_value;
	g_buf[1] = cali_param.min_trigger_res_value;
	g_buf[2] = cali_param.div_trigger_res_value;
	g_buf[3] = MAX_DISPLAY_ADC_VALUE;
	g_buf[4] = cali_param.max_display_value;
	
    math_resi_init(cali_param.max_trigger_res_value,
                   cali_param.min_trigger_res_value,
                   cali_param.div_trigger_res_value,
                   MAX_DISPLAY_ADC_VALUE,
                   cali_param.max_display_value);

    MX_LOGI(CALI_TAG, "Calibration params -> max_trig:%u, min_trig:%u, div_trig:%u, max_disp:%u, type:%u",
        (unsigned int)cali_param.max_trigger_res_value,
        (unsigned int)cali_param.min_trigger_res_value,
        (unsigned int)cali_param.div_trigger_res_value,
        (unsigned int)cali_param.max_display_value,
        (unsigned int)cali_param.sensor_data_type
    );   	   

    MX_LOGI(CALI_TAG, "g_sensor_data_type : %u, g_is_creep_enable : %u", 
        (unsigned int)g_sensor_data_type, 
        (unsigned int)g_is_creep_enable);
}

/**
 * @brief Process raw 16-bit sensor data against calibration map
 * @param in_raw_data Input raw sensor data matrix (MT_AX_NUM x MT_AY_NUM)
 * @param out_cali_data Output buffer to store processed data
 * @return 1 on success, 0 on invalid parameters
 */
void process_calibration_frame(const uint16_t in_raw_data[MT_AX_NUM][MT_AY_NUM],
                                  uint16_t out_cali_data[MT_AX_NUM][MT_AY_NUM])
{
    if (in_raw_data == NULL || out_cali_data == NULL) {
        return;
    }

    if (g_check_cali &&
        (pressure_params != NULL) &&
        (pressure_params->num_points >= 2U) &&
        (pressure_params->num_points <= MAX_PRESSURE_POINTS)) 
    {
        // 实时加载最新的标定配置
        cali_info.real_pressure_value = pressure_params->pressure_points;
        cali_info.cali_params = (uint16_t *)app_map_data_ptr();
        cali_info.cali_pressure_num = (uint8_t)pressure_params->num_points;
        cali_info.cali_data_out_bit_size = UINT16_DOUBLE;
        
        // 传递一维首地址给核心算法
        math_resi_cali((uint16_t *)in_raw_data,
                       &cali_info,
                       (uint16_t *)out_cali_data,
                       (uint32_t)(MT_AX_NUM * MT_AY_NUM));
    } 
    else 
    { 
        // 未标定或参数失效时，安全透传原始数据
        if ((const void *)in_raw_data != (const void *)out_cali_data) {
            memcpy((void *)out_cali_data, (const void *)in_raw_data, sizeof(uint16_t) * MT_AX_NUM * MT_AY_NUM);
        }
    }

    // 如果满足重置条件（收到新配置），重置底层标定组件
    if (g_reset_takeEffect) {
        g_reset_takeEffect = false;
        app_calibration_reset_init();
    }
}

/*
* @brief Check if sensor frame can be sent
* @return true if sensor frame can be sent, false otherwise
*/
static bool can_send_sensor_frame(void)
{
    if (!g_is_active_reporting && (g_sensor_frames_to_send <= 0)) {
        return false;
    }

    if (g_sensor_frames_to_send > 0) {
        g_sensor_frames_to_send--;
    }

    return true;
}

/**
 * @brief Send 8-bit sensor frame through Mozen protocol
 * @param sensor_data 8-bit sensor matrix
 * @return 1 on success, 0 on failure or busy
 */
uint8_t send_sensor_frame_8bit(const uint8_t (*sensor_data)[MT_AY_NUM])
{
    uint16_t payload_len = 0U;

    if (sensor_data == NULL || !can_send_sensor_frame()) {
        return 0;
    }

    // 防重入互斥锁
    if (s_is_payload_packing) {
        return 0;
    }
    s_is_payload_packing = true;

    if (!app_calibration_build_sensor8bit_payload(sensor_data, sensor_data_payload, (uint16_t)sizeof(sensor_data_payload), &payload_len)) {
        s_is_payload_packing = false;
        return 0;
    }

    (void)mozen_protocol_send_frame(&g_mozen_prot, 0x01U, sensor_data_payload, payload_len, false);
    
    s_is_payload_packing = false;
    return 1;
}

/**
 * @brief Send 16-bit sensor frame through Mozen protocol
 * @param sensor_data 16-bit sensor matrix
 * @return 1 on success, 0 on failure or busy
 */
uint8_t send_sensor_frame_16bit(const uint16_t (*sensor_data)[MT_AY_NUM])
{
    uint16_t payload_len = 0U;

    if (sensor_data == NULL || !can_send_sensor_frame()) {
        return 0;
    }

    // 防重入互斥锁
    if (s_is_payload_packing) {
        return 0;
    }
    s_is_payload_packing = true;

    if (!app_calibration_build_sensor16bit_payload(sensor_data, sensor_data_payload, (uint16_t)sizeof(sensor_data_payload), &payload_len)) {
        s_is_payload_packing = false;
        return 0;
    }

    (void)mozen_protocol_send_frame(&g_mozen_prot, 0x01U, sensor_data_payload, payload_len, false);
    
    s_is_payload_packing = false;
    return 1;
}

/* Legacy compatibility wrappers */
#if APP_CALI_ENABLE_LEGACY_API
uint8_t array_resi_params_write(const uint8_t *data, uint16_t len)
{
    return app_math_write_temp_params(data, len);
}

uint8_t array_resi_params_save(const uint8_t *data, uint16_t len)
{
    return app_math_save_params(data, len);
}

uint8_t clear_array_init_info(void)
{
    return app_math_clear_params();
}

uint8_t return_array_resi_init_info(uint8_t *response_buf, uint16_t *response_len)
{
    return app_math_read_temp_params(response_buf, response_len);
}

uint8_t return_flash_array_resi_init_info(uint8_t *response_buf, uint16_t *response_len)
{
    return app_math_read_solidified_params(response_buf, response_len);
}

pressure_params_t *init_pressure_params(void)
{
    return app_pressure_init();
}

uint8_t write_temp_pressure_params(const uint8_t *data, uint16_t len)
{
    return app_pressure_write_temp_params(data, len);
}

uint8_t read_temp_pressure_params(uint8_t *response_buf, uint16_t *response_len)
{
    return app_pressure_read_temp_params(response_buf, response_len);
}

uint8_t write_solidified_pressure_params(const uint8_t *data, uint16_t len)
{
    return app_pressure_save_params(data, len);
}

uint8_t read_solidified_pressure_params(uint8_t *response_buf, uint16_t *response_len)
{
    return app_pressure_read_solidified_params(response_buf, response_len);
}

uint8_t clear_pressure_params_from_flash(void)
{
    return app_pressure_clear_params();
}

void write_cali_params_to_ram(const uint8_t *data, uint16_t len)
{
    (void)app_map_write_temp_params(data, len);
}

uint8_t save_cali_params_to_flash(const uint8_t *data, uint16_t len)
{
    return app_map_save_params(data, len);
}

uint8_t clear_cali_params_from_flash(void)
{
    return app_map_clear_params();
}

uint8_t read_cali_params_from_ram(uint8_t *response_buf, uint16_t *response_len)
{
    return app_map_read_temp_params(response_buf, response_len);
}
#endif
