#ifndef PACKET_H
#define PACKET_H

#include "math/Vector.h"
#include "utils/Buffer.h"
#include "utils/StringBuffer.h"
#include "utils/Types.h"

enum class PacketSendMode { RELIABLE, SEQUENCED, UNSEQUENCED };

class InPacket {
    const char* data;
    int size;
    int index;

public:
    InPacket(const void* data, int size);

    bool read(uint8& u);
    bool read(uint16& u);
    bool read(uint32& u);
    bool read(int8& s);
    bool read(int16& s);
    bool read(int32& s);
    bool read(float& f);

    template<int N>
    bool read(StringBuffer<N>& s) {
        uint16 end;
        if(read(end)) {
            return true;
        }
        s.clear();
        for(unsigned int i = 0; i < end; i++) {
            int8 c;
            if(read(c)) {
                return true;
            }
            s.append(c);
        }
        return false;
    }

    template<int N, typename T>
    bool read(Vector<N, T>& v) {
        for(int i = 0; i < N; i++) {
            if(read(v[i])) {
                return true;
            }
        }
        return false;
    }

private:
    bool read(void* buffer, int length);
};

struct OutPacket {
    Buffer buffer;

    OutPacket(int initialSize);

    OutPacket& writeU8(uint8 u);
    OutPacket& writeU16(uint16 u);
    OutPacket& writeU32(uint32 u);
    OutPacket& writeS8(int8 s);
    OutPacket& writeS16(int16 s);
    OutPacket& writeS32(int32 s);
    OutPacket& writeFloat(float f);

    template<int N>
    OutPacket& writeString(const StringBuffer<N>& s) {
        int end = s.getLength() > 65535 ? 65535 : s.getLength();
        writeU16(static_cast<uint16>(end));
        for(int i = 0; i < end; i++) {
            writeS8(s[i]);
        }
        return *this;
    }

    template<int N>
    OutPacket& writeVector(const Vector<N, int>& v) {
        for(int i = 0; i < N; i++) {
            writeS32(v[i]);
        }
        return *this;
    }

    template<int N>
    OutPacket& writeVector(const Vector<N, float>& v) {
        for(int i = 0; i < N; i++) {
            writeFloat(v[i]);
        }
        return *this;
    }
};

#endif