#include "acoustic_analyzer/core/fft_utils.h"
#include <cmath>
#include <fstream>

namespace acoustic {

void apply_preemphasis(const float* in, float* out, size_t n, float coef) {
    if (n == 0) return;
    out[0] = in[0];
    for (size_t i = 1; i < n; ++i) {
        out[i] = in[i] - coef * in[i - 1];
    }
}

void apply_hann_window(float* data, size_t n) {
    for (size_t i = 0; i < n; ++i) {
        float w = 0.5f - 0.5f * std::cos(2.0f * static_cast<float>(M_PI) * i / (n - 1));
        data[i] *= w;
    }
}

// Compute power spectrum via DFT.
// For n_fft = 2048 this is fast enough for offline/demo use.
// Twiddle factors are cached in a static lookup table per n_fft size.
void compute_power_spectrum(const float* frame, int n_fft, float* power_out) {
    int bins = n_fft / 2 + 1;

    // Build or reuse cached twiddle table for this n_fft
    static thread_local std::vector<float> cos_table;
    static thread_local std::vector<float> sin_table;
    static thread_local int cached_nfft = 0;

    if (cached_nfft != n_fft) {
        cached_nfft = n_fft;
        cos_table.resize(static_cast<size_t>(n_fft));
        sin_table.resize(static_cast<size_t>(n_fft));
        for (int k = 0; k < n_fft; ++k) {
            float angle = -2.0f * static_cast<float>(M_PI) * k / n_fft;
            cos_table[k] = std::cos(angle);
            sin_table[k] = std::sin(angle);
        }
    }

    for (int b = 0; b < bins; ++b) {
        float real = 0.0f;
        float imag = 0.0f;
        for (int n = 0; n < n_fft; ++n) {
            int idx = (b * n) % n_fft;
            real += frame[n] * cos_table[idx];
            imag += frame[n] * sin_table[idx];
        }
        power_out[b] = real * real + imag * imag;
    }
}

bool load_mel_filter_bank(const std::string& path, int n_mels, int n_fft_bins,
                          std::vector<float>& filter_bank) {
    std::ifstream f(path, std::ios::binary);
    if (!f) return false;
    size_t expected = static_cast<size_t>(n_mels) * n_fft_bins;
    filter_bank.resize(expected);
    f.read(reinterpret_cast<char*>(filter_bank.data()), expected * sizeof(float));
    return f.gcount() == static_cast<std::streamsize>(expected * sizeof(float));
}

} // namespace acoustic
