#include "rtp-internal.h"
#include "rtp-util.h"
#include <errno.h>

// https://www.iana.org/assignments/rtcp-xr-block-types/rtcp-xr-block-types.xhtml
/*
    BT 	    Name 	                                        Reference
    1	    Loss RLE Report Block	                        [RFC3611]
    2	    Duplicate RLE Report Block	                    [RFC3611]
    3	    Packet Receipt Times Report Block	            [RFC3611]
    4	    Receiver Reference Time Report Block	        [RFC3611]
    5	    DLRR Report Block	                            [RFC3611]
    6	    Statistics Summary Report Block	                [RFC3611]
    7	    VoIP Metrics Report Block	                    [RFC3611]
    8	    RTCP XR	                                        [RFC5093]
    9	    Texas Instruments Extended VoIP Quality Block	[http://focus.ti.com/general/docs/bcg/bcgdoccenter.tsp?templateId=6116&navigationId=12078#42][David_Lide]
    10	    Post-repair Loss RLE Report Block	            [RFC5725]
    11	    Multicast Acquisition Report Block	            [RFC6332]
    12	    IDMS Report Block	                            [RFC7272]
    13	    ECN Summary Report	                            [RFC6679]
    14	    Measurement Information Block	                [RFC6776]
    15	    Packet Delay Variation Metrics Block	        [RFC6798]
    16	    Delay Metrics Block	                            [RFC6843]
    17	    Burst/Gap Loss Summary Statistics Block	        [RFC7004]
    18	    Burst/Gap Discard Summary Statistics Block	    [RFC7004]
    19	    Frame Impairment Statistics Summary	            [RFC7004]
    20	    Burst/Gap Loss Metrics Block	                [RFC6958]
    21	    Burst/Gap Discard Metrics Block	                [RFC7003][RFC Errata 3735]
    22	    MPEG2 Transport Stream PSI-Independent Decodability Statistics Metrics Block	[RFC6990]
    23	    De-Jitter Buffer Metrics Block	                [RFC7005]
    24	    Discard Count Metrics Block	                    [RFC7002]
    25	    DRLE (Discard RLE Report)	                    [RFC7097]
    26	    BDR (Bytes Discarded Report)	                [RFC7243]
    27	    RFISD (RTP Flows Initial Synchronization Delay)	[RFC7244]
    28	    RFSO (RTP Flows Synchronization Offset Metrics Block)	[RFC7244]
    29	    MOS Metrics Block	                            [RFC7266]
    30	    LCB (Loss Concealment Metrics Block)	        [RFC7294, Section 4.1]
    31	    CSB (Concealed Seconds Metrics Block)	        [RFC7294, Section 4.1]
    32	    MPEG2 Transport Stream PSI Decodability Statistics Metrics Block	[RFC7380]
    33	    Post-Repair Loss Count Metrics Report Block	    [RFC7509]
    34	    Video Loss Concealment Metric Report Block	    [RFC7867]
    35	    Independent Burst/Gap Discard Metrics Block	    [RFC8015]
    36-254  Unassigned
    255	    Reserved for future extensions	                [RFC3611]
*/

/*
Parameter 	Reference
pkt-loss-rle	[RFC3611]
pkt-dup-rle	[RFC3611]
pkt-rcpt-times	[RFC3611]
stat-summary	[RFC3611]
voip-metrics	[RFC3611]
rcvr-rtt	[RFC3611]
post-repair-loss-rle	[RFC5725]
grp-sync	[http://www.etsi.org/deliver/etsi_ts/183000_183099/183063/][ETSI 183 063][Miguel_Angel_Reina_Ortega]
multicast-acq	[RFC6332]
ecn-sum	[RFC6679]
pkt-dly-var	[RFC6798]
delay	[RFC6843]
burst-gap-loss-stat	[RFC7004]
burst-gap-discard-stat	[RFC7004]
frame-impairment-stat	[RFC7004]
burst-gap-loss	[RFC6958]
burst-gap-discard	[RFC7003]
ts-psi-indep-decodability	[RFC6990]
de-jitter-buffer	[RFC7005]
pkt-discard-count	[RFC7002]
discard-rle	[RFC7097]
discard-bytes	[RFC7243]
rtp-flow-init-syn-delay	[RFC7244]
rtp-flow-syn-offset	[RFC7244]
mos-metric	[RFC7266]
loss-conceal	[RFC7294]
conc-sec	[RFC7294]
ts-psi-decodability	[RFC7380]
post-repair-loss-count	[RFC7509]
video-loss-concealment	[RFC7867]
ind-burst-gap-discard	[RFC8015]
*/

static int rtcp_xr_rrt_pack(uint64_t ntp, uint8_t* ptr, uint32_t bytes);
static int rtcp_xr_dlrr_pack(const rtcp_dlrr_t* dlrr, int count, uint8_t* ptr, uint32_t bytes);
static int rtcp_xr_ecn_pack(const rtcp_ecn_t* ecn, uint8_t* ptr, uint32_t bytes);

static int rtcp_xr_lrle_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes);
static int rtcp_xr_drle_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes);
static int rtcp_xr_prt_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes);
static int rtcp_xr_rrt_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes);
static int rtcp_xr_dlrr_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes);
static int rtcp_xr_ecn_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes);


// https://www.rfc-editor.org/rfc/rfc3611.html#section-4.1
/*
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     BT=1      | rsvd. |   T   |         block length          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        SSRC of source                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          begin_seq            |             end_seq           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          chunk 1              |             chunk 2           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   :                              ...                              :
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          chunk n-1            |             chunk n           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_xr_lrle_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes)
{
    uint32_t i, j;
    uint32_t source;
    uint32_t len, num;
    uint16_t seq, end;
    uint32_t chunk;
    uint8_t* v, v0[32];

    len = nbo_r16(ptr + 2);

    if (bytes < 8 || (len + 1) * 4 > bytes)
        return -1;

    len *= 4; // to bytes
    if (len < 8)
        return 0;

    source = nbo_r32(ptr + 4);
    seq = nbo_r16(ptr + 8);
    end = nbo_r16(ptr + 10);
    num = end - seq;

    if ((num + 7) / 8 > sizeof(v0))
    {
        v = calloc((num + 7) / 8, sizeof(*v));
        if (!v) return -ENOMEM;
    }
    else
    {
        v = v0;
        memset(v, 0, (num + 7) / 8 * sizeof(v[0]));
    }

    ptr += 8;
    len -= 8;
    for(i = 0; len > 2; ptr += 2, len -= 2)
    {
        chunk = nbo_r16(ptr);
        if (0 == (0x8000 & chunk))
        {
            // Run Length Chunk
            for (j = 0; j < (chunk & 0x3FFF) && i < num; j++, i++)
            {
                if(0x4000 & chunk)
                    v[i/8] |= 1 << (7-(i%8));
            }
        }
        else
        {
            // Bit Vector Chunk
            for (j = 0; j < 15 && i < num; j++, i++)
            {
                if (chunk & (1 << (14 - j)))
                    v[i / 8] |= 1 << (7 - (i % 8));
            }
        }
    }

    msg->u.xr.u.rle.source = source;
    msg->u.xr.u.rle.begin = seq;
    msg->u.xr.u.rle.end = end;
    msg->u.xr.u.rle.chunk = v;
    msg->u.xr.u.rle.count = num;
    ctx->handler.on_rtcp(ctx->cbparam, msg);
    (void)ctx, (void)header;
    if (v && v != v0)
        free(v);
    return 0;
}

// https://www.rfc-editor.org/rfc/rfc3611.html#section-4.2
/*
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     BT=2      | rsvd. |   T   |         block length          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        SSRC of source                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          begin_seq            |             end_seq           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          chunk 1              |             chunk 2           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   :                              ...                              :
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          chunk n-1            |             chunk n           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_xr_drle_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes)
{
    uint32_t i, j;
    uint32_t source;
    uint32_t len, num;
    uint16_t seq, end;
    uint32_t chunk;
    uint8_t* v, v0[32];

    len = nbo_r16(ptr + 2);

    if (bytes < 8 || (len + 1) * 4 > bytes)
        return -1;

    len *= 4; // to bytes
    if (len < 8)
        return 0;

    source = nbo_r32(ptr + 4);
    seq = nbo_r16(ptr + 8);
    end = nbo_r16(ptr + 10);
    num = end - seq;

    if ((num + 7) / 8 > sizeof(v0))
    {
        v = calloc((num + 7) / 8, sizeof(*v));
        if (!v) return -ENOMEM;
    }
    else
    {
        v = v0;
        memset(v, 0, (num + 7) / 8 * sizeof(v[0]));
    }

    ptr += 8;
    len -= 8;
    for (i = 0; len > 2; ptr += 2, len -= 2)
    {
        chunk = nbo_r16(ptr);
        if (0 == (0x8000 & chunk))
        {
            // Run Length Chunk
            for (j = 0; j < (chunk & 0x3FFF) && i < num; j++, i++)
            {
                if (0x4000 & chunk)
                    v[i / 8] |= 1 << (7 - (i % 8));
            }
        }
        else
        {
            // Bit Vector Chunk
            for (j = 0; j < 15 && i < num; j++, i++)
            {
                if (chunk & (1 << (14 - j)))
                    v[i / 8] |= 1 << (7 - (i % 8));
            }
        }
    }

    msg->u.xr.u.rle.source = source;
    msg->u.xr.u.rle.begin = seq;
    msg->u.xr.u.rle.end = end;
    msg->u.xr.u.rle.chunk = v;
    msg->u.xr.u.rle.count = num;
    ctx->handler.on_rtcp(ctx->cbparam, msg);
    (void)ctx, (void)header;
    if (v && v != v0)
        free(v);
    return 0;
}


// https://www.rfc-editor.org/rfc/rfc3611.html#section-4.3
/*
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     BT=3      | rsvd. |   T   |         block length          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        SSRC of source                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          begin_seq            |             end_seq           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |       Receipt time of packet begin_seq                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |       Receipt time of packet (begin_seq + 1) mod 65536        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   :                              ...                              :
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |       Receipt time of packet (end_seq - 1) mod 65536          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_xr_prt_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes)
{
    uint32_t i;
    uint32_t source;
    uint32_t len, num;
    uint16_t seq, end;
    uint32_t* timestamp, timestamp0[32];

    len = nbo_r16(ptr + 2);

    if (bytes < 8 || (len + 1) * 4 > bytes)
        return -1;

    len *= 4; // to bytes
    if (len < 8)
        return 0;

    source = nbo_r32(ptr + 4);
    seq = nbo_r16(ptr + 8);
    end = nbo_r16(ptr + 10);
    num = end - seq;

    if (num > sizeof(timestamp0)/sizeof(timestamp0[0]))
    {
        timestamp = calloc(num, sizeof(*timestamp));
        if (!timestamp) return -ENOMEM;
    }
    else
    {
        timestamp = timestamp0;
        memset(timestamp, 0, num * sizeof(timestamp[0]));
    }

    ptr += 8;
    len -= 8;
    for (i = 0; len > 4; ptr += 4, len -= 4)
    {
        timestamp[i] = nbo_r32(ptr);
    }

    msg->u.xr.u.prt.source = source;
    msg->u.xr.u.prt.begin = seq;
    msg->u.xr.u.prt.end = end;
    msg->u.xr.u.prt.timestamp = timestamp;
    msg->u.xr.u.prt.count = num;
    ctx->handler.on_rtcp(ctx->cbparam, msg);
    (void)ctx, (void)header;
    if (timestamp && timestamp != timestamp0)
        free(timestamp);
    return 0;
}


// https://www.rfc-editor.org/rfc/rfc3611.html#section-4.4
/*
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     BT=4      |   reserved    |       block length = 2        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |              NTP timestamp, most significant word             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |             NTP timestamp, least significant word             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_xr_rrt_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes)
{
    uint32_t len;
    uint64_t ntp;

    len = nbo_r16(ptr + 2);

    if (bytes < 12 || (len + 1) * 4 > bytes)
        return -1;

    ntp = nbo_r32(ptr + 4);
    ntp = (ntp << 32) | nbo_r32(ptr + 8);

    msg->u.xr.u.rrt = ntp;
    ctx->handler.on_rtcp(ctx->cbparam, msg);
    (void)ctx, (void)header;
    return 0;
}

static int rtcp_xr_rrt_pack(uint64_t ntp, uint8_t* ptr, uint32_t bytes)
{
    if (bytes < 12)
        return -1;

    nbo_w32(ptr, (RTCP_XR_RRT << 24) | 2);
    nbo_w32(ptr + 4, (uint32_t)(ntp >> 32));
    nbo_w32(ptr + 8, (uint32_t)ntp);
    return 12;
}

// https://www.rfc-editor.org/rfc/rfc3611.html#section-4.5
/*
  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |     BT=5      |   reserved    |         block length          |
 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 |                 SSRC_1 (SSRC of first receiver)               | sub-
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
 |                         last RR (LRR)                         |   1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                   delay since last RR (DLRR)                  |
 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 |                 SSRC_2 (SSRC of second receiver)              | sub-
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
 :                               ...                             :   2
 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*/
static int rtcp_xr_dlrr_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes)
{
    uint32_t i;
    uint32_t len, num;
    rtcp_dlrr_t* dlrr, dlrr0[32];

    len = nbo_r16(ptr + 2);
    if (bytes < 8 || (len + 1) * 4 > bytes)
        return -1;

    num = len / 3;
    if (num > sizeof(dlrr0))
    {
        dlrr = calloc(num, sizeof(*dlrr));
        if (!dlrr) return -ENOMEM;
    }
    else
    {
        dlrr = dlrr0;
        memset(dlrr, 0, num * sizeof(dlrr[0]));
    }

    ptr += 4;
    for (i = 0; i < num; i++, ptr += 12)
    {
        dlrr[i].ssrc = nbo_r32(ptr + 0);
        dlrr[i].lrr = nbo_r32(ptr + 4);
        dlrr[i].dlrr = nbo_r32(ptr + 8);
    }

    msg->u.xr.u.dlrr.dlrr = dlrr;
    msg->u.xr.u.dlrr.count = num;
    ctx->handler.on_rtcp(ctx->cbparam, msg);
    (void)ctx, (void)header;
    if (dlrr && dlrr != dlrr0)
        free(dlrr);
    return 0;
}

static int rtcp_xr_dlrr_pack(const rtcp_dlrr_t* dlrr, int count, uint8_t* ptr, uint32_t bytes)
{
    int i;
    if ((int)bytes < 4 + count * 12)
        return - 1;

    nbo_w32(ptr, (RTCP_XR_DLRR << 24) | (count * 3));
    bytes -= 4;
    ptr += 4;

    for (i = 0; i < count && bytes >= 12; i++)
    {
        nbo_w32(ptr, dlrr[i].ssrc);
        nbo_w32(ptr + 4, dlrr[i].lrr);
        nbo_w32(ptr + 8, dlrr[i].dlrr);

        bytes -= 12;
        ptr += 12;
    }
    return 4 + i * 12;
}

// https://www.rfc-editor.org/rfc/rfc7097.html#section-5
// rtcp-xr: discard-rle
/*
       0               1               2               3
       0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |     BT=25     |rsvd |E|   T   |         block length          |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                        SSRC of source                         |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |          begin_seq            |             end_seq           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |          chunk 1              |             chunk 2           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      :                              ...                              :
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |          chunk n-1            |             chunk n           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/


// https://datatracker.ietf.org/doc/html/rfc7243#section-5
// rtcp-xr: discard-bytes
/*
    0               1               2               3
    0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     BT=26     | I |E|Reserved |       Block length=2          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        SSRC of source                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |             Number of RTP payload bytes discarded             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

// https://www.rfc-editor.org/rfc/rfc6679.html#section-5.2
/*
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     BT=13     | Reserved      |         Block Length          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | SSRC of Media Sender                                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | ECT (0) Counter                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | ECT (1) Counter                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | ECN-CE Counter                | not-ECT Counter               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | Lost Packets Counter          | Duplication Counter           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                   Figure 4: RTCP XR ECN Summary Report
*/
static int rtcp_xr_ecn_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes)
{
    uint32_t len;
    rtcp_ecn_t ecn;
    
    len = nbo_r16(ptr + 2);
    if (bytes < 24 || (len + 1) * 4 > bytes)
        return -1;
    
    ecn.ext_highest_seq = nbo_r32(ptr); // ssrc
    ecn.ect[0] = nbo_r32(ptr + 4);
    ecn.ect[1] = nbo_r32(ptr + 8);
    ecn.ect_ce_counter = nbo_r16(ptr + 12);
    ecn.not_ect_counter = nbo_r16(ptr + 14);
    ecn.lost_packets_counter = nbo_r16(ptr + 16);
    ecn.duplication_counter = nbo_r16(ptr + 18);

    memcpy(&msg->u.xr.u.ecn, &ecn, sizeof(msg->u.xr.u.ecn));
    ctx->handler.on_rtcp(ctx->cbparam, msg);
    (void)ctx, (void)header;
    return 0;
}

static int rtcp_xr_ecn_pack(const rtcp_ecn_t* ecn, uint8_t* ptr, uint32_t bytes)
{
    if (bytes < 24)
        return -1;

    nbo_w32(ptr, (RTCP_XR_ECN << 24) | 5);
    nbo_w32(ptr + 4, ecn->ext_highest_seq);
    nbo_w32(ptr + 8, ecn->ect[0]);
    nbo_w32(ptr + 12, ecn->ect[1]);
    nbo_w16(ptr + 16, ecn->ect_ce_counter);
    nbo_w16(ptr + 18, ecn->not_ect_counter);
    nbo_w16(ptr + 20, ecn->lost_packets_counter);
    nbo_w16(ptr + 22, ecn->duplication_counter);
    return 24;
}

/*
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |V=2|P|reserved |   PT=XR=207   |             length            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              SSRC                             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   :                         report blocks                         :
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      BT       | type-specific |         block length          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   :             type-specific block contents                      :
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
void rtcp_xr_unpack(struct rtp_context* ctx, const rtcp_header_t* header, const uint8_t* ptr, size_t bytes)
{
    int r;
    size_t len;
    struct rtcp_msg_t msg;
    struct rtp_member* sender;

    if (bytes < 8 /*sizeof(rtcp_xr_t)*/)
    {
        assert(0);
        return;
    }

    msg.ssrc = nbo_r32(ptr);
    sender = rtp_sender_fetch(ctx, msg.ssrc);
    if (!sender) return; // error
    assert(sender != ctx->self);

    r = 0;
    ptr += 4;
    bytes -= 4;
    while (bytes >= 4)
    {
        len = nbo_r16(ptr + 2);
        if (len * 4 > bytes - 4)
            break; // invalid

        msg.type = RTCP_XR | (ptr[0] << 8);
        switch (ptr[0])
        {
        case RTCP_XR_LRLE:
            r = rtcp_xr_lrle_unpack(ctx, header, &msg, ptr, bytes);
            break;

        case RTCP_XR_DRLE:
            r = rtcp_xr_drle_unpack(ctx, header, &msg, ptr, bytes);
            break;

        case RTCP_XR_PRT:
            r = rtcp_xr_prt_unpack(ctx, header, &msg, ptr, bytes);
            break;

        case RTCP_XR_RRT:
            r = rtcp_xr_rrt_unpack(ctx, header, &msg, ptr, bytes);
            break;

        case RTCP_XR_DLRR:
            r = rtcp_xr_dlrr_unpack(ctx, header, &msg, ptr, bytes);
            break;

        case RTCP_XR_ECN:
            r = rtcp_xr_ecn_unpack(ctx, header, &msg, ptr, bytes);
            break;

        default:
            //assert(0);
            r = 0; // ignore
            break;
        }

        ptr += len;
        bytes -= len;
    }

    return;
}

int rtcp_xr_pack(struct rtp_context* ctx, uint8_t* data, int bytes, enum rtcp_xr_type_t id, const rtcp_xr_t* xr)
{
    int r;
    rtcp_header_t header;

    (void)ctx;
    if (bytes < 4 + 4)
        return 4 + 4;

    switch (id)
    {
    case RTCP_XR_RRT:
        r = rtcp_xr_rrt_pack(xr->u.rrt, data + 8, bytes - 8);
        break;

    case RTCP_XR_DLRR:
        r = rtcp_xr_dlrr_pack(xr->u.dlrr.dlrr, xr->u.dlrr.count, data + 8, bytes - 8);
        break;

    case RTCP_XR_ECN:
        r = rtcp_xr_ecn_pack(&xr->u.ecn, data + 8, bytes - 8);
        break;

    case RTCP_XR_LRLE:
    case RTCP_XR_DRLE:
    case RTCP_XR_PRT:
    default:
        assert(0);
        return -1;
    }

    header.v = 2;
    header.p = 0;
    header.pt = RTCP_XR;
    header.rc = id;
    header.length = (r + 4 + 3) / 4;
    nbo_write_rtcp_header(data, &header);

    nbo_w32(data + 4, ctx->self->ssrc);
    //nbo_w32(data + 4, xr->sender);

    //assert(8 == (header.length + 1) * 4);
    return header.length * 4 + 4;
}

#if defined(_DEBUG) || defined(DEBUG)
static void rtcp_on_xr_test(void* param, const struct rtcp_msg_t* msg)
{
    int r;
    static uint8_t buffer[1400];
    switch (msg->type & 0xFF)
    {
    case RTCP_XR:
        switch ((msg->type >> 8) & 0xFF)
        {
        case RTCP_XR_RRT:
            assert(0x1234567823456789 == msg->u.xr.u.rrt);
            r = rtcp_xr_rrt_pack(msg->u.xr.u.rrt, buffer, sizeof(buffer));
            assert(12 == r && 0 == memcmp(buffer, param, r));
            break;

        case RTCP_XR_DLRR:
            assert(1 == msg->u.xr.u.dlrr.count);
            assert(0x12345678 == msg->u.xr.u.dlrr.dlrr[0].ssrc && 0x23344556 == msg->u.xr.u.dlrr.dlrr[0].lrr && 0x33343536 == msg->u.xr.u.dlrr.dlrr[0].dlrr);
            r = rtcp_xr_dlrr_pack(msg->u.xr.u.dlrr.dlrr, msg->u.xr.u.dlrr.count, buffer, sizeof(buffer));
            assert(16 == r && 0 == memcmp(buffer, param, r));
            break;

        case RTCP_XR_ECN:
            r = rtcp_xr_ecn_pack(&msg->u.xr.u.ecn, buffer, sizeof(buffer));
            assert(r > 0 && 0 == memcmp(buffer, param, r));

        default:
            break;
        }
        break;

    default:
        assert(0);
    }
}

static void rtcp_rtpfb_rrt_test(void)
{
    const uint8_t data[] = { 0x04, 0x00, 0x00, 0x02, 0x12, 0x34, 0x56, 0x78, 0x23, 0x45, 0x67, 0x89 };

    struct rtcp_msg_t msg;
    struct rtp_context rtp;
    rtp.handler.on_rtcp = rtcp_on_xr_test;
    rtp.cbparam = (void*)data;

    msg.type = (RTCP_XR_RRT << 8) | RTCP_XR;
    assert(0 == rtcp_xr_rrt_unpack(&rtp, NULL, &msg, data, sizeof(data)));
}

static void rtcp_rtpfb_dlrr_test(void)
{
    const uint8_t data[] = { 0x05, 0x00, 0x00, 0x03, 0x12, 0x34, 0x56, 0x78, 0x23, 0x34, 0x45, 0x56, 0x33, 0x34, 0x35, 0x36 };

    struct rtcp_msg_t msg;
    struct rtp_context rtp;
    rtp.handler.on_rtcp = rtcp_on_xr_test;
    rtp.cbparam = (void*)data;

    msg.type = (RTCP_XR_DLRR << 8) | RTCP_XR;
    assert(0 == rtcp_xr_dlrr_unpack(&rtp, NULL, &msg, data, sizeof(data)));
}

static void rtcp_rtpfb_ecn_test(void)
{
    const uint8_t data[] = { 0x00 };

    struct rtcp_msg_t msg;
    struct rtp_context rtp;
    rtp.handler.on_rtcp = rtcp_on_xr_test;
    rtp.cbparam = (void*)data;

    msg.type = (RTCP_XR_ECN << 8) | RTCP_XR;
    assert(0 == rtcp_xr_ecn_unpack(&rtp, NULL, &msg, data, sizeof(data)));
}

void rtcp_xr_test(void)
{
    rtcp_rtpfb_rrt_test();
    rtcp_rtpfb_dlrr_test();
    //rtcp_rtpfb_ecn_test();
}
#endif
