#include "porttypes.h"
using namespace Xybrid::Data;

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

#include <QDebug>

void AudioPort::pull() {
    auto t = audioEngine->curTickId();
    if (tickUpdatedOn == t) return;
    lock.lock();
    if (tickUpdatedOn == t) { lock.unlock(); return; } // someone else got here before us

    size_t ts = audioEngine->curTickSize();
    size = ts;
    size_t s = sizeof(float) * ts;

    if (type == Input) {
        if (connections.size() == 1) {
            // if this is a single connection, just repoint to source audio
            if (auto p = std::static_pointer_cast<AudioPort>(connections[0].lock()); p && p->dataType() == Audio) {
                p->pull();
                bufL = p->bufL;
                bufR = p->bufR;
                goto done;//return;
            }
        }
        bufL = static_cast<float*>(audioEngine->tickAlloc(s*2));
        bufR = &bufL[ts]; // for some reason just adding the size wonks out
        memset(bufL, 0, s*2); // clear buffers

        for (auto c : connections) { // mix
            if (auto p = std::static_pointer_cast<AudioPort>(c.lock()); p && p->dataType() == Audio) {
                p->pull();
                for (size_t i = 0; i < ts; i++) {
                    bufL[i] += p->bufL[i];
                    bufR[i] += p->bufR[i];
                }
            }
        }
    } else if (auto pt = std::static_pointer_cast<AudioPort>(passthroughTo.lock()); pt && pt->dataType() == Audio) {
        // passthrough; ports abound
        pt->pull();
        bufL = pt->bufL;
        bufR = pt->bufR;
    } else { // output without valid passthrough, just clear and prepare a blank buffer
        bufL = static_cast<float*>(audioEngine->tickAlloc(s*2));
        bufR = &bufL[ts];
        memset(bufL, 0, s*2); // clear buffers
    }
done:
    tickUpdatedOn = t;
    lock.unlock();
}

void CommandPort::pull() {
    auto t = audioEngine->curTickId();
    if (tickUpdatedOn == t) return;
    lock.lock();
    if (tickUpdatedOn == t) { lock.unlock(); return; } // someone else got here before us

    dataSize = 0;
    if (type == Input) {
        for (auto c : connections) {
            if (auto p = std::static_pointer_cast<CommandPort>(c.lock()); p && p->dataType() == Command) {
                p->pull();
                data = p->data; // just repoint to input's buffer
                dataSize = p->dataSize;
                break;
            }
        }
    } else if (auto pt = std::static_pointer_cast<CommandPort>(passthroughTo.lock()); pt && pt->dataType() == Command) {
        // valid passthrough
        pt->pull();
        data = pt->data; // again, just repoint
        dataSize = pt->dataSize;
    } // don't need an else case, size is already zero

    tickUpdatedOn = t;
    lock.unlock();
}

void CommandPort::push(std::vector<uint8_t> v) {
    tickUpdatedOn = audioEngine->curTickId();
    dataSize = v.size();
    data = static_cast<uint8_t*>(audioEngine->tickAlloc(dataSize));
    memcpy(data, v.data(), dataSize);
}
