// Copyright (C) 2002-2015 Federico Montesino Pouzols <fedemp@altern.org>.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with GNU ccRTP.  If not, see <http://www.gnu.org/licenses/>.
//
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
// This exception applies only to the code released under the name GNU
// ccRTP.  If you copy code from other releases into a copy of GNU
// ccRTP, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU ccRTP, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//

#ifndef CCXX_RTP_RTPPKT_H_
#define CCXX_RTP_RTPPKT_H_

#include <ccrtp/base.h>
#include <ccrtp/formats.h>
#include <ccrtp/CryptoContext.h>

NAMESPACE_COMMONCPP

/**
 * @file rtppkt.h
 *
 * @short RTP packets handling.
 **/

/**
 * @defgroup rtppacket RTP data packets manipulation.
 * @{
 **/

/**
 * @class RTPPacket
 * @short A base class for both IncomingRTPPkt and OutgoingRTPPkt.
 *
 * Provides common low level header structures and related
 * methods. This class provides an interface that allows for partial
 * and generic manipulation of RTP data packets. Values are returned
 * in host order, except raw structures, which are returned as they
 * are sent through the network.
 *
 * @author David Sugar <dyfet@ostel.com>
 **/

class CryptoContext;

class  __EXPORT RTPPacket
{
private:
    struct RTPFixedHeader;
    struct RTPHeaderExt;

public:
    /**
     * Constructor, construct a packet object given the memory
     * zone its content (header and payload) is stored. Commonly
     * used to build RTPPacket objects from incoming data.
     *
     * @param block whole packet
     * @param len total length (header + payload + padding) of the
     *        packet
     * @param duplicate whether to memcopy the packet. At present,
     *        this feature is not used.
     * @note used in IncomingRTPPkt.
     **/
    RTPPacket(const unsigned char* const block, size_t len,
          bool duplicate = false);

    /**
     * Construct a packet object without specifying its real
     * content yet. Commonly used for outgoing packets. Header
     * fields and payload must be filled in by another methods or
     * by a derived constructor.
     *
     * @param hdrlen length of the header (including CSRC and extension).
     * @param plen payload length.
     * @param paddinglen pad packet to a multiple of paddinglen
     * @note used in OutgoingRTPPkt.
     */
        RTPPacket(size_t hdrlen, size_t plen, uint8 paddinglen, CryptoContext* pcc= NULL);

    /**
     * Get the length of the header, including contributing
     * sources identifiers and header extension, if present.
     *
     * @return number of octets.
     **/
    inline uint32
    getHeaderSize() const
    { return hdrSize; }

    /**
     * @return pointer to the payload section of the packet.
     **/
    inline const uint8* const
    getPayload() const
    { return (uint8*)(buffer + getHeaderSize()); }

    /**
     * @return length of the payload section, in octets.
     **/
    inline uint32
    getPayloadSize() const
    { return payloadSize; }

    /**
     * @return value of the PT header field.
     **/
    inline PayloadType
    getPayloadType() const
    { return static_cast<PayloadType>(getHeader()->payload); }

    /**
     * @return value of the sequence number header field, in host order.
     **/
    inline uint16
    getSeqNum() const
    { return cachedSeqNum; }

    /**
     * @return packet timestamp in host order.
     **/
    inline uint32
    getTimestamp() const
    { return cachedTimestamp; }

    /**
     * @return RTP protocol version of packet.
     **/
    inline uint8
    getProtocolVersion() const
    { return getHeader()->version; }

    /**
     * Ask whether the packet contains padding bytes at the end
     * @return true if the header padding bit is 1.
     **/
    inline bool
    isPadded() const
    { return getHeader()->padding; }

    /**
     * Get the number of octets padding the end of the payload
     * section.
     *
     * @return Padding length in octets.
     **/
    inline uint8
    getPaddingSize() const
    { return buffer[total - 1]; }

    /**
     * Ask whether the packet is marked (for isntance, is a new
     * talk spurt in some audio profiles).
     *
     * @return true is the header marker bit is 1.
     **/
    inline bool
    isMarked() const
    { return getHeader()->marker; }

    /**
     * Ask whether the packet contains header extensions.
     *
     * @return true if the header extension bit is 1.
     **/
    inline bool
    isExtended() const
    { return getHeader()->extension; }

    /**
     * Get the number of contributing sources specified in the
     * packet header.
     **/
    inline uint16
    getCSRCsCount() const
    { return getHeader()->cc; }

    /**
     * Get the 32-bit identifiers of the contributing sources for
     * the packet as an array, of length getCSRCsCount().
     *
     * @return An array of CSRC identifiers as they are in the
     * packet (in network order).
     **/
    inline const uint32*
    getCSRCs() const
    { return static_cast<const uint32*>(&(getHeader()->sources[1])); }

    /**
     * Get the first 16 bits (in network order) of the header of
     * the RTP header extension. Its meaning is undefined at this
     * level.
     *
     * @return 0 if the packet has no header extension, otherwise
     *         the first 16 bits of the header extension, in
     *         network order.
     *
     * @note 0 could be a valid value for the first 16 bits, in
     *         that case RTPPacket::isExtended() should be use.
     **/
    inline uint16
    getHdrExtUndefined() const
    { return (isExtended()? getHeaderExt()->undefined : 0); }

    /**
     * Get the length (in octets) of the data contained in the
     * header extension. Note that this length does not include
     * the four octets at the beginning of the header extension.
     *
     * @return 0 if the packet has no header extension, otherwise
     *         the length.
     *
     * @note 0 is a valid value for this field, so
     *       RTPPacket::isExtended() should be used.
     **/
    inline uint32
    getHdrExtSize() const
    { return (isExtended()?
          (static_cast<uint32>(ntohs(getHeaderExt()->length)) << 2) :
          0); }

    /**
     * Get the content of the header extension.
     *
     * @return NULL if the packet has no header extension, otherwise
     *         a pointer to the packet header extension content.
     **/
    inline const unsigned char*
    getHdrExtContent() const
    { return (isExtended() ?
          (reinterpret_cast<const unsigned char*>(getHeaderExt()) +
           sizeof(RTPHeaderExt)) :
          NULL); }

    /**
     * Get the raw packet as it will be sent through the network.
     *
     * @return memory zone where the raw packet structure is
     *         stored in.
     **/
    inline const unsigned char* const
    getRawPacket() const
    { return buffer; }

    /**
     * Get the raw packet length, including header, extension,
     * payload and padding.
     *
     * @return size of the raw packet structure.
     **/
    inline uint32
    getRawPacketSize() const
    { return total; }

        inline uint32
        getRawPacketSizeSrtp() const
        { return total + srtpLength; }

        inline size_t
    getSizeOfFixedHeader() const
    { return sizeof(RTPFixedHeader); }

    /**
     * Re-compute payload length.
     *
     * This recomputation may be necessary in case of SRTP. We need to decrypt
     * the packet before we can handle padding. See @c takeInDataPacket in
     * @c incqueue.cpp
     *
     * @param padding
     *     If true then set padding flag in RTP header and re-compute
     *     payloadSize.
     */
    void reComputePayLength(bool padding);

protected:
    /**
     * Destructor, free the buffer provided in the constructor.
     **/
    inline virtual ~RTPPacket()
    { endPacket(); }

    /**
     * Free memory allocated for the packet.
     **/
    void
    endPacket();

    /**
     * Return low level structure for the header of the packet.
     *
     * @return RTPFixedHeader pointer to the header of the packet.
     **/
    inline RTPFixedHeader*
    getHeader() const
    { return reinterpret_cast<RTPFixedHeader*>(buffer); }

    inline void
    setExtension(bool e)
    { getHeader()->extension = e; }

    /**
     * Get a pointer to RTPHeaderExt pointing after the RTP header
     * (fixed part plus contributing sources). No check for
     * for the X bit is done.
     *
     * @return header extension if present, garbage if not.
     **/
    inline const RTPHeaderExt*
    getHeaderExt() const
    {
         uint32 fixsize = sizeof(RTPFixedHeader) + (getHeader()->cc << 2);
     return (reinterpret_cast<RTPHeaderExt*>(buffer + fixsize));
    }

    /**
     * Obtain the absolute timestamp carried in the packet header.
     *
     * @return 32-bit timestamp in host order.
     **/
    inline uint32
    getRawTimestamp() const
    { return ntohl(getHeader()->timestamp); }

    inline void
    setbuffer(const void* src, size_t len, size_t pos)
    { memcpy(buffer + pos,src,len); }

    /// Packet sequence number in host order.
    uint16 cachedSeqNum;
    /// Packet timestamp in host order (includes initial shift).
    uint32 cachedTimestamp;

        /**
         * Offset into packet memory pointing to area for SRTP data.
         *
         * This offset points to the memory where the SRTP protect will
         * store the authentication and MKI data.
         */
        uint32 srtpDataOffset;

        /**
         * Lebgth of additional SRTP data.
         *
         * Covers the SRTP authentication and MKI data.
         */
        int32 srtpLength;

        /// total length, including header, payload and padding
        uint32 total;

        /// note: payload (not full packet) size.
        uint32 payloadSize;

private:
    /// packet in memory
    unsigned char* buffer;
    /// size of the header, including contributing sources and extensions
    uint32 hdrSize;
    /// whether the object was contructed with duplicated = true
    bool duplicated;

#ifdef  CCXX_PACKED
#pragma pack(1)
#endif
    /**
     * @struct RTPFixedHeader
     * @short RTP fixed header as it is send through the network.
     *
     * A low-level representation for generic RTP packet header as
     * defined in RFC 1889. A packet consists of the fixed RTP
     * header, a possibly empty list of contributing sources and
     * the payload. Header contents are kept in network (big
     * endian) order.
     **/
    struct RTPFixedHeader
    {
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN
        /// For big endian boxes
        unsigned char version:2;       ///< Version, currently 2
        unsigned char padding:1;       ///< Padding bit
        unsigned char extension:1;     ///< Extension bit
        unsigned char cc:4;            ///< CSRC count
        unsigned char marker:1;        ///< Marker bit
        unsigned char payload:7;       ///< Payload type
#else
        /// For little endian boxes
        unsigned char cc:4;            ///< CSRC count
        unsigned char extension:1;     ///< Extension bit
        unsigned char padding:1;       ///< Padding bit
        unsigned char version:2;       ///< Version, currently 2
        unsigned char payload:7;       ///< Payload type
        unsigned char marker:1;        ///< Marker bit
#endif
        uint16 sequence;        ///< sequence number
        uint32 timestamp;       ///< timestamp
        uint32 sources[1];      ///< contributing sources
    };

    /**
     * @struct RFC2833Payload
     * @short a structure defining RFC2833 Telephony events.
     *
     * structure to define RFC2833 telephony events in RTP.  You can
     * use this by recasing the pointer returned by getPayload().
     */

public:
    struct RFC2833Payload
    {
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN
            uint8 event : 8;
            bool ebit : 1;
            bool rbit : 1;
            uint8 vol : 6;
            uint16 duration : 16;
#else
            uint8 event : 8;
            uint8 vol : 6;
            bool rbit : 1;
            bool ebit : 1;
            uint16 duration : 16;
#endif
    };

private:
    /**
     * @struct RTPHeaderExt
     *
     * Fixed component of the variable-length header extension,
     * appended to the fixed header, after the CSRC list, when X
     * == 1.
     **/
    struct RTPHeaderExt
    {
        uint16 undefined; ///< to be defined
        uint16 length;    ///< number of 32-bit words in the extension
    };
#ifdef  CCXX_PACKED
#pragma pack()
#endif

    /* definitions for access to most common 2833 fields... */

public:
    /**
     * Fetch a raw 2833 packet.
     *
     * @return low level 2833 data structure.
     */
    inline struct RFC2833Payload *getRaw2833Payload(void)
        {return (struct RFC2833Payload *)getPayload();}

    /**
     * Fetch 2833 duration field.
     *
     * @return 2833 duration in native host machine byte order.
     */
    inline uint16 get2833Duration(void)
        {return ntohs(getRaw2833Payload()->duration);}

    /**
     * Set 2833 duration field.
     *
     * @param timestamp to use, native host machine byte order.
     */
    inline void set2833Duration(uint16 timestamp)
        {getRaw2833Payload()->duration = htons(timestamp);}
};

/**
 * @class OutgoingRTPPkt
 * @short RTP packets being sent.
 *
 * This class is intented to construct packet objects just before they
 * are inserted into the sending queue, so that they are processed in
 * a understandable and format independent manner inside the stack.
 *
 * @author Federico Montesino Pouzols <fedemp@altern.org>
 **/
class __EXPORT OutgoingRTPPkt : public RTPPacket
{
public:
    /**
     * Construct a new packet to be sent, containing several
     * contributing source identifiers, header extensions and
     * payload.
         *
         * A new copy in memory (holding all this components
     * along with the fixed header) is created. If the pointer
         * to the SRTP CryptoContext is not NULL and holds a CryptoContext
         * for the SSRC take the SSRC data into account when computing
         * the required memory buffer.
     *
     * @param csrcs array of countributing source 32-bit
     *        identifiers, in host order.
     * @param numcsrc number of CSRC identifiers in the array.
     * @param hdrext whole header extension.
     * @param hdrextlen size of whole header extension, in octets.
     * @param data payload.
     * @param datalen payload length, in octets.
     * @param paddinglen pad packet to a multiple of paddinglen.
         * @param pcc Pointer to the SRTP CryptoContext, defaults to NULL
         * if not specified.
     *
     * @note For efficiency purposes, since this constructor is
     * valid for all packets but is too complex for the common
     * case, two simpler others are provided.
     **/
    OutgoingRTPPkt(const uint32* const csrcs, uint16 numcsrc,
               const unsigned char* const hdrext, uint32 hdrextlen,
               const unsigned char* const data, size_t datalen,
                       uint8 paddinglen= 0, CryptoContext* pcc= NULL);

    /**
     * Construct a new packet to be sent, containing several
     * contributing source identifiers and payload.
         *
         * A new copy in
     * memory (holding all this components along with the fixed
         * header) is created. If the pointer
         * to the SRTP CryptoContext is not NULL and holds a CryptoContext
         * for the SSRC take the SSRC data into account when computing
         * the required memory buffer.
     *
     * @param csrcs array of countributing source 32-bit
     * identifiers, in host order.
     * @param numcsrc number of CSRC identifiers in the array.
     * @param data payload.
     * @param datalen payload length, in octets.
     * @param paddinglen pad packet to a multiple of paddinglen.
         * @param pcc Pointer to the SRTP CryptoContext, defaults to NULL
         * if not specified.
         **/
    OutgoingRTPPkt(const uint32* const csrcs, uint16 numcsrc,
               const unsigned char* const data, size_t datalen,
                       uint8 paddinglen= 0, CryptoContext* pcc= NULL);

    /**
     * Construct a new packet (fast variant, with no contributing
     * sources and no header extension) to be sent.
         *
         * A new copy in
         * memory (holding the whole packet) is created. If the pointer
         * to the SRTP CryptoContext is not NULL and holds a CryptoContext
         * for the SSRC take the SSRC data into account when computing
         * the required memory buffer.
     *
     * @param data payload.
     * @param datalen payload length, in octets.
     * @param paddinglen pad packet to a multiple of paddinglen.
         * @param pcc Pointer to the SRTP CryptoContext, defaults to NULL
         * if not specified.
         **/
    OutgoingRTPPkt(const unsigned char* const data, size_t datalen,
                       uint8 paddinglen= 0, CryptoContext* pcc= NULL);

    ~OutgoingRTPPkt()
    { }

    /**
     * @param pt Packet payload type.
     **/
    inline void
    setPayloadType(PayloadType pt)
    { getHeader()->payload = pt; }

    /**
         * Sets the sequence number in the header.
         *
     * @param seq Packet sequence number, in host order.
     **/
    inline void
    setSeqNum(uint16 seq)
    {
        cachedSeqNum = seq;
        getHeader()->sequence = htons(seq);
    }

    /**
     * @param pts Packet timestamp, in host order.
     **/
    inline void
    setTimestamp(uint32 pts)
    {
        cachedTimestamp = pts;
        getHeader()->timestamp = htonl(pts);
    }

    /**
     * Set synchronization source numeric identifier.
     *
     * @param ssrc 32-bit Synchronization SouRCe numeric
     * identifier, in host order.
     **/
    inline void
    setSSRC(uint32 ssrc) const
    { getHeader()->sources[0] = htonl(ssrc); }

    /**
     * Set synchronization source numeric identifier. Special
     * version to save endianness conversion.
     *
     * @param ssrc 32-bit Synchronization SouRCe numeric
     * identifier, in network order.
     **/
    inline void
    setSSRCNetwork(uint32 ssrc) const
    { getHeader()->sources[0] = ssrc; }

    /**
     * Specify the value of the marker bit. By default, the marker
     * bit of outgoing packets is false/0. This method allows to
     * explicity specify and change that value.
     *
     * @param mark value for the market bit.
     */
    inline void
    setMarker(bool mark)
    { getHeader()->marker = mark; }

        /**
         * Called packet is setup.
         *
         * This private method computes the SRTP data and stores it in the
         * packet. Then encrypt the payload data (ex padding).
         */
        void protect(uint32 ssrc, CryptoContext* pcc);

    /**
     * Outgoing packets are equal if their sequence numbers match.
     **/
    inline bool
    operator==(const OutgoingRTPPkt &p) const
    { return ( this->getSeqNum() == p.getSeqNum() ); }

    /**
     * Outgoing packets are not equal if their sequence numbers differ.
     **/
    inline bool
    operator!=(const OutgoingRTPPkt &p) const
    { return ( this->getSeqNum() != p.getSeqNum() ); }

private:
    /**
     * Copy constructor from objects of its same kind, declared
     * private to avoid its use.
     **/
    OutgoingRTPPkt(const OutgoingRTPPkt &o);

    /**
     * Assignment operator from objects of its same kind, declared
     * private to avoid its use.
     **/
    OutgoingRTPPkt&
    operator=(const OutgoingRTPPkt &o);

    /**
     * Set the list of CSRC identifiers in an RTP packet,
     * switching host to network order.
     */
    void setCSRCArray(const uint32* const csrcs, uint16 numcsrc);

};

/**
 * @class IncomingRTPPkt
 *
 * @short RTP packets received from other participants.
 *
 * This class is intented to construct a packet object just after
 * every packet is received by the scheduled queue, so that they are
 * processed in an understandable and format independent manner inside
 * the stack.
 *
 * @author Federico Montesino Pouzols <fedemp@altern.org>
 */
class __EXPORT IncomingRTPPkt : public RTPPacket
{
public:
    /**
     * Build an RTP packet object from a data buffer. This
     * constructor first performs a generic RTP data packet header
     * check, whose result can be checked via isHeaderValid().
     *
     * @param block pointer to the buffer the whole packet is stored in.
     * @param len length of the whole packet, expressed in octets.
     *
     * @note If check fails, the packet object is
     * incomplete. checking isHeaderValid() is recommended before
     * using a new RTPPacket object.
     **/
    IncomingRTPPkt(const unsigned char* block, size_t len);

    ~IncomingRTPPkt()
    { }

    /**
     * Get validity of this packet
     * @return whether the header check performed at construction
     *         time ended successfully.
     **/
    inline bool
    isHeaderValid()
    { return headerValid; }

    /**
     * Get synchronization source numeric identifier.
     *
     * @return 32-bits Synchronization SouRCe numeric identifier,
     * in host order.
     **/
    inline uint32
    getSSRC() const
    { return cachedSSRC; }

        /**
         * Unprotect a received packet.
         *
         * Perform SRTP processing on this packet.
         *
         * @param pcc Pointer to SRTP CryptoContext.
         * @return
         *     one if no errors, -1 if authentication failed, -2 if
         *     replay check failed
         */
        int32
        unprotect(CryptoContext* pcc);

    /**
     * Two incoming packets are equal if they come from sources
     * with the same SSRC and have the same sequence number.
     **/
    inline bool
    operator==(const IncomingRTPPkt &p) const
    { return ( (this->getSeqNum() == p.getSeqNum()) &&
           (this->getSSRC() == p.getSSRC()) ); }

    /**
     * Two incoming packets are not equal if they come from
     * different sources or have different sequence numbers.
     **/
    inline bool
    operator!=(const IncomingRTPPkt &p) const
    { return !( *this == p ); }

private:
    /**
     * Copy constructor from objects of its same kind, declared
     * private to avoid its use.
     **/
    IncomingRTPPkt(const IncomingRTPPkt &ip);

    /**
     * Assignment operator from objects of its same kind, declared
     * private to avoid its use.
     */
    IncomingRTPPkt&
    operator=(const IncomingRTPPkt &ip);

    /// Header validity, checked at construction time.
    bool headerValid;
    /// SSRC 32-bit identifier in host order.
    uint32 cachedSSRC;
    // Masks for RTP header validation: types matching RTCP SR or
    // RR must be rejected to avoid accepting misaddressed RTCP
    // packets.
    static const uint16 RTP_INVALID_PT_MASK;
    static const uint16 RTP_INVALID_PT_VALUE;
};

/** @}*/ // rtppacket

END_NAMESPACE

#endif  // ndef CCXX_RTP_RTPPKT_H_

/** EMACS **
 * Local variables:
 * mode: c++
 * c-basic-offset: 8
 * End:
 */
