#include "testsynth.h"
using Xybrid::Instruments::TestSynth;
using namespace Xybrid::Data;

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

#include "data/porttypes.h"

#include "config/pluginregistry.h"
using namespace Xybrid::Config;

#include "audio/audioengine.h"
using namespace Xybrid::Audio;

#include "ui/patchboard/nodeobject.h"
#include "ui/gadgets/knobgadget.h"
using namespace Xybrid::UI;

#include <cmath>

#include <QDebug>

#include "nodelib/resampler.h"

namespace {
    bool _ = PluginRegistry::enqueueRegistration([] {
        auto i = std::make_shared<PluginInfo>();
        i->id = "plug:testsynth";
        i->displayName = "The Testron";
        i->category = "Instrument";
        //i->hidden = true;
        i->createInstance = []{ return std::make_shared<TestSynth>(); };
        PluginRegistry::registerPlugin(i);
        //inf = i;
    });

    const double PI = std::atan(1)*4;
    const double SEMI = std::pow(2.0, 1.0/12.0);

    double fOsc(double& time) {
        time = std::fmod(time, 2.0);

        auto a = 0.0;
        a += std::sin(time * PI*2);
        a += std::sin(time * PI) * std::pow(.75, 4);
        a += std::sin(time * PI*4) * std::pow(.75, 4);
        a += std::sin(time * PI*8) * std::pow(.25, 4);

        return a;
    }
}

TestSynth::TestSynth() {
    //
}

void TestSynth::init() {
    addPort(Port::Input, Port::Command, 0);
    addPort(Port::Output, Port::Audio, 0);
}

void TestSynth::reset() {
    osc = 0.0;
    cvol = 0.0;
    tvol = 0.0;
    noteId = 0;
}

void TestSynth::process() {
    auto cp = std::static_pointer_cast<CommandPort>(port(Port::Input, Port::Command, 0));
    cp->pull();
    auto p = std::static_pointer_cast<AudioPort>(port(Port::Output, Port::Audio, 0));
    p->pull();

    if (!project) return;
    if (project->samples.empty()) return;
    auto smp = *(project->samples.begin());

    size_t mi = 0;
    while (cp->dataSize >= mi+5) {
        uint16_t id = reinterpret_cast<uint16_t&>(cp->data[mi]);
        int16_t n = reinterpret_cast<int16_t&>(cp->data[mi+2]);
        if (n > -1) {
            noteId = id;
            note = n;
            tvol = 1.0;
            osc = 0;
        } else if (n < -1 && id == noteId) { // note off
            tvol = 0.0;
        }
        mi += 5 + cp->data[mi+4]*2;
    }

    if (tvol <= 0) return;
    size_t ts = audioEngine->curTickSize();

    double rate = static_cast<double>(smp->sampleRate) / static_cast<double>(audioEngine->curSampleRate());
    rate *= std::pow(SEMI, note - 60);
    //qDebug() << "rate" << rate << "note" << note;

    for (size_t s = 0; s < ts; s++) {
        double ip = std::floor(osc);
        double fp = osc - ip;
        size_t lutIndex = static_cast<size_t>(fp*NodeLib::LUT_STEPS) % NodeLib::LUT_STEPS;
        auto& pt = NodeLib::resamplerLUT[lutIndex];
        AudioFrame out(0.0);
        auto ii = static_cast<ptrdiff_t>(ip);
        for (size_t i = 0; i < 8; i++) {
            auto si = ii+static_cast<ptrdiff_t>(i);
            if (si >= 0 && si < static_cast<ptrdiff_t>(smp->length())) out += (*smp)[static_cast<size_t>(si)] * pt[i];
        }
        (*p)[s] = out;


        osc += rate;
    }

    /*for (size_t s = 0; s < ts; s++) {
        if (tvol > cvol) cvol += 64.0 / audioEngine->curSampleRate();
        else if (tvol < cvol) cvol -= 16.0 / audioEngine->curSampleRate();
        cvol = std::clamp(cvol, 0.0, 1.0);
        if (cvol == 0.0) { osc = 0.0; }
        float oscV = static_cast<float>(fOsc(osc) * std::pow(cvol*.5, 4));

        double enote = note + std::sin(lfo * PI*2) * 0.1;
        double freq = 440.0 * std::pow(SEMI, enote - (45+12));
        osc += freq / audioEngine->curSampleRate();

        lfo += 3.0 / audioEngine->curSampleRate();
        lfo = std::fmod(lfo,  1.0);

        p->bufL[s] = oscV;
        p->bufR[s] = oscV;
    }//*/
}

void TestSynth::onGadgetCreated() {
    /*if (!obj) return;
    auto k = new KnobGadget(obj);
    k->setPos(16, 16);
    static double dbl = 0.5;
    k->bind(dbl);*/
}

void TestSynth::onDoubleClick() {
    //
}
