/* ------------------------------------------------------------------
 * 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.
 * -------------------------------------------------------------------
 */
#include "atom.h"
#include "atomutils.h"
#include "oscl_file_io.h"
#include"avcconfigurationatom.h"
#include"a_atomdefs.h"

#ifndef OSCL_VECTOR_H_INCLUDED
#include "oscl_vector.h"
#endif
typedef Oscl_Vector<PVA_FF_AVCConfigurationAtom::PVA_FF_ParameterSet*, OsclMemAllocator> parameterSetVecType;



PVA_FF_AVCConfigurationAtom::PVA_FF_AVCConfigurationAtom(uint8 profile, uint8 profileComp, uint8 level):
        PVA_FF_Atom(AVC_CONFIGURATION_BOX)
{
    _configurationVersion = 1;
    _avcProfileIndication = 1;
    _avcLevelIndication = 1;
    _profileCompatibility = 1;
    _reserved_zero_5bits = 1;
    _lengthSize = 0;
    _numSequenceParameterSets = 0;
    _seqParameterSetLen = 0;
    _numPictureParameterSets = 0;
    _avcProfileIndication = profile;
    _profileCompatibility = profileComp;
    _avcLevelIndication = level;

    PV_MP4_FF_NEW(fp->auditCB, parameterSetVecType, (), _sequenceParameterSetVec);
    PV_MP4_FF_NEW(fp->auditCB, parameterSetVecType, (), _pictureParameterSetVec);
    recomputeSize();
}

void PVA_FF_AVCConfigurationAtom::setSample(void* samp, uint32 size)
{
    OSCL_UNUSED_ARG(size);
    if (_numSequenceParameterSets == 0 && _numPictureParameterSets == 0)
    {
        _sample = (uint8*)samp;

        if (_lengthSize == 0)
        {
            _lengthSize = NAL_UNIT_LENGTH;      // set length size of NAL units as 16 bits
        }
        if (_numSequenceParameterSets == 0)
        {
            _numSequenceParameterSets = *(uint8*)_sample;
            _sample += sizeof(uint8);
        }

        bool avcProfileIndicationErr = false;
        bool set_li = false;
        bool set_pi = false;
        bool set_pc = false;
        uint8 profile_comp = 0x00;

        for (int i = 0; i < _numSequenceParameterSets; i++)
        {

            oscl_memcpy(&_seqParameterSetLen, _sample, 2);
            _sample += sizeof(uint16);
            if (_seqParameterSetLen != 0)
            {
                if (_avcProfileIndication == 1)
                {
                    _avcProfileIndication = *(_sample + 1);
                    set_pi = true;
                }
                if (set_pi)
                {
                    if (_avcProfileIndication != *(_sample + 1))
                        avcProfileIndicationErr = true;
                }

                uint8 li = *(_sample + 3);
                uint8 pc = *(_sample + 2);

                if (_avcLevelIndication == 0xFF)
                {
                    _avcLevelIndication = li;
                    set_li = true;
                }
                if (set_li)
                {
                    if (li > _avcLevelIndication)
                        _avcLevelIndication = li;
                }

                if (_profileCompatibility == 0xFF)
                {
                    _avcLevelIndication = li;
                    set_pc = true;
                }
                if (set_pc)
                {
                    if (pc > profile_comp)
                        profile_comp = pc;
                }
                _totalSeqParameterSetLength += _seqParameterSetLen;

                _pSequenceParameterSet = _sample;

                PVA_FF_ParameterSet *paramSet = OSCL_NEW(PVA_FF_ParameterSet, (_seqParameterSetLen, _pSequenceParameterSet));


                _sample += _seqParameterSetLen;
                (*_sequenceParameterSetVec).push_back(paramSet);
            }
        }
        if (avcProfileIndicationErr)
            _avcProfileIndication = 1;

        if (_profileCompatibility == 0xFF)
        {
            if (((_avcProfileIndication == 0x66) ||
                    (_avcProfileIndication == 0x77) ||
                    (_avcProfileIndication == 0x88)) &&
                    (_avcLevelIndication == 0x11))
            {
                _profileCompatibility = 0x08;
            }

            if (_avcLevelIndication != 0x11)
            {
                if (_avcProfileIndication == 0x66)
                {
                    _profileCompatibility = 0x01;
                }
                else if (_avcProfileIndication == 0x77)
                {
                    _profileCompatibility = 0x02;
                }
                else if (_avcProfileIndication == 0x88)
                {
                    _profileCompatibility = 0x04;
                }
                else
                {
                    _profileCompatibility = profile_comp;
                }
            }
        }

        if (_numPictureParameterSets == 0)
        {
            _numPictureParameterSets = *(uint8*)_sample;
            _sample += sizeof(uint8);
        }
        for (int j = 0; j < _numPictureParameterSets; j++)
        {


            oscl_memcpy(&_picParameterSetLen, _sample, 2);
            _sample += sizeof(uint16);

            _totalPicutureParameterSetLength += _picParameterSetLen;

            _pPictureParameterSet = _sample;

            PVA_FF_ParameterSet *paramSet = OSCL_NEW(PVA_FF_ParameterSet, (_picParameterSetLen, _pPictureParameterSet));

            _sample += _picParameterSetLen;
            (*_pictureParameterSetVec).push_back(paramSet);
        }

    }
}
// Destructor
PVA_FF_AVCConfigurationAtom::~PVA_FF_AVCConfigurationAtom()
{

    if (_sequenceParameterSetVec != NULL)
    {
        for (uint32 i = 0; i < _sequenceParameterSetVec->size(); i++)
        {
            OSCL_DELETE((*_sequenceParameterSetVec)[i]);
        }
        PV_MP4_FF_TEMPLATED_DELETE(NULL, parameterSetVecType, Oscl_Vector, _sequenceParameterSetVec);
    }
    if (_pictureParameterSetVec != NULL)
    {
        for (uint32 i = 0; i < _pictureParameterSetVec->size(); i++)
        {
            OSCL_DELETE((*_pictureParameterSetVec)[i]);
        }
        PV_MP4_FF_TEMPLATED_DELETE(NULL, parameterSetVecType, Oscl_Vector, _pictureParameterSetVec);
    }

}

bool
PVA_FF_AVCConfigurationAtom::renderToFileStream(MP4_AUTHOR_FF_FILE_IO_WRAP *fp)
{

    int32 rendered = 0;
    uint16 length;
    uint8* paramSet;
    uint8  numSps = 0;

    if (!renderAtomBaseMembers(fp))
    {
        return false;
    }
    rendered += getDefaultSize();

    if (!PVA_FF_AtomUtils::render8(fp, _configurationVersion))
    {
        return false;
    }
    rendered += 1;

    if (!PVA_FF_AtomUtils::render8(fp, _avcProfileIndication))
    {
        return false;
    }
    rendered += 1;

    if (!PVA_FF_AtomUtils::render8(fp, _profileCompatibility))
    {
        return false;
    }
    rendered += 1;
    if (!PVA_FF_AtomUtils::render8(fp, _avcLevelIndication))
    {
        return false;
    }
    rendered += 1;


    if (!PVA_FF_AtomUtils::render8(fp, _lengthSize))
    {
        return false;
    }
    rendered += 1;
    numSps = _numSequenceParameterSets;
    numSps |= 0xE0;             // first 3 bits are reserved
    if (!PVA_FF_AtomUtils::render8(fp, numSps))
    {
        return false;
    }
    rendered += 1;
    for (int i = 0; i < _numSequenceParameterSets; i++)
    {

        PVA_FF_ParameterSet *pSet = (*_sequenceParameterSetVec)[i];

        length = pSet->getParameterSetLength();
        paramSet = pSet->getParameterSet();

        if (!PVA_FF_AtomUtils::render16(fp, length))
        {
            return false;
        }
        rendered += 2;

        if (!PVA_FF_AtomUtils::renderByteData(fp, length, paramSet))
        {
            return false;
        }
        rendered  += length;

    }

    if (!PVA_FF_AtomUtils::render8(fp, _numPictureParameterSets))
    {
        return false;
    }
    rendered += 1;
    if (_pictureParameterSetVec->size() < _numPictureParameterSets)
    {
        return false;
    }
    for (int j = 0; j < _numPictureParameterSets; j++)
    {

        PVA_FF_ParameterSet *pSet = (*_pictureParameterSetVec)[j];

        length = pSet->getParameterSetLength();
        paramSet = pSet->getParameterSet();

        if (!PVA_FF_AtomUtils::render16(fp, length))
        {
            return false;
        }
        rendered += 2;

        if (!PVA_FF_AtomUtils::renderByteData(fp, length, paramSet))
        {
            return false;
        }
        rendered  += length;

    }

    return true;
}

void
PVA_FF_AVCConfigurationAtom::recomputeSize()
{
    int32 size = getDefaultSize();


    size += 7; // wrote six one byte data

    if (_sequenceParameterSetVec->size() >= _numSequenceParameterSets)
    {
        for (int i = 0; i < _numSequenceParameterSets; i++)
        {
            PVA_FF_ParameterSet *pSet = (*_sequenceParameterSetVec)[i];

            uint16  length = pSet->getParameterSetLength();
            size += 2;
            size += length;
        }
    }
    if (_pictureParameterSetVec->size() >= _numPictureParameterSets)
    {
        for (int j = 0; j < _numPictureParameterSets; j++)
        {
            PVA_FF_ParameterSet *pSet = (*_pictureParameterSetVec)[j];

            uint16  length = pSet->getParameterSetLength();
            size += 2;
            size += length;
        }
    }

    _size = size;

    // Update size of parent
    if (_pparent != NULL)
    {
        _pparent->recomputeSize();
    }

}

uint32
PVA_FF_AVCConfigurationAtom::getSize()
{
    recomputeSize();
    return (_size);
}
