#include "et_half-wave_analysis.h"

#define DEBUG 0

#if DEBUG
#define DEBUG_PRINT(...) printf(__VA_ARGS__)
#else
#define DEBUG_PRINT(...)
#endif

/**
 * @brief Analyze a single half-cycle segment
 * @param cycle Pointer to et_half_cycle_t structure to store analysis results
 * @param point Pointer to input et_zc_point_t array
 * @param offset DC offset to subtract
 * @param start_idx Starting index of the segment
 * @param end_idx Ending index of the segment
 * @param is_first_half Flag indicating if this is the first half-cycle (1) or second (0)
 */
static void analyze_half_cycle(et_half_cycle_t *cycle, const et_zc_point_t *point, uint16_t offset, uint32_t start_idx,
                               uint32_t end_idx, int is_first_half)
{
    // Find peak in this segment
    float    peak_val = -FLT_MAX;
    uint32_t peak_idx = start_idx;

    // Also track the minimum value to determine if this is a positive or negative half-cycle
    float    min_val = FLT_MAX;
    uint32_t min_idx = start_idx;

    float    segment_sum   = 0.0f;
    uint32_t segment_count = 0;

    for (uint32_t j = start_idx; j < end_idx; j++)
    {
        float val    = (float)point[j].val - offset;
        segment_sum += val;
        segment_count++;

        if (val > peak_val)
        {
            peak_val = val;
            peak_idx = j;
        }

        if (val < min_val)
        {
            min_val = val;
            min_idx = j;
        }
    }

    // Determine if this is a positive or negative half-cycle based on which extreme is further from zero
    int      is_positive = (fabs(peak_val) >= fabs(min_val));
    float    extreme_val = is_positive ? peak_val : min_val;
    uint32_t extreme_idx = is_positive ? peak_idx : min_idx;

    // Store the absolute value of the extreme (peak or trough) - compute once
    float abs_extreme_val = fabs(extreme_val);

    // Update overall cycle data
    if (is_first_half)
    {
        cycle->pos_cycle_mean = segment_sum / segment_count;
        cycle->pos_sample_cnt = segment_count;

        if (abs_extreme_val > cycle->pos_cycle_peak)
        {
            cycle->pos_cycle_peak = abs_extreme_val;
            cycle->pos_peak_idx   = extreme_idx;
            DEBUG_PRINT("New positive peak: %f\n", cycle->pos_cycle_peak);
        }
    }
    else
    {
        cycle->neg_cycle_mean = segment_sum / segment_count;
        cycle->neg_sample_cnt = segment_count;

        if (abs_extreme_val > cycle->neg_cycle_peak)
        {
            cycle->neg_cycle_peak = abs_extreme_val;
            cycle->neg_peak_idx   = extreme_idx;
            DEBUG_PRINT("New negative peak: %f\n", cycle->neg_cycle_peak);
        }
    }
}

/**
 * @brief Create and initialize a half-wave analysis structure
 * @param handle Pointer to et_half_cycle_analysis_t structure
 * @param full_cycle_count Maximum number of full cycles to analyze
 * @param average_count Number of full cycles to use for calculating average metrics
 * @return int 0 on success, -1 on failure
 */
int et_half_wave_analysis_create(et_half_cycle_analysis_t *handle, uint32_t full_cycle_count, uint32_t average_count)
{
    if (handle == NULL)
        return -1;

    // Each full cycle has three zero crossing points (start, middle, end)
    uint32_t max_pos = 3 * full_cycle_count - 1;

    // Initialize all fields to zero
    handle->full_cycle_max_size = full_cycle_count;
    handle->average_count       = average_count;
    handle->mean_diff           = 0.0f;
    handle->mean_ratio          = 0.0f;
    handle->peak_ratio          = 0.0f;
    handle->peak_diff           = 0.0f;
    handle->width_ratio         = 0.0f;
    handle->width_diff          = 0.0f;

    handle->half_cycle = (et_half_cycle_t *)malloc(sizeof(et_half_cycle_t) * full_cycle_count);
    if (handle->half_cycle == NULL)
        return -1;

    if (et_zero_cross_create(&handle->zc, max_pos) != 0)
    {
        free(handle->half_cycle);
        return -1;
    }
    return 0;
}

/**
 * @brief Destroy a half-wave analysis structure and free allocated memory
 * @param handle Pointer to et_half_cycle_analysis_t structure to destroy
 */
void et_half_wave_analysis_destroy(et_half_cycle_analysis_t *handle)
{
    if (handle != NULL)
    {
        if (handle->half_cycle)
            free(handle->half_cycle);
        et_zero_cross_destroy(&handle->zc);
    }
}

/**
 * @brief Analyze half-wave characteristics from zero crossing points
 * @param handle Pointer to et_half_cycle_analysis_t structure for storing results
 * @param point Pointer to input et_zc_point_t array
 * @param length Number of samples
 * @param offset DC offset to subtract
 * @return int 0 on success, -1 on failure, -2 if insufficient zero crossings
 * @note The calculation results will not be queued. New calculation results will clear the previous results.
 *       To maintain averaging over average_count cycles, ensure sufficient points are provided.
 */
int et_half_cycle_analyze_point(et_half_cycle_analysis_t *handle, const et_zc_point_t *point, uint32_t length,
                                uint16_t offset)
{
    if (handle == NULL || point == NULL || length < 2)
        return -1;

    // Reset analysis results
    memset(handle->half_cycle, 0, sizeof(et_half_cycle_t) * handle->full_cycle_max_size);
    handle->mean_diff   = 0.0f;
    handle->mean_ratio  = 0.0f;
    handle->peak_ratio  = 0.0f;
    handle->peak_diff   = 0.0f;
    handle->width_ratio = 0.0f;
    handle->width_diff  = 0.0f;

    // Get zero crossing positions using existing function
    uint32_t zc_count = et_zero_cross_pos_point(&handle->zc, point, length, offset);

    // Analyze consecutive half-cycle pairs
    // Need at least 3 zero crossing points to define a complete cycle
    if (zc_count < 3)
        return -2;

    uint32_t full_cycle_count = 0;
    for (uint32_t i = 0; i <= zc_count - 3; i++)
    {
        // Only analyze the first pair of consecutive half-cycles (A~B and B~C)
        // First half-cycle (from A to B)
        uint32_t first_start = (uint32_t)handle->zc.pos_buf[i];
        uint32_t first_end   = (uint32_t)handle->zc.pos_buf[i + 1]; // First zero crossing (point B)

        // Second half-cycle (from B to C)
        uint32_t second_start = (uint32_t)handle->zc.pos_buf[i + 1]; // First zero crossing (point B)
        uint32_t second_end   = (uint32_t)handle->zc.pos_buf[i + 2]; // Second zero crossing (point C)

        // Analyze the first half-cycle (A to B)
        analyze_half_cycle(&handle->half_cycle[full_cycle_count], point, offset, first_start, first_end, 1);

        // Analyze the second half-cycle (B to C)
        analyze_half_cycle(&handle->half_cycle[full_cycle_count], point, offset, second_start, second_end, 0);
        full_cycle_count++;
    }

    uint32_t min_cnt = handle->average_count > full_cycle_count ? full_cycle_count : handle->average_count;

    float pos_mean_total   = 0.0f;
    float neg_mean_total   = 0.0f;
    float pos_peak_total   = 0.0f;
    float neg_peak_total   = 0.0f;
    float pos_sample_total = 0.0f;
    float neg_sample_total = 0.0f;

    // Calculate totals for averaging
    for (uint32_t i = 0; i < min_cnt; i++)
    {
        handle->mean_diff  += (handle->half_cycle[i].pos_cycle_mean - handle->half_cycle[i].neg_cycle_mean);
        handle->peak_diff  += (handle->half_cycle[i].pos_cycle_peak - handle->half_cycle[i].neg_cycle_peak);
        handle->width_diff += (handle->half_cycle[i].pos_sample_cnt - handle->half_cycle[i].neg_sample_cnt);
        pos_sample_total   += handle->half_cycle[i].pos_sample_cnt;
        neg_sample_total   += handle->half_cycle[i].neg_sample_cnt;
        pos_peak_total     += handle->half_cycle[i].pos_cycle_peak;
        neg_peak_total     += handle->half_cycle[i].neg_cycle_peak;
        pos_mean_total     += handle->half_cycle[i].pos_cycle_mean;
        neg_mean_total     += handle->half_cycle[i].neg_cycle_mean;
    }

    // Calculate ratios with division by zero protection
    if (fabsf(neg_peak_total) < FLT_EPSILON)
        handle->peak_ratio = 0.0f;
    else
        handle->peak_ratio = pos_peak_total / neg_peak_total;

    if (fabsf(neg_mean_total) < FLT_EPSILON)
        handle->mean_ratio = 0.0f;
    else
        handle->mean_ratio = pos_mean_total / neg_mean_total;

    if (fabsf(neg_sample_total) < FLT_EPSILON)
        handle->width_ratio = 0.0f;
    else
        handle->width_ratio = pos_sample_total / neg_sample_total;

    return 0;
}

/**
 * @brief Calculate the difference between positive and negative half-cycle peak values
 * @param handle Pointer to et_half_cycle_analysis_t structure with analysis results
 * @return float Difference between positive and negative half-cycle peak values
 */
float et_half_wave_peak_diff(const et_half_cycle_analysis_t *handle)
{
    if (handle == NULL)
        return 0.0f;
    return handle->peak_diff;
}

/**
 * @brief Calculate the ratio between positive and negative half-cycle peak values
 * @param handle Pointer to et_half_cycle_analysis_t structure with analysis results
 * @return float Ratio between positive and negative half-cycle peak values
 */
float et_half_wave_peak_ratio(const et_half_cycle_analysis_t *handle)
{
    if (handle == NULL)
        return 0.0f;

    return handle->peak_ratio;
}

/**
 * @brief Calculate the ratio between positive and negative half-cycle mean values
 * @param handle Pointer to et_half_cycle_analysis_t structure with analysis results
 * @return float Ratio between positive and negative half-cycle mean values
 */
float et_half_wave_mean_ratio(const et_half_cycle_analysis_t *handle)
{
    if (handle == NULL)
        return 0.0f;

    return handle->mean_ratio;
}

/**
 * @brief Calculate the difference between positive and negative half-cycle mean values
 * @param handle Pointer to et_half_cycle_analysis_t structure with analysis results
 * @return float Difference between positive and negative half-cycle mean values
 */
float et_half_wave_mean_diff(const et_half_cycle_analysis_t *handle)
{
    if (handle == NULL)
        return 0.0f;

    return handle->mean_diff;
}

/**
 * @brief Calculate the ratio of sample counts between positive and negative half-cycles
 * @param handle Pointer to et_half_cycle_analysis_t structure with analysis results
 * @return float Ratio of positive to negative half-cycle sample counts
 */
float et_half_wave_sample_count_ratio(const et_half_cycle_analysis_t *handle)
{
    if (handle == NULL)
        return 0.0f;

    return handle->width_ratio;
}
