/* ------------------------------------------------------------------
 * Copyright (C) 1998-2010 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.
 * -------------------------------------------------------------------
 */
/*********************************************************************************/
/*     -------------------------------------------------------------------       */
/*                         MPEG-4 SyncSampleAtom Class                           */
/*     -------------------------------------------------------------------       */
/*********************************************************************************/
/*
    This SyncSampleAtom Class provides a compact marking of the random access
    points within the stream.
*/


#define IMPLEMENT_SyncSampleAtom

#include "syncsampleatom.h"
#include "atomutils.h"
#include "atomdefs.h"

// Stream-in constructor
SyncSampleAtom::SyncSampleAtom(MP4_FF_FILE *fp, uint32 size, uint32 type)
        : FullAtom(fp, size, type)
{
    _psampleNumbers = NULL;

    if (_success)
    {

        _nextSampleNumber = 0;

        if (!AtomUtils::read32(fp, _entryCount))
        {
            _success = false;
        }

        uint32 dataSize = _size - (DEFAULT_FULL_ATOM_SIZE + 4);

        uint32 entrySize = (4);

        if ((_entryCount*entrySize) > dataSize)
        {
            _success = false;
        }

        if (_success)
        {

            PV_MP4_FF_ARRAY_NEW(NULL, uint32, (_entryCount), _psampleNumbers);

            uint32 sync;
            for (uint32 i = 0; i < _entryCount; i++)
            {
                if (!AtomUtils::read32(fp, sync))
                {
                    _success = false;
                    break;
                }
                _psampleNumbers[i] = (sync);
            }
        }

        if (!_success)
            _mp4ErrorCode = READ_SYNC_SAMPLE_ATOM_FAILED;
    }
    else
    {
        if (_mp4ErrorCode != ATOM_VERSION_NOT_SUPPORTED)
            _mp4ErrorCode = READ_SYNC_SAMPLE_ATOM_FAILED;
    }
}

// Destructor
SyncSampleAtom::~SyncSampleAtom()
{
    // Cleanup vector
    if (_psampleNumbers != NULL)
    {
        PV_MP4_ARRAY_DELETE(NULL, _psampleNumbers);
        _psampleNumbers = NULL;
    }
}

// Returns the sync sample number at vector location 'index'
MP4_ERROR_CODE
SyncSampleAtom::getSampleNumberAt(int32 index, uint32& aSampleNumber) const
{
    MP4_ERROR_CODE errCode = READ_FAILED;
    if (index < (int32)_entryCount)
    {
        aSampleNumber = _psampleNumbers[index] - 1;
        errCode  = EVERYTHING_FINE;
    }
    return errCode;
}

// Returns the first sync sample that occurs at or after 'sampleNum'.  This is used
// when seeking in the bitstream trying to find a random access sample.  This method
// returns the random access sample that is closest immediately following 'sampleNum'
MP4_ERROR_CODE
SyncSampleAtom::getSyncSampleFollowing(uint32 sampleNum, uint32& aSyncSampleFollowing) const
{
    uint32 sync = 0;
    int32 count = 0;

    // Incase the sampleNum is one of the sync sample the existing sync
    // is same as sampleNum In this way the sample return from getSyncSampleFollowing
    // and getSyncSampleBefore become same causing repos issue. This check
    // will advance the sync to next I frame incase it is available
    if (sync == sampleNum)
    {
        if (count < (int32)_entryCount)
        {
            sync = _psampleNumbers[count] - 1;
        }

        // No I frame ahead exists, return 0 will ensure that engine ignore reposition and continue playing from current position
        else
        {
            sync = 0;
        }
    }

    while (sync < sampleNum)
    {
        if (count < (int32)_entryCount)
        {
            sync = _psampleNumbers[count] - 1;
            count++;
        }
        else
        {
            return DEFAULT_ERROR;
        }
    }

    aSyncSampleFollowing = sync;
    return EVERYTHING_FINE;
}

// Returns the first sync sample that occurs at or after 'sampleNum'.  This is used
// when seeking in the bitstream trying to find a random access sample.  This method
// returns the random access sample that is closest immediately following 'sampleNum'
MP4_ERROR_CODE
SyncSampleAtom::getSyncSampleBefore(uint32 sampleNum, uint32& aSyncSampleBefore) const
{
    uint32 sync = 0;
    int32 count = 0;

    //fix resetPlayback at the end of bitstream
    //_asm {int 3};

    while ((sync < sampleNum) &&
            (count < (int32)_entryCount))
    {
        sync = _psampleNumbers[count] - 1;
        count ++;
    }

    //the nearest I frame before sampleNum
    if (sync > sampleNum)
    {
        if (count < 2)
        {
            // It will only happen if the 1st frame is not key frame, and the seek point is
            // earlier than the 1st key frame. For such case, just seek back to the 1st frame.
            sync = 0;
        }
        else
        {
            count = count - 2;
            sync = _psampleNumbers[count] - 1;
        }
    }
    aSyncSampleBefore = sync;
    return EVERYTHING_FINE;
}

bool
SyncSampleAtom::IsSyncSample(uint32 sampleNum) const
{
    for (int32 count = 0; count < (int32)_entryCount; count++)
    {
        if ((_psampleNumbers[count] - 1) == sampleNum)
        {
            return true;
        }
    }

    return false;
}
