#include "decimator.h"
#include <algorithm>
#include <limits>

// ── float* overload (legacy) ─────────────────────────────────────────

Decimator::Envelope Decimator::decimate(
    const float* t_raw, const float* v_raw,
    size_t N, int cols, double t_min, double t_max)
{
    Envelope env;
    if (N == 0 || cols <= 0) return env;

    if (static_cast<int>(N) <= cols) {
        env.t.reserve(N);
        env.vmin.reserve(N);
        env.vmax.reserve(N);
        for (size_t i = 0; i < N; ++i) {
            double t = static_cast<double>(t_raw[i]);
            if (t >= t_min && t <= t_max) {
                double v = static_cast<double>(v_raw[i]);
                env.t.push_back(t);
                env.vmin.push_back(v);
                env.vmax.push_back(v);
            }
        }
        return env;
    }

    env.t.resize(cols);
    env.vmin.resize(cols,  std::numeric_limits<double>::max());
    env.vmax.resize(cols, -std::numeric_limits<double>::max());

    double span = (t_max - t_min > 0) ? (t_max - t_min) : 1.0;
    size_t src = 0;
    while (src < N && static_cast<double>(t_raw[src]) < t_min) ++src;

    for (int col = 0; col < cols && src < N; ++col) {
        double cright = t_min + span * (col + 1) / cols;
        double sum_t = 0.0;
        size_t cnt = 0;
        while (src < N && static_cast<double>(t_raw[src]) < cright) {
            double v = static_cast<double>(v_raw[src]);
            if (v < env.vmin[col]) env.vmin[col] = v;
            if (v > env.vmax[col]) env.vmax[col] = v;
            sum_t += static_cast<double>(t_raw[src]);
            ++cnt; ++src;
        }
        env.t[col] = cnt > 0 ? sum_t / cnt
                              : t_min + span * (col + 0.5) / cols;
        if (cnt == 0) { env.vmin[col] = 0.0; env.vmax[col] = 0.0; }
    }

    while (!env.t.empty() && env.vmin.back() > env.vmax.back()) {
        env.t.pop_back(); env.vmin.pop_back(); env.vmax.pop_back();
    }
    return env;
}

// ── double* overload ─────────────────────────────────────────────────

Decimator::Envelope Decimator::decimate(
    const double* t_raw, const double* v_raw,
    size_t N, int cols, double t_min, double t_max)
{
    Envelope env;
    if (N == 0 || cols <= 0) return env;

    if (static_cast<int>(N) <= cols) {
        env.t.reserve(N);
        env.vmin.reserve(N);
        env.vmax.reserve(N);
        for (size_t i = 0; i < N; ++i) {
            if (t_raw[i] >= t_min && t_raw[i] <= t_max) {
                env.t.push_back(t_raw[i]);
                env.vmin.push_back(v_raw[i]);
                env.vmax.push_back(v_raw[i]);
            }
        }
        return env;
    }

    env.t.resize(cols);
    env.vmin.resize(cols,  std::numeric_limits<double>::max());
    env.vmax.resize(cols, -std::numeric_limits<double>::max());

    double span = (t_max - t_min > 0) ? (t_max - t_min) : 1.0;
    size_t src = 0;
    while (src < N && t_raw[src] < t_min) ++src;

    for (int col = 0; col < cols && src < N; ++col) {
        double cright = t_min + span * (col + 1) / cols;
        double sum_t = 0.0;
        size_t cnt = 0;
        while (src < N && t_raw[src] < cright) {
            if (v_raw[src] < env.vmin[col]) env.vmin[col] = v_raw[src];
            if (v_raw[src] > env.vmax[col]) env.vmax[col] = v_raw[src];
            sum_t += t_raw[src];
            ++cnt; ++src;
        }
        env.t[col] = cnt > 0 ? sum_t / cnt
                              : t_min + span * (col + 0.5) / cols;
        if (cnt == 0) { env.vmin[col] = 0.0; env.vmax[col] = 0.0; }
    }

    while (!env.t.empty() && env.vmin.back() > env.vmax.back()) {
        env.t.pop_back(); env.vmin.pop_back(); env.vmax.pop_back();
    }
    return env;
}

// ── Overview downsampling ─────────────────────────────────────────────

std::vector<std::pair<double,double>>
overview_downsample(const double* t, const double* v, size_t N, size_t target)
{
    std::vector<std::pair<double,double>> out;
    if (N == 0 || target == 0) return out;
    if (N <= target) {
        out.reserve(N);
        for (size_t i = 0; i < N; ++i) out.emplace_back(t[i], v[i]);
        return out;
    }
    out.reserve(target);
    size_t step = std::max<size_t>(N / target, 1);
    for (size_t i = 0; i < N && out.size() < target; i += step) {
        size_t end = std::min(i + step, N);
        double st = 0, sv = 0;
        for (size_t j = i; j < end; ++j) { st += t[j]; sv += v[j]; }
        size_t cnt = end - i;
        out.emplace_back(st / cnt, sv / cnt);
    }
    return out;
}
