/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */
#ifndef H264_PAYLOAD_PARSER_INTERLEAVE_MODE_H
#define H264_PAYLOAD_PARSER_INTERLEAVE_MODE_H


#ifndef OSCL_PRIQUEUE_H_INCLUDED
#include "oscl_priqueue.h"
#endif

#ifndef OSCL_MEM_H_INCLUDED
#include "oscl_mem.h"
#endif

#ifndef PAYLOAD_PARSER_H_INCLUDED
#include "payload_parser.h"
#endif

#ifndef H264_PAYLOAD_PARSER_MACROS_H_INCLUDED
#include "h264_payload_parser_macros.h"
#endif

/////////////////////////////////////////////////////////////////////////////////
////////////// priority queue related data structures ///////////////////////////
/////////////////////////////////////////////////////////////////////////////////
class PVH264PayLoadOutputMediaData
{
    public:
        PVH264PayLoadOutputMediaData() : don(0), fuType(0) {};

        PVH264PayLoadOutputMediaData(const PVH264PayLoadOutputMediaData& x)
        {
            data  = x.data;
            don  = x.don;
            fuType = x.fuType;
        }

        PVH264PayLoadOutputMediaData(IPayloadParser::Payload adata, uint32 aDon, uint32 aFuType)
        {
            data = adata;
            don = aDon;
            fuType = aFuType;
        }

        //required for compile of OsclPriorityQueue but not currently used & not implemented.
        //would be needed if code called "remove".
        bool operator==(const PVH264PayLoadOutputMediaData& x)const
        {
            OSCL_UNUSED_ARG(&x);
            OSCL_ASSERT(0);
            return false;
        }

    public:
        IPayloadParser::Payload data;
        uint32  don;
        uint32  fuType; // First byte: current fuType
        // 0 complete NAL
        // 1 starting FU
        // 2 intermediate FU
        // 3 ending FU
        // Second byte: previous fuType
        // Third-Fourth bytes: counter for intermediate FUs for the following compare function

};

// priority comparison class
class PVH264PayLoadOutputMediaDataCompareLess
{
    public:
        /**
        * The algorithm used in OsclPriorityQueue needs a compare function
        * that returns true when a's priority is less than b's. In the
        * current context, when a's don is larger than b's don, return true
        * Basically when a's don is smaller than b's don, then a's priority
        * is higher than b's.
        * @return true if a's priority is less than b's, else false
        */
        int compare(PVH264PayLoadOutputMediaData& a, PVH264PayLoadOutputMediaData& b) const
        {
            int32 diffDon = PVH264PayLoadOutputMediaDataCompareLess::don_diff(a.don, b.don);
            if (diffDon == 0)
            {
                // use fuType
                diffDon = PVH264PayLoadOutputMediaDataCompareLess::fuType_diff(a.fuType, b.fuType);
            }
            return (diffDon < 0);
        }

    private:

        static int32 don_diff(uint32 m_don, uint32 n_don)
        {
            if (m_don == n_don) return 0;

            if (m_don < n_don && (n_don - m_don < 32768))
                return ((int32)n_don - (int32)m_don);

            if (m_don < n_don && (n_don - m_don >= 32768))
                return ((int32)n_don - (int32)m_don - 65536);

            if (m_don > n_don && (m_don - n_don < 32768))
                return -((int32)m_don - (int32)n_don);

            if (m_don > n_don && (m_don - n_don >= 32768))
                return (65536 - m_don + n_don);

            return 0;
        }

        static int32 fuType_diff(uint32 m_fuType, uint32 n_fuType)
        {
            int32 diff_fuType = (int32)(n_fuType & 0x03) - (int32)(m_fuType & 0x03);

            // for intermediate FUs
            if (diff_fuType == 0)
            {
                diff_fuType = (int32)(n_fuType >> 16) - (int32)(m_fuType >> 16);
            }

            return diff_fuType;
        }
};

//memory allocator type for this node.
typedef OsclMemAllocator PVH264PayloadParserAllocator;


class InterleaveModeProcessing
{

    public:

        // constructor
        InterleaveModeProcessing() : iNALUnitsCounter(0),
                iDon(0),
                iDonBase(0),
                iTimestampOffset(0),
                iPrevNALType(0),
                iRtpPayloadPtr(NULL)
        {
            ;
        }

        // destructor
        ~InterleaveModeProcessing()
        {

            // for iOutputMediaDataQueue, remove all the elements from the output data priority queue
            while (!iOutputMediaDataQueue.empty())
            {
                iOutputMediaDataQueue.pop();
            }

            iNALUnitsCounter = iDon = iDonBase =
                                          iTimestampOffset = iPrevNALType = 0;
            iRtpPayloadPtr = NULL;
        }

        // initialize the iOutputMediaDataQueue when in the interleaved mode
        bool initialize(uint32 aInterleaveDepth)
        {
            if (aInterleaveDepth > 0)
            {
                int32 err = 0;
                OSCL_TRY(err, iOutputMediaDataQueue.reserve((aInterleaveDepth + 1) << 2));
                if (err) return false;
            }
            return true;
        }

        // push data to priority queue
        bool queue(const IPayloadParser::Payload& aMediaData, const uint8 nal_type, const uint32 fragmentedBitMask)
        {
            uint32 prev_fuType = iPrevNALType & 0x03;
            uint32 fuType = fragmentedBitMask & 0x03;
            uint32 fuCounter = 0;
            if (fuType == 2)  // for the above fuType_diff() in priority comparsion
            {
                fuCounter = (fragmentedBitMask >> BIT_SHIFT_FOR_FU_COUNTER) << 16;
            }
            PVH264PayLoadOutputMediaData out_data(aMediaData, iDon, (fuCounter | (prev_fuType << 8) | fuType));

            // push into the prority queue
            int32 err;
            OSCL_TRY(err, iOutputMediaDataQueue.push(out_data));
            if (err || iOutputMediaDataQueue.empty()) return false;

            // update internal variables
            updateWhenQueuingData(fuType, nal_type, fragmentedBitMask);
            return true;
        }

        // pop data from priority queue
        void dequeue(IPayloadParser::Payload& accessUnit)
        {
            PVH264PayLoadOutputMediaData out_data(iOutputMediaDataQueue.top());
            iOutputMediaDataQueue.pop();
            accessUnit = out_data.data;

            // update internal variables
            updateWhenDequeuingData(out_data.fuType);
        }

        // priority queue is empty
        bool isQueueEmpty()
        {
            return iOutputMediaDataQueue.empty();
        }

    private:
        // update variables when the prority queue is queuing data
        void updateWhenQueuingData(const uint32 fuType, const uint8 nal_type, const uint32 fragmentedBitMask)
        {
            if (fuType == 0 || fuType == 3)  // complete NAL or ending FU
            {
                iNALUnitsCounter++;
            }

            // update iPrevNALType, only for complete NALs and FU-B
            if (nal_type != H264_RTP_PAYLOAD_FU_A)
            {
                iPrevNALType = fragmentedBitMask & 0x07;
            }
            else   // FU-A
            {
                iPrevNALType >>= 2;
                iPrevNALType <<= 2;     // clear bit 1 and bit 0
                iPrevNALType |= fuType; // keep bit 2 unchanged for FU-A, for isCurrRTPPacketIgnored()
            }
        }

        // update variables when the prority queue is de-queuing data
        void updateWhenDequeuingData(const uint32 fuType)
        {
            uint32 prev_fuType = (fuType >> 8) & 0x03;
            uint32 curr_fuType = fuType & 0x03;

            if (curr_fuType == 0)  // complete NAL
            {
                iNALUnitsCounter--;
                if (prev_fuType == 1 || prev_fuType == 2)  // current one is complete NAL, but the previous one is fragmented NAL in case that the ending fragmented NAL is lost
                {
                    iNALUnitsCounter--;
                }
            }
            else if (curr_fuType == 3)  // ending fragemented NAL
            {
                iNALUnitsCounter--;
            }
        }


    public:
        // counter of complete NAL units, which could be smaller than the actual number of elements in iOutputMediaDataQueue
        uint32 iNALUnitsCounter;

        // hold the current DON, for FU-As following the first FU-B
        uint32 iDon;

        // hold the DONB, for MTAPs, or serves as the counter for FUs
        uint32 iDonBase;

        // for MTAPs only, save the timestamp offset
        uint32 iTimestampOffset;

        // for the NAL type of the previous media message sent out, should be equal to "iIsFragmentedBitMask&0x03" of class H264PayloadParser
        uint32 iPrevNALType;

        // save the rtp payload pointer to check if the current input rtp packet is the same as the previous one
        uint8 *iRtpPayloadPtr;

        // Priority Queue of output media data
        OsclPriorityQueue < PVH264PayLoadOutputMediaData,
        PVH264PayloadParserAllocator,
        Oscl_Vector<PVH264PayLoadOutputMediaData, PVH264PayloadParserAllocator>,
        PVH264PayLoadOutputMediaDataCompareLess > iOutputMediaDataQueue;

};

#endif // H264_PAYLOAD_PARSER_INTERLEAVE_MODE_H

