#pragma once
#include <cstdint>
#include <optional>
#include <vector>

namespace net {
    enum class rmq_flags {
        none = 0,
        short_angle = (1 << 1),
        float_angle = (1 << 2),
        hires_coord = (1 << 3),
        float_coord = (1 << 4),
        edict_scale = (1 << 5),
        alpha_sanity = (1 << 6),
        int_coord = (1 << 7),
    };

    class msg {
        std::vector<std::uint8_t> _data{};
        bool _eos{false};
        std::size_t _offset{0};

        void write_lores_coord(float f);

        void write_hires_coord(float f);

        std::optional<float> read_lores_coord();

        std::optional<float> read_hires_coord();

    public:
        void clear();

        void write_char(char c);

        void write_byte(std::uint8_t b);

        void write_short(short s);

        void write_long(int l);

        void write_float(float f);

        void write_string(std::string_view s);

        void write_coord(float f, rmq_flags flags);

        void write_angle(float f, rmq_flags flags);

        void write_angle_16(float f, rmq_flags flags);

        [[nodiscard]] bool eos() const { return _eos; }

        void begin_reading();

        std::optional<char> read_char();

        std::optional<std::uint8_t> read_byte();

        std::optional<short> read_short();

        std::optional<int> read_long();

        std::optional<float> read_float();

        std::optional<std::string> read_string();

        std::optional<float> read_coord(rmq_flags flags);

        std::optional<float> read_angle(rmq_flags flags);

        std::optional<float> read_angle_16(rmq_flags flags);

        [[nodiscard]] bool empty() const { return _data.empty(); }

        [[nodiscard]] std::size_t size() const { return _data.size(); }

        auto data() { return _data.data(); }

        void write(const std::uint8_t* data, std::size_t size);

        auto offset() const { return _offset; }
    };
}
