#pragma once

#include <memory>

namespace Xybrid::Data {
    class CommandPort;
}

namespace Xybrid::NodeLib {
    class ParamReader;
    class CommandReader {
        friend class ParamReader;
        uint8_t* data;
        size_t dataSize;
        size_t cur = static_cast<size_t>(-1);
    public:
        CommandReader(Data::CommandPort*);
        CommandReader(std::shared_ptr<Data::CommandPort> p) : CommandReader(p.get()) { }

        operator bool() const;
        CommandReader& operator++();

        // data access
        inline uint16_t noteId() const { return reinterpret_cast<uint16_t&>(data[cur]); }
        inline int16_t note() const { return reinterpret_cast<int16_t&>(data[cur+2]); }
        inline uint8_t numParams() const { return data[cur+4]; }
        inline uint8_t param(uint8_t num) const { return data[cur+5+num*2]; }
        inline uint8_t val(uint8_t num) const { return data[cur+6+num*2]; }
    };
    class ParamReader {
        CommandReader cr;
        int16_t pn = -1;
        int16_t pmax = 0;
    public:
        ParamReader(const CommandReader&);

        operator bool() const;
        ParamReader& operator++();

        // data access
        inline uint8_t param() const { return cr.param(static_cast<uint8_t>(pn)); }
        inline uint8_t val() const { return cr.val(static_cast<uint8_t>(pn)); }

        int16_t next(bool acceptsTweens = false, uint8_t = 1) const;
        int16_t tween() const;
    };
}
