/*
 * @Date: 2025-07-07 09:47:50
 * @Author: mypx
 * @LastEditors: mypx mypx_coder@163.com
 * @LastEditTime: 2025-09-19 09:09:14
 * @FilePath: et_ema_filter.c
 * @Description: Exponential Moving Average (EMA) filter implementation
 * Copyright (c) 2025 by mypx, All Rights Reserved.
 */
#include "et_ema_filter.h"
#include <string.h>

/**
 * @brief Initialize EMA filter for real-time processing
 * 
 * This function initializes the EMA filter with the given smoothing factor.
 * The filter starts in uninitialized state and will be initialized
 * with the first input sample.
 * 
 * @param ema pointer to EMA filter structure
 * @param alpha smoothing factor (0 < alpha < 1)
 * @return 0 on success, -1 on error
 */
int et_ema_filter_init(et_ema_filter_t *ema, float alpha)
{
    if (ema == NULL)
    {
        return -1;
    }

    // validate alpha range
    if (alpha <= 0.0f || alpha >= 1.0f)
    {
        return -1;
    }

    ema->alpha          = alpha;
    ema->last_output    = 0.0f;
    ema->is_initialized = 0;

    return 0;
}

/**
 * @brief Apply EMA filter to single input value (real-time)
 * 
 * This function implements the standard EMA algorithm:
 * y[n] = alpha * x[n] + (1 - alpha) * y[n-1]
 * 
 * For the first sample, the output equals the input.
 * 
 * @param ema pointer to EMA filter structure
 * @param new_input new input value to filter
 * @return filtered output value
 */
float et_ema_filter_process(et_ema_filter_t *ema, float new_input)
{
    if (ema == NULL)
    {
        return 0.0f;
    }

    // handle first sample initialization
    if (!ema->is_initialized)
    {
        ema->last_output    = new_input;
        ema->is_initialized = 1;
        return new_input;
    }

    // apply EMA formula: y[n] = alpha * x[n] + (1 - alpha) * y[n-1]
    float current_output = ema->alpha * new_input + (1.0f - ema->alpha) * ema->last_output;
    ema->last_output     = current_output;

    return current_output;
}

/**
 * @brief Reset EMA filter state
 * 
 * This function resets the filter to uninitialized state,
 * clearing all previous filtering history.
 * 
 * @param ema pointer to EMA filter structure
 * @return 0 on success, -1 on error
 */
int et_ema_filter_reset(et_ema_filter_t *ema)
{
    if (ema == NULL)
    {
        return -1;
    }

    ema->last_output    = 0.0f;
    ema->is_initialized = 0;

    return 0;
}

/**
 * @brief Apply EMA filter to array of input data
 * 
 * This function applies EMA filtering to an array of input data.
 * The first output sample equals the first input sample.
 * Subsequent samples follow the EMA formula.
 * 
 * @param config pointer to array configuration structure
 * @return 0 on success, -1 on error
 */
int et_ema_array_filter_process(const et_ema_array_config_t *config)
{
    if (config == NULL || config->input == NULL || config->output == NULL)
    {
        return -1;
    }

    if (config->data_size == 0)
    {
        return -1;
    }

    // validate alpha range
    if (config->alpha <= 0.0f || config->alpha >= 1.0f)
    {
        return -1;
    }

    // first sample: output equals input
    config->output[0] = config->input[0];

    // apply EMA to remaining samples
    for (uint32_t i = 1; i < config->data_size; i++)
    {
        // EMA formula: y[n] = alpha * x[n] + (1 - alpha) * y[n-1]
        config->output[i] = config->alpha * config->input[i] + (1.0f - config->alpha) * config->output[i - 1];
    }

    return 0;
}
