#ifndef PACKET_H
#define PACKET_H

#include "network/ENet.h"
#include "utils/StringBuffer.h"
#include "utils/Types.h"

class InPacket {
    ENetPacket* packet;
    unsigned int index;

    friend class Client;
    friend class Server;

    InPacket(ENetPacket* packet);

    bool read(void* buffer, unsigned int length);

public:
    bool readU8(uint8& u);
    bool readU16(uint16& u);
    bool readU32(uint32& u);
    bool readS8(int8& s);
    bool readS16(int16& s);
    bool readS32(int32& s);
    bool readFloat(float& f);

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

class OutPacket {
    ENetPacket* packet;
    unsigned int index;
    int channel;

    friend class Client;
    friend class Server;

    OutPacket(unsigned int size, int flags, int channel);

public:
    static OutPacket reliable(unsigned int size);
    static OutPacket sequenced(unsigned int size);
    static OutPacket unsequenced(unsigned int size);

    ~OutPacket();
    OutPacket(const OutPacket& other);
    OutPacket(OutPacket&& other);
    OutPacket& operator=(OutPacket other);

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

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

private:
    void write(const void* buffer, unsigned int length);
};

#endif