/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is MPEG4IP.
 *
 * The Initial Developer of the Original Code is Cisco Systems Inc.
 * Portions created by Cisco Systems Inc. are
 * Copyright (C) Cisco Systems Inc. 2001.  All Rights Reserved.
 *
 * Contributor(s):
 *      Dave Mackie     dmackie@cisco.com
 */

#ifndef MP4V2_IMPL_RTPHINT_H
#define MP4V2_IMPL_RTPHINT_H

namespace mp4v2 {
namespace impl {

///////////////////////////////////////////////////////////////////////////////

// forward declarations
class MP4RtpHintTrack;
class MP4RtpHint;
class MP4RtpPacket;

class MP4RtpData : public MP4Container {
public:
    MP4RtpData(MP4RtpPacket& packet);

    MP4RtpPacket& GetPacket() {
        return m_packet;
    }

    virtual uint16_t GetDataSize() = 0;
    virtual void GetData(uint8_t* pDest) = 0;

    MP4Track* FindTrackFromRefIndex(uint8_t refIndex);

    virtual void WriteEmbeddedData(MP4File& file, uint64_t startPos) {
        // default is no-op
    }

protected:
    MP4RtpPacket& m_packet;
};

MP4ARRAY_DECL(MP4RtpData, MP4RtpData*)

class MP4RtpNullData : public MP4RtpData {
public:
    MP4RtpNullData(MP4RtpPacket& packet);

    uint16_t GetDataSize() {
        return 0;
    }

    void GetData(uint8_t* pDest) {
        // no-op
    }
};

class MP4RtpImmediateData : public MP4RtpData {
public:
    MP4RtpImmediateData(MP4RtpPacket& packet);

    void Set(const uint8_t* pBytes, uint8_t numBytes);

    uint16_t GetDataSize();

    void GetData(uint8_t* pDest);
};

class MP4RtpSampleData : public MP4RtpData {
public:
    MP4RtpSampleData(MP4RtpPacket& packet);

    ~MP4RtpSampleData(void) {
        CHECK_AND_FREE(m_pRefData);
    };

    void SetEmbeddedImmediate(
        MP4SampleId sampleId,
        uint8_t* pData, uint16_t dataLength);

    void SetReferenceSample(
        MP4SampleId refSampleId, uint32_t refSampleOffset,
        uint16_t sampleLength);

    void SetEmbeddedSample(
        MP4SampleId sampleId, MP4Track* pRefTrack,
        MP4SampleId refSampleId, uint32_t refSampleOffset,
        uint16_t sampleLength);

    uint16_t GetDataSize();

    void GetData(uint8_t* pDest);

    void WriteEmbeddedData(MP4File& file, uint64_t startPos);

protected:
    uint8_t*        m_pRefData;

    MP4Track*       m_pRefTrack;
    MP4SampleId     m_refSampleId;
    uint32_t        m_refSampleOffset;
};

class MP4RtpSampleDescriptionData : public MP4RtpData {
public:
    MP4RtpSampleDescriptionData(MP4RtpPacket& packet);

    void Set(uint32_t sampleDescrIndex,
             uint32_t offset, uint16_t length);

    uint16_t GetDataSize();

    void GetData(uint8_t* pDest);
};

class MP4RtpPacket : public MP4Container {
public:
    MP4RtpPacket(MP4RtpHint& hint);

    ~MP4RtpPacket();

    void AddExtraProperties();

    MP4RtpHint& GetHint() {
        return m_hint;
    }

    void Set(uint8_t payloadNumber, uint32_t packetId, bool setMbit);

    int32_t GetTransmitOffset();

    bool GetPBit();

    bool GetXBit();

    bool GetMBit();

    uint8_t GetPayload();

    uint16_t GetSequenceNumber();

    void SetTransmitOffset(int32_t transmitOffset);

    bool IsBFrame();

    void SetBFrame(bool isBFrame);

    void SetTimestampOffset(uint32_t timestampOffset);

    void AddData(MP4RtpData* pData);

    uint32_t GetDataSize();

    void GetData(uint8_t* pDest);

    void Read(MP4File& file);

    void ReadExtra(MP4File& file);

    void Write(MP4File& file);

    void WriteEmbeddedData(MP4File& file, uint64_t startPos);

    void Dump(uint8_t indent, bool dumpImplicits);

protected:
    MP4RtpHint&         m_hint;
    MP4RtpDataArray     m_rtpData;
};

MP4ARRAY_DECL(MP4RtpPacket, MP4RtpPacket*)

class MP4RtpHint : public MP4Container {
public:
    MP4RtpHint(MP4RtpHintTrack& track);

    ~MP4RtpHint();

    MP4RtpHintTrack& GetTrack() {
        return m_track;
    }

    uint16_t GetNumberOfPackets() {
        return m_rtpPackets.Size();
    }

    bool IsBFrame() {
        return m_isBFrame;
    }
    void SetBFrame(bool isBFrame) {
        m_isBFrame = isBFrame;
    }

    uint32_t GetTimestampOffset() {
        return m_timestampOffset;
    }
    void SetTimestampOffset(uint32_t timestampOffset) {
        m_timestampOffset = timestampOffset;
    }

    MP4RtpPacket* AddPacket();

    MP4RtpPacket* GetPacket(uint16_t index) {
        return m_rtpPackets[index];
    }

    MP4RtpPacket* GetCurrentPacket() {
        if (m_rtpPackets.Size() == 0) {
            return NULL;
        }
        return m_rtpPackets[m_rtpPackets.Size() - 1];
    }

    void Read(MP4File& file);

    void Write(MP4File& file);

    void Dump(uint8_t indent, bool dumpImplicits);

protected:
    MP4RtpHintTrack&    m_track;
    MP4RtpPacketArray   m_rtpPackets;

    // values when adding packets to a hint (write mode)
    bool                m_isBFrame;
    uint32_t            m_timestampOffset;
};

class MP4RtpHintTrack : public MP4Track {
public:
    MP4RtpHintTrack(MP4File& file, MP4Atom& trakAtom);

    ~MP4RtpHintTrack();

    void InitRefTrack();

    void InitPayload();

    void InitRtpStart();

    void InitStats();

    MP4Track* GetRefTrack() {
        InitRefTrack();
        return m_pRefTrack;
    }

    void GetPayload(
        char** ppPayloadName = NULL,
        uint8_t* pPayloadNumber = NULL,
        uint16_t* pMaxPayloadSize = NULL,
        char **ppEncodingParams = NULL);

    void SetPayload(
        const char* payloadName,
        uint8_t payloadNumber,
        uint16_t maxPayloadSize,
        const char *encoding_parms,
        bool add_rtpmap,
        bool add_mpeg4_esid);

    void ReadHint(
        MP4SampleId hintSampleId,
        uint16_t* pNumPackets = NULL);

    uint16_t GetHintNumberOfPackets();

    bool GetPacketBFrame(uint16_t packetIndex);

    uint16_t GetPacketTransmitOffset(uint16_t packetIndex);

    void ReadPacket(
        uint16_t packetIndex,
        uint8_t** ppBytes,
        uint32_t* pNumBytes,
        uint32_t ssrc,
        bool includeHeader = true,
        bool includePayload = true);

    MP4Timestamp GetRtpTimestampStart();

    void SetRtpTimestampStart(MP4Timestamp start);

    void AddHint(bool isBFrame, uint32_t timestampOffset);

    void AddPacket(bool setMbit, int32_t transmitOffset = 0);

    void AddImmediateData(const uint8_t* pBytes, uint32_t numBytes);

    void AddSampleData(MP4SampleId sampleId,
                       uint32_t dataOffset, uint32_t dataLength);

    void AddESConfigurationPacket();

    void WriteHint(MP4Duration duration, bool isSyncSample);

    void FinishWrite(uint32_t options = 0);

protected:
    MP4Track*   m_pRefTrack;

    MP4StringProperty*      m_pRtpMapProperty;
    MP4Integer32Property*   m_pPayloadNumberProperty;
    MP4Integer32Property*   m_pMaxPacketSizeProperty;
    MP4Integer32Property*   m_pSnroProperty;
    MP4Integer32Property*   m_pTsroProperty;
    uint32_t                m_rtpSequenceStart;
    uint32_t                m_rtpTimestampStart;

    // reading
    MP4RtpHint* m_pReadHint;
    uint8_t*    m_pReadHintSample;
    uint32_t    m_readHintSampleSize;
    MP4Timestamp m_readHintTimestamp;

    // writing
    MP4RtpHint* m_pWriteHint;
    MP4SampleId m_writeHintId;
    uint32_t    m_writePacketId;

    // statistics
    // in trak.udta.hinf
    MP4Integer64Property*   m_pTrpy;
    MP4Integer64Property*   m_pNump;
    MP4Integer64Property*   m_pTpyl;
    MP4Integer32Property*   m_pMaxr;
    MP4Integer64Property*   m_pDmed;
    MP4Integer64Property*   m_pDimm;
    MP4Integer32Property*   m_pPmax;
    MP4Integer32Property*   m_pDmax;

    // in trak.mdia.minf.hmhd
    MP4Integer16Property*   m_pMaxPdu;
    MP4Integer16Property*   m_pAvgPdu;
    MP4Integer32Property*   m_pMaxBitRate;
    MP4Integer32Property*   m_pAvgBitRate;

    MP4Timestamp            m_thisSec;
    uint32_t                m_bytesThisSec;
    uint32_t                m_bytesThisHint;
    uint32_t                m_bytesThisPacket;
};

///////////////////////////////////////////////////////////////////////////////

}
} // namespace mp4v2::impl

#endif // MP4V2_IMPL_RTPHINT_H
