#include "transpose.h"
using Xybrid::Gadgets::Transpose;
using namespace Xybrid::Data;

#include "util/strings.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/layoutgadget.h"
#include "ui/gadgets/knobgadget.h"
using namespace Xybrid::UI;

#include <cmath>

#include <QCborMap>

// clazy:excludeall=non-pod-global-static
RegisterPlugin(Transpose, {
    i->id = "gadget:transpose";
    i->displayName = "Transpose";
    i->category = "Gadget";
})

Transpose::Transpose() {

}

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

void Transpose::process() {
    int off = octave.load()*12 + amount.load();

    auto in = std::static_pointer_cast<CommandPort>(port(Port::Input, Port::Command, 0));
    auto out = std::static_pointer_cast<CommandPort>(port(Port::Output, Port::Command, 0));
    if (off == 0) { // a
        if (out->passthroughTo.expired()) out->passthroughTo = in;
        return;
    }
    if (!out->passthroughTo.expired()) out->passthroughTo.reset();
    in->pull();
    out->pull();

    out->data = reinterpret_cast<uint8_t*>(audioEngine->tickAlloc(in->size));
    out->size = in->size;
    memcpy(out->data, in->data, in->size); // precopy

    size_t mi = 0;
    while (out->size >= mi+5) {
        int16_t& n = reinterpret_cast<int16_t&>(out->data[mi+2]);
        if (n > -1) {
            int nn = n;
            nn += off;
            if (nn < 0 || nn > 32767) nn = -1;
            n = static_cast<int16_t>(nn);
        }
        mi += 5 + out->data[mi+4]*2;
    }
}

void Transpose::saveData(QCborMap& m) const {
    m[qs("amount")] = QCborValue(amount);
    m[qs("octave")] = QCborValue(octave);
}

void Transpose::loadData(const QCborMap& m) {
    auto oct = m.value("octave");
    if (oct.isUndefined()) { // convert from single value
        int a = static_cast<int>(m.value("amount").toInteger(0));
        int s = a < 0 ? -1 : 1;
        a = std::abs(a);
        octave = (a-(a%12))/12*s;
        amount = a%12 * s;
        return;
    }
    octave = static_cast<int>(oct.toInteger(0));
    amount = static_cast<int>(m.value("amount").toInteger(0));
}

void Transpose::onGadgetCreated() {
    if (!obj) return;
    obj->showPluginName = false;
    auto l = (new LayoutGadget(obj))->setMetrics(8, 10);

    (new KnobGadget(l))->bind(amount)->setLabel("Transpose")->setTextFunc(KnobGadget::textOffset)->setRange(-12, 12, 1, KnobGadget::MedStep);
    (new KnobGadget(l))->bind(octave)->setLabel("Octave")->setTextFunc(KnobGadget::textOffset)->setRange(-5, 5, 1, KnobGadget::BigStep);
}
