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

namespace acoustic {

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

void apply_hann_window(float* data, std::size_t n) {
    for (std::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;
    }
}

namespace {

// Iterative Cooley-Tukey complex FFT (power-of-2 sizes only).
struct Cpx { float r = 0.0f, i = 0.0f; };

void fft_inplace(Cpx* data, int n, bool inverse) {
    int j = 0;
    for (int i = 1; i < n; ++i) {
        int bit = n >> 1;
        for (; j & bit; bit >>= 1) j ^= bit;
        j ^= bit;
        if (i < j) std::swap(data[i], data[j]);
    }
    for (int len = 2; len <= n; len <<= 1) {
        float ang = 2.0f * static_cast<float>(M_PI) / len * (inverse ? 1.0f : -1.0f);
        float wlen_r = std::cos(ang);
        float wlen_i = std::sin(ang);
        for (int i = 0; i < n; i += len) {
            float wr = 1.0f, wi = 0.0f;
            for (int k = 0; k < len / 2; ++k) {
                int u = i + k;
                int v = i + k + len / 2;
                float ur = data[u].r, ui = data[u].i;
                float vr = data[v].r * wr - data[v].i * wi;
                float vi = data[v].r * wi + data[v].i * wr;
                data[u].r = ur + vr;
                data[u].i = ui + vi;
                data[v].r = ur - vr;
                data[v].i = ui - vi;
                float wnr = wr * wlen_r - wi * wlen_i;
                float wni = wr * wlen_i + wi * wlen_r;
                wr = wnr;
                wi = wni;
            }
        }
    }
    if (inverse) {
        float inv_n = 1.0f / n;
        for (int i = 0; i < n; ++i) {
            data[i].r *= inv_n;
            data[i].i *= inv_n;
        }
    }
}

} // anonymous namespace

void compute_power_spectrum(const float* frame, int n_fft, float* power_out) {
    int bins = n_fft / 2 + 1;
    std::vector<Cpx> data(n_fft);
    for (int i = 0; i < n_fft; ++i) {
        data[i].r = frame[i];
        data[i].i = 0.0f;
    }
    fft_inplace(data.data(), n_fft, false);
    for (int i = 0; i < bins; ++i) {
        power_out[i] = data[i].r * data[i].r + data[i].i * data[i].i;
    }
}

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;
    std::size_t expected = static_cast<std::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
