
/*---------------------------------------------------------------------------*/
/* websocketframe.h                                                          */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

enum opcode_t {
    WSOC_CONTINOUS_FRAME = 0x0,
    WSOC_TEXT_FRAME = 0x1,
    WSOC_BINARY_FRAME = 0x2,
    WSOC_CLOSE = 0x8,
    WSOC_PING = 0x9,
    WSOC_PONG = 0xA
};


enum frame_t {
    WSF_UNDEFINED = 0,
    WSF_SINGLE = 1,
    WSF_FRAGMENTED_START = 2,
    WSF_FRAGMENTED_CONTINOUS = 4,
    WSF_FRAGMENTED_END = 8,
    WSF_BINARY = 16
};


class WebsocketFrame {
protected:
    byte * _buffer;
    byte * _dataBuffer;
    size_t _headerSize;
    size_t _extLenAndMaskSize;
    int _maskKeyStart;
    ulong64 _dataLength;
    frame_t _type;
    bool _serverMode;

    void EncodeFrame(frame_t ftype, opcode_t opcode, byte * data, size_t len);

public:
	WebsocketFrame();
	~WebsocketFrame();

    inline bool finBit()          { return (_buffer[0] & 0x80) == 0x80; }
    inline bool maskBit()         { return (_buffer[1] & 0x80) == 0x80; }
    inline opcode_t opcode()      { return opcode_t(_buffer[0] & 0x0F); }
    inline ulong64 dataLength()   { return _dataLength; }
    inline size_t frameSize()     { return size_t(_dataLength + _headerSize); }
    inline size_t headerSize()    { return _headerSize; }
    inline size_t headerSizeExt() { return _extLenAndMaskSize; }
    inline byte * buffer()        { return _buffer; }
    inline byte * dataBuffer()    { return (_serverMode ? _dataBuffer : &_buffer[_headerSize]); }
    inline frame_t type()         { return (frame_t)_type; }
    inline bool isServerMode()    { return _serverMode; }

    void SetServerMode(bool serverMode);
    void DecodeHeaderBase();
    void DecodeHeaderExt();
    void EncodeDataFrame(frame_t ftype, void * data, size_t len);
    void EncodeCloseFrame(word status, const char * reason);
    void EncodePingFrame();
    void EncodePongFrame();
    void MaskData(byte * in, byte * out, size_t len);
};
