#pragma once

#include <cmath>
#include <cstddef>
#include <array>

#include "data/audioframe.h"
#include "data/sample.h"

namespace Xybrid::NodeLib {
    const constexpr size_t LUT_LEVELS = 16;
    const constexpr size_t LUT_TAPS = 8;
    const constexpr ptrdiff_t LUT_HTAPS = LUT_TAPS/2-1;//static_cast<ptrdiff_t>(LUT_TAPS - (LUT_TAPS+0.5)/2);
    const constexpr size_t LUT_STEPS = 1024;
    extern const std::array<std::array<std::array<double, LUT_TAPS>, LUT_LEVELS>, LUT_STEPS> resamplerLUT;

    inline Data::AudioFrame resamp(Data::Sample* smp, double pos, double rate [[maybe_unused]]) {
        auto loop = smp->loopStart >= 0;
        auto len = static_cast<ptrdiff_t>(smp->length());
        auto ls = static_cast<ptrdiff_t>(smp->loopStart);
        auto le = static_cast<ptrdiff_t>(smp->loopEnd);
        auto ll = le - ls;

        double ip = std::floor(pos);
        auto& pt = NodeLib::resamplerLUT[static_cast<size_t>((pos - ip)*NodeLib::LUT_STEPS) % NodeLib::LUT_STEPS][static_cast<size_t>(std::clamp(std::floor(rate - 0.00001), 0.0, (LUT_LEVELS-1)*1.0))];

        Data::AudioFrame out(0.0);

        auto ii = static_cast<ptrdiff_t>(ip) - LUT_HTAPS;
        for (size_t i = 0; i < 8; i++) {
            if (loop && ii >= le) ii = ((ii - ls) % ll) + ls;
            else if (ii >= len) return out; // we can early-out here
            if (ii >= 0) out += (*smp)[static_cast<size_t>(ii)] * pt[i];
            ii++;
        }

        return out;
    }
}
