#include "et_trend.h"
#include "et_log.h"
#include <float.h>
#include <stdlib.h>

/**
 * @brief Find the index of minimum value in array
 * 
 * @param data Input data array
 * @param len Length of the array
 * @return uint8_t Index of the minimum value
 */
static uint8_t find_min_index(const float *data, uint8_t len)
{
    uint8_t min_idx = 0;
    for (uint8_t i = 1; i < len; i++)
    {
        if (data[i] < data[min_idx])
        {
            min_idx = i;
        }
    }
    return min_idx;
}

/**
 * @brief Find the index of maximum value in array
 * 
 * @param data Input data array
 * @param len Length of the array
 * @return uint8_t Index of the maximum value
 */
static uint8_t find_max_index(const float *data, uint8_t len)
{
    uint8_t max_idx = 0;
    for (uint8_t i = 1; i < len; i++)
    {
        if (data[i] > data[max_idx])
        {
            max_idx = i;
        }
    }
    return max_idx;
}

ET_TrendResult et_trend_detect(const et_trend_t *trend, float *out_avg_diff)
{
    // Input parameter validation
    if (!trend || !trend->data || trend->length < trend->config.window_size)
    {
        ET_ERR("Invalid input: trend=%p, data=%p, window_size=%d, length=%d", trend, trend ? trend->data : NULL,
               trend ? trend->config.window_size : 0, trend ? trend->length : 0);
        return TREND_INVALID;
    }

    // Position window data (original time series, maintain order)
    const float *input       = trend->data + (trend->length - trend->config.window_size);
    uint8_t      window_size = trend->config.window_size;

    // Record index of outlier to exclude
    uint8_t exclude_idx = 0xFF; // Initial invalid index
    uint8_t valid_count = window_size;

    switch (trend->config.outlier_mode)
    {
    case OUTLIER_MIN:
    {
        exclude_idx = find_min_index(input, window_size);
        valid_count--;
        break;
    }
    case OUTLIER_MAX:
    {
        exclude_idx = find_max_index(input, window_size);
        valid_count--;
        break;
    }
    case OUTLIER_BOTH:
    {
        // First exclude minimum, then exclude maximum from remaining data
        uint8_t min_idx = find_min_index(input, window_size);
        // Construct temporary array excluding minimum
        float   temp[ET_TREND_WINDOW_MAX];
        uint8_t temp_len = 0;
        for (uint8_t i = 0; i < window_size; i++)
        {
            if (i != min_idx)
            {
                temp[temp_len++] = input[i];
            }
        }
        // Find index of maximum in temporary array and map back to original index
        uint8_t max_temp_idx = find_max_index(temp, temp_len);
        // Map back to original index (skip min_idx)
        exclude_idx = 0;
        for (uint8_t i = 0; i < window_size; i++)
        {
            if (i == min_idx)
                continue;
            if (exclude_idx == max_temp_idx)
            {
                exclude_idx = i;
                break;
            }
            exclude_idx++;
        }
        valid_count -= 2;
        break;
    }
    default: // OUTLIER_NONE
        break;
    }

    // Insufficient valid data after filtering
    if (valid_count < 2)
    {
        ET_DBG("Insufficient valid data after outlier exclusion: %d", valid_count);
        return TREND_NO_VALID_DATA;
    }

    // Collect valid data after filtering (maintain original order)
    float   valid_data[ET_TREND_WINDOW_MAX];
    uint8_t valid_idx = 0;
    for (uint8_t i = 0; i < window_size; i++)
    {
        if (i != exclude_idx)
        { // Skip outlier
            valid_data[valid_idx++] = input[i];
            ET_DBG("valid_data[%d]=%.3f", valid_idx - 1, valid_data[valid_idx - 1]);
        }
    }

    // Calculate average values of first and second halves (based on original time order)
    float   avg_start = 0.0f, avg_end = 0.0f;
    float   diff = 0.0f;
    uint8_t half = valid_count / 2;

    // First half (early data)
    for (uint8_t i = 0; i < half; i++)
    {
        avg_start += valid_data[i];
    }
    avg_start /= half;

    // Second half (later data)
    uint8_t second_half_count = valid_count - half;
    for (uint8_t i = half; i < valid_count; i++)
    {
        avg_end += valid_data[i];
    }
    avg_end /= second_half_count;

    diff = avg_end - avg_start;

    // Fallback logic for OUTLIER_MIN mode (only when filtered data cannot show trend)
    if (trend->config.outlier_mode == OUTLIER_MIN && diff > -trend->config.threshold)
    {
        ET_DBG("OUTLIER_MIN adjustment: diff=%.3f too small, using original input.", diff);
        // Re-calculate using original data
        uint8_t original_half = window_size / 2;
        avg_start             = 0.0f;
        for (uint8_t i = 0; i < original_half; i++)
        {
            avg_start += input[i];
        }
        avg_start /= original_half;

        avg_end                      = 0.0f;
        uint8_t original_second_half = window_size - original_half;
        for (uint8_t i = original_half; i < window_size; i++)
        {
            avg_end += input[i];
        }
        avg_end /= original_second_half;

        diff = avg_end - avg_start;
    }

    // Output difference value
    if (out_avg_diff)
    {
        *out_avg_diff = diff;
    }

    ET_DBG("avg_start=%.3f, avg_end=%.3f, diff=%.3f, threshold=%.3f", avg_start, avg_end, diff,
           trend->config.threshold);

    // 增加数据窗口信息输出
    ET_DBG("[TREND] Window data (%d samples):", window_size);
    for (uint8_t i = 0; i < window_size; i++)
    {
        ET_DBG("[%d]: %.3f", i, input[i]);
    }

    // 输出过滤后的有效数据
    ET_DBG("[TREND] Valid data after filtering (%d samples):", valid_count);
    for (uint8_t i = 0; i < valid_count; i++)
    {
        ET_DBG("[%d]: %.3f", i, valid_data[i]);
    }

    ET_INFO("[TREND] Result: %d (avg_start=%.3f, avg_end=%.3f, diff=%.3f)",
            (diff > trend->config.threshold)    ? TREND_INCREASING
            : (diff < -trend->config.threshold) ? TREND_DECREASING
                                                : TREND_STABLE,
            avg_start, avg_end, diff);

    // Determine trend
    if (diff > trend->config.threshold)
    {
        return TREND_INCREASING;
    }
    else if (diff < -trend->config.threshold)
    {
        return TREND_DECREASING;
    }
    else
    {
        return TREND_STABLE;
    }
}

int et_trend_init(et_trend_t *trend, et_trend_config_t *cfg)
{
    if (!trend || !cfg || cfg->window_size < ET_TREND_WINDOW_MIN || cfg->window_size > ET_TREND_WINDOW_MAX)
    {
        ET_ERR("Invalid input: cfg=%p, window_size=%d", cfg, cfg ? cfg->window_size : 0);
        return -1;
    }
    // Initialize configuration
    trend->config.window_size  = cfg->window_size;
    trend->config.threshold    = cfg->threshold;
    trend->config.outlier_mode = cfg->outlier_mode;

    return 0;
}
