#pragma once

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

namespace Xybrid::Data {
    class AudioPort : public Port {
    public:
        class FrameRef {
            friend class AudioPort;
            AudioPort* port;
            size_t at;
            FrameRef(AudioPort* port, size_t at) : port(port), at(at) { }
        public:
            FrameRef& operator=(AudioFrame f) {
                port->bufL[at] = static_cast<float>(f.l);
                port->bufR[at] = static_cast<float>(f.r);
                return *this;
            }
            FrameRef& operator+=(AudioFrame f) {
                port->bufL[at] += static_cast<float>(f.l);
                port->bufR[at] += static_cast<float>(f.r);
                return *this;
            }
            operator AudioFrame() const { return { port->bufL[at], port->bufR[at] }; }
            AudioFrame operator*(AudioFrame o) const { return static_cast<AudioFrame>(*this) * o; }
        };

        float* bufL;
        float* bufR;
        size_t size;

        AudioPort() = default;
        ~AudioPort() override = default;

        FrameRef operator[](size_t at) { return { this, at }; }
        FrameRef at(size_t at) { return { this, at }; } // alternative syntax if you don't like (*p)[0]

        Port::DataType dataType() const override { return Port::Audio; }

        void pull() override;
    };

    class CommandPort : public Port {
    public:
        uint8_t* data;
        size_t dataSize;

        CommandPort() = default;
        ~CommandPort() override = default;

        Port::DataType dataType() const override { return Port::Command; }
        bool singleInput() const override { return true; }

        void pull() override;

        /// Push a data buffer
        void push(std::vector<uint8_t>);
    };
}
