/*
 * Filename:    svf.cpp
 *
 * Description: State Variable Filter
 *
 *
 * Version:
 * Created:     Fri Nov  1 23:36:50 2019
 * Revision:    None
 * Author:      Rachel Fae Fox (foxiepaws),fox@foxiepa.ws
 *
 */


#include "svf.h"
using Xybrid::Effects::SVF;
using namespace Xybrid::Data;

#include "util/strings.h"

#include "nodelib/basics.h"
using namespace Xybrid::NodeLib;

#include "data/audioframe.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/togglegadget.h"
#include "ui/gadgets/knobgadget.h"
using namespace Xybrid::UI;

#include <cmath>

#include <QCborMap>

// clazy:excludeall=non-pod-global-static
RegisterPlugin(SVF, {
    i->id = "fx:svf";
    i->oldIds = {"gadget:svf"};
    i->displayName = "Filter";
    i->category = "Effect";
})

SVF::SVF() { }

void SVF::init() {
    addPort(Port::Input, Port::Audio, 0);
    addPort(Port::Output, Port::Audio, 0);

    //addPort(Port::Input, Port::Parameter, 0);
    //auto p = Param("Cutoff", 0.0, 16000.0, 0.0);
    cutoff.makePort(this);
}

void SVF::reset() {
    filter.reset();
}

void SVF::release() {

}

void SVF::process() {
    auto in = std::static_pointer_cast<AudioPort>(port(Port::Input, Port::Audio, 0));
    auto out = std::static_pointer_cast<AudioPort>(port(Port::Output, Port::Audio, 0));
    in->pull();
    out->pull();

    auto r = filter.scaledResonance(resonance);
    auto c = cutoff.start();

    size_t ts = audioEngine->curTickSize();
    for (size_t f = 0; f < ts; f++) {
        AudioFrame inp = (*in)[f];

        filter.process(inp, c.next(), r);

        switch (mode) {
        case Low:
            (*out)[f] = filter.low;
            break;
        case High:
            (*out)[f] = filter.high;
            break;
        case Band:
            (*out)[f] = filter.band;
            break;
        case Notch:
            (*out)[f] = filter.notch;
            break;
        default:
            (*out)[f] = inp;
        }
    }
}

void SVF::saveData(QCborMap& m) const {
    //m[qs("cutoff")] = cutoff;
    cutoff.save(m);
    m[qs("resonance")] = resonance;
    m[qs("mode")] = mode;
}

void SVF::loadData(const QCborMap& m) {
    cutoff.load(m, "frequency");
    cutoff.load(m);
    //cutoff = m.value("cutoff").toDouble(m.value("frequency").toDouble(cutoff));
    resonance = m.value("resonance").toDouble(resonance);
    mode = static_cast<FilterMode>(m.value("mode").toInteger(mode));
}

namespace {
    std::unordered_map<SVF::FilterMode, QString> modeNames = [] {
        std::unordered_map<SVF::FilterMode, QString> m;
        m[SVF::Off] = "off";
        m[SVF::Low] = "low";
        m[SVF::High] = "high";
        m[SVF::Band] = "band";
        m[SVF::Notch] = "notch";
        return m;
    }();
}

void SVF::onGadgetCreated() {
    if (!obj) return;
    auto l = new LayoutGadget(obj);

    auto modetxt = [](double inp) {
        if (auto f = modeNames.find(static_cast<FilterMode>(inp)); f != modeNames.end()) return f->second;
        return qs("?");
    };

    KnobGadget::autoCutoff(l, cutoff);
    (new KnobGadget(l))->bind(resonance)->setLabel(qs("Res"))->setTextFunc(KnobGadget::textPercent)->setRange(0.0, 1.0, 0.01)->setDefault(0.0);
    (new KnobGadget(l))->bind(mode)->setLabel(qs("Mode"))->setTextFunc(modetxt)->setRange(0, Notch, 1, KnobGadget::BigStep)->setDefault(0);
}
