#include <nic.h>
#include <utility/random.h>
#include <udp.h>
#include <tcp.h>

__BEGIN_SYS


class RTP : public Protocol_Common {
public:
    enum PType {
        PCMU = 0,      _1016 = 1,     G721 = 2,         GSM = 3,
        G723 = 4,      DVI4_8 = 5,    DVI4_16 = 6,      LPC = 7,
        PCMA = 8,      G722 = 9,      L16_Stereo = 10,  L16_Mono = 11,
        QCELP = 12,    CN = 13,       MPA = 14,         G728 = 15,
        DVI4_11 = 16,  DVI4_22 = 17,  G729 = 18,        CellB = 25,
        JPEG = 26,     NV = 28,       H261 = 31,        MPV = 32,
        MP2T = 33,     H263 = 34
    };

class Header {

    static const u8 VERSION_NUMBER = 2;
    static const u8 CSRC_COUNT = 0;

public:
    Header(u8 marker, PType payload_type, u32 timestamp, u32 ssrc_id) {
    set_marker(marker);
    set_payload_type(payload_type);
    set_timestamp(timestamp);
    set_contrib_count(CSRC_COUNT);
    set_version_number(VERSION_NUMBER);
    set_ssrc_id(ssrc_id);
}

    u16 sequence_number() const { return _sequence_number; }
    u32 timestamp() const { return _timestamp; }
    u32 ssrc_id() const { return _ssrc_id; }
    u8 payload_type() const { return _payload_type & 0x7f; }
    u8 marker() const { return _marker >> 7; }
    u8 contrib_sources_count() const { return _contrib_sources_count & 0x0f; }
    u8 extensions() const { return (_extensions >> 4) & 0x1; }
    u8 version_number() const { return _version_number >> 6; }

    void set_version_number(u8 version_number) {
        u8 vnumber = 0x03 & version_number;
        u8 aux = _version_number & 0x3f;
        _version_number = vnumber | aux;
    }

    void set_sequence_number(u16 seq) {
        _sequence_number = CPU::htons(seq);
    }

    void set_timestamp(u32 timestamp) {
        _timestamp = CPU::htonl(timestamp);
    }

    void set_ssrc_id(u32 ssrc_id) {
        _ssrc_id = CPU::htonl(ssrc_id);
    }

    void set_marker(u8 marker) {
        //Setando 7 MSB's pra 0. Só assim o OR bit-a-bit vai manter o payload_type.
        u8 m = marker << 7;
        u8 aux = _marker & 0x7f;
        _marker = m | aux;
    }

    void set_payload_type(PType payload_type) {
        //Mesmo motivo que no set_marker()
        u8 ptype = 0x7f & ( (u8) payload_type);
        u8 aux = _payload_type & 0x80;
        _payload_type = ptype | aux;
    }

    void set_contrib_count(u8 contrib_count) {
        u8 cc = 0x0f & contrib_count;
        u8 aux = _contrib_sources_count & 0xf0;
        _contrib_sources_count = cc | aux;
    }

    const int size() const { return sizeof(Header); }

    friend Debug & operator << (Debug & db, const Header & h);

private:
    u8 _contrib_sources_count:4;
    u8 _extensions:1;
    u8 _padding:1;
    u8 _version_number:2;

    u8 _payload_type:7;
    u8 _marker:1;

    u16 _sequence_number;
    u32 _timestamp;
    u32 _ssrc_id;

    // This RTP header currently does not support contributing sources
};

class PDU {
public:
    static const int TCP_PAYLOAD_SIZE = 512;
    static const int PDU_DATA_SIZE = TCP_PAYLOAD_SIZE - sizeof(Header);

    PDU(Header head, const char data[]) : _header(head) {
        memcpy(_data, data, PDU_DATA_SIZE);
    }

    const char * data() const {return _data;}

private:
    Header _header;
    char _data[ PDU_DATA_SIZE ];
};

RTP(UDP::Address remote);

static int get_ssrc_id();

static int listen(RTP * self, UDP::Address rtp_local, UDP::Address rtcp_local);

int send(u32 timestamp, PType payload_type, u8 marker, char *payload,int payload_len);
    char * receive();

private:
    UDP::Address _rtp_remote;
    UDP::Address _rtcp_remote;

    TCP::Socket *_local_rtp;
    TCP::Socket *_local_rtcp;

    TCP::Socket *_remote_rtp;
    TCP::Socket *_remote_rtcp;

    u32 _ssrc_id;
    u16 _sequence_number;
};

__END_SYS