/* The copyright in this software is being made available under the BSD
 * License, included below. This software may be subject to other third party
 * and contributor rights, including patent rights, and no such rights are
 * granted under this license.
 *
 * Copyright (c) 2010-2011, ISO/IEC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the ISO/IEC nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */



#include "TEncSeqStructure.h"
#include "TEncFormattedStringParser.h"
#include <algorithm>

ErrVal FrameDescriptor::initFrameDescriptor( const std::string&  rcString, UInt &ruiIncrement, std::map<UInt, UInt>& rcColDirTracker)
{
  std::string::size_type uiPos = 0;

  while( 1 )
  {
    SliceType eSliceType;
    Bool bStoreForRef;
    Bool bIsIDR;
    UInt uiLayer;

    RNOK( TEncFormattedStringParser::extractFrameType( rcString, eSliceType, bStoreForRef, bIsIDR, uiPos ) );
    RNOK( TEncFormattedStringParser::extractFrameLayer( rcString, uiLayer, uiPos ) );

    m_aeSliceType.push_back( eSliceType );
    m_abStoreForRef.push_back( bStoreForRef );
    m_abIsIDR.push_back( bIsIDR );
    m_auiLayer.push_back( uiLayer );
    m_aaiAllowedRelativeRefPocsL0.push_back( std::vector<int>() );
    m_aaiAllowedRelativeRefPocsL1.push_back( std::vector<int>() );
    m_aaiAllowedReferenceViewIdxL0.push_back( std::vector<int>() );
    m_aaiAllowedReferenceViewIdxL1.push_back( std::vector<int>() );

    if( eSliceType == P_SLICE )
    {
      RNOK( TEncFormattedStringParser::extractAllowedRelativeRefPocs( rcString, m_aaiAllowedRelativeRefPocsL0.back(), m_aaiAllowedReferenceViewIdxL0.back(), uiPos ) );
    }
    else if( eSliceType == B_SLICE )
    {
      RNOK( TEncFormattedStringParser::extractAllowedRelativeRefPocs( rcString, m_aaiAllowedRelativeRefPocsL0.back(), m_aaiAllowedRelativeRefPocsL1.back(),
                                                                      m_aaiAllowedReferenceViewIdxL0.back(), m_aaiAllowedReferenceViewIdxL1.back(), uiPos ) );
    }

    // check that only available views are referenced
    {
      const Int iCurrViewIdx = Int( m_aeSliceType.size() - 1 );
      for( UInt ui = 0; ui < m_aaiAllowedReferenceViewIdxL0.back().size(); ui++ )
      {
        const Int iRefViewIdx = m_aaiAllowedReferenceViewIdxL0.back()[ui];
        ROF( iRefViewIdx == -1 || ( 0 <= iRefViewIdx && iRefViewIdx < iCurrViewIdx ) );
      }
      for( UInt ui = 0; ui < m_aaiAllowedReferenceViewIdxL1.back().size(); ui++ )
      {
        const Int iRefViewIdx = m_aaiAllowedReferenceViewIdxL1.back()[ui];
        ROF( iRefViewIdx == -1 || ( 0 <= iRefViewIdx && iRefViewIdx < iCurrViewIdx ) );
      }
    }

    ROT( eSliceType == I_SLICE && ! m_aaiAllowedRelativeRefPocsL0.back().empty() && ! m_aaiAllowedRelativeRefPocsL1.back().empty() );
    ROT( eSliceType == P_SLICE && ! m_aaiAllowedRelativeRefPocsL1.back().empty() );
    if( rcString[uiPos] == '_' )
    {
      uiPos++;
      break;
    }
  }

  RNOK( TEncFormattedStringParser::extractFrameIncrement( rcString, ruiIncrement, uiPos ) );

  if(rcColDirTracker.find( m_auiLayer[0] )==rcColDirTracker.end())
    rcColDirTracker.insert( std::make_pair(m_auiLayer[0],1) );

  m_uiColDir = rcColDirTracker[ m_auiLayer[0] ];
  rcColDirTracker[ m_auiLayer[0]  ] = 1 - rcColDirTracker[ m_auiLayer[0] ];

  ROF( std::string::npos == uiPos || rcString.length() == uiPos );

  return Err::m_nOK;
}


UInt64 TEncSeqStructure::FrameSequencePart::findIncrement( UInt64 uiIncrement, bool &rbSuccess ) const
{
  TOF( isInfinitelyLong() || uiIncrement < getSize() );
  rbSuccess = false;
  const UInt64 uiOffset = uiIncrement % xGetSize();
  for( UInt ui = 0; ui < UInt( m_auiIncrements.size() ); ui++ )
  {
    if( uiOffset == m_auiIncrements[ui] )
    {
      rbSuccess = true;
      return uiIncrement - uiOffset + ui;
    }
  }
  return 0;
}


TEncSeqStructure::FrameSequencePart::FrameSequencePart( const std::string& rcString )
{
  std::string cNoRepString;

  //----- get number of repetitions and number of frames -----
  UInt uiNumRep = 0;
  TNOK( TEncFormattedStringParser::extractRepetitions( rcString, cNoRepString, uiNumRep ) );
  xSetNumRep( uiNumRep );
  UInt uiNumberOfFrames = 0;
  TNOK( TEncFormattedStringParser::getNumberOfFrames( cNoRepString, uiNumberOfFrames ) );
  TOF( uiNumberOfFrames );
  //----- create array -----
  m_acFrameDescriptors.resize( uiNumberOfFrames );
  TOF( m_acFrameDescriptors.size() == uiNumberOfFrames );
  m_auiIncrements.resize( uiNumberOfFrames );

  //----- initialize array -----
  UInt uiIndex;
  size_t uiPos;
  for( uiPos = 0, uiIndex = 0; uiIndex < uiNumberOfFrames; uiIndex++ )
  {
    std::string cFDString;
    TNOK( TEncFormattedStringParser::extractNextFrameDescription( cNoRepString, cFDString, uiPos ) );
    UInt uiIncrement = 0;
    TNOK( m_acFrameDescriptors[uiIndex].initFrameDescriptor( cFDString, uiIncrement, m_cColDirTracker ) );
    m_auiIncrements[uiIndex] = uiIncrement;
  }
  TNOK( xCheck() );
}


ErrVal TEncSeqStructure::FrameSequencePart::xCheck()
{
  ROTS( m_acFrameDescriptors.empty() );

  std::vector<bool> abCovered( m_acFrameDescriptors.size(), false );
  for( UInt uiIndex = 0; uiIndex < m_acFrameDescriptors.size(); uiIndex++ )
  {
    const UInt uiInc = m_auiIncrements[uiIndex];
    ROT( uiInc >= m_acFrameDescriptors.size() ); // error in config file parameter GOPFormatString
    ROT( abCovered[ uiInc ] );
    abCovered[uiInc] = true;
  }

  return Err::m_nOK;
}


TEncSeqStructure::GeneralSequencePart::GeneralSequencePart( const std::string& rcString )
{
  std::string cNoRepString;

  UInt uiNumRep = 0;
  TNOK( TEncFormattedStringParser::extractRepetitions( rcString, cNoRepString, uiNumRep ) );
  xSetNumRep( uiNumRep );
  UInt uiNumberOfParts = 0;
  TNOK( TEncFormattedStringParser::getNumberOfParts( cNoRepString, uiNumberOfParts ) );
  TOF( uiNumberOfParts );

  //----- create array -----
  m_apcSequenceParts.resize( uiNumberOfParts );

  //----- initialize array -----
  UInt uiIndex;
  size_t uiPos;
  for( uiPos = 0, uiIndex = 0; uiIndex < uiNumberOfParts; uiIndex++ )
  {
    std::string cPartString;

    TNOK( TEncFormattedStringParser::extractPart( cNoRepString, cPartString, uiPos ) );

    if( TEncFormattedStringParser::isFrameSequencePart( cPartString ) )
    {
      m_apcSequenceParts[uiIndex] = new FrameSequencePart( cPartString );
    }
    else
    {
      m_apcSequenceParts[uiIndex] = new GeneralSequencePart( cPartString );
    }
  }
}


TEncSeqStructure::TEncSeqStructure()
: m_pcSequencePart( NULL )
{
}

TEncSeqStructure::~TEncSeqStructure()
{
  delete m_pcSequencePart; m_pcSequencePart = NULL ;
}

ErrVal TEncSeqStructure::init( const std::string& rcString )
{
  std::string cStringWithoutWhitespace;
  for( UInt ui = 0; ui < rcString.length(); ui++ )
  {
    if( rcString[ui] != ' ' && rcString[ui] != '\t' )
    {
      cStringWithoutWhitespace += rcString[ui];
    }
  }

  if( cStringWithoutWhitespace.find( "*n{" ) == std::string::npos )
  {
    cStringWithoutWhitespace = "*n{" + cStringWithoutWhitespace + "}";
  }
  m_pcSequencePart = new GeneralSequencePart( cStringWithoutWhitespace );

  return Err::m_nOK;
}


UInt TEncSeqStructure::getMaxAbsPocDiff( const UInt uiNumberOfFrames )
{
  TEncSeqStructure::Iterator cSeqIter = TEncSeqStructure::Iterator( *this, PicOrderCnt( 0 ), 0 );

  UInt uiMaxAbsPocDiff = 0;
  Int64 iLastPoc = cSeqIter.getPoc();
  int iNumFramesLeft = uiNumberOfFrames;

  for( int iIndex = 0; 0 != iNumFramesLeft; ++cSeqIter, iIndex++ )
  {
    Int64 iPoc = cSeqIter.getPoc();
    if( iPoc >= uiNumberOfFrames )
    {
      // skiped due to frame behind end of sequence
      continue;
    }
    iNumFramesLeft--;
    //----- check POC differences -----
    UInt uiAbsFrameDiffRef = (UInt) gAbs( iPoc - iLastPoc );
    if( uiMaxAbsPocDiff < uiAbsFrameDiffRef  )
    {
      uiMaxAbsPocDiff = uiAbsFrameDiffRef;
    }
    iLastPoc = iPoc;
  }

  return uiMaxAbsPocDiff;
}

TEncSeqStructure::Iterator::Iterator( const TEncSeqStructure &r, PicOrderCnt cLayerChangeStartPoc, int iLayerOffset )
: m_cBasePoc( 0 )
, m_cLayerChangeStartPoc( cLayerChangeStartPoc )
, m_iLayerOffset( iLayerOffset )
{
  m_acSeqPartPath.resize( 1 );
  xGetCurr().m_pcSeqPart = r.m_pcSequencePart;
  xGetCurr().m_uiCurrPos = 0;
  xGoToLeaf( false );
}

void TEncSeqStructure::Iterator::xGoToLeaf( bool bGoToRightmostLeaf )
{
  while( !xGetCurr().m_pcSeqPart->isLeafNode() )
  {
    SequencePartWithPos cPartWithPos;
    cPartWithPos.m_pcSeqPart = xGetCurr().m_pcSeqPart->getChildNode( xGetCurr().m_uiCurrPos );
    cPartWithPos.m_uiCurrPos = 0;
    if( bGoToRightmostLeaf )
    {
      TOT( cPartWithPos.m_pcSeqPart->isInfinitelyLong() );
      cPartWithPos.m_uiCurrPos = cPartWithPos.m_pcSeqPart->getSize() - 1;
    }
    m_acSeqPartPath.push_back( cPartWithPos );
  }
}


TEncSeqStructure::Iterator& TEncSeqStructure::Iterator::operator++()
{
  ++xGetCurr().m_uiCurrPos;
  if( !xGetCurr().m_pcSeqPart->isInfinitelyLong() && xGetCurr().m_uiCurrPos == xGetCurr().m_pcSeqPart->getSize() )
  {
    xGoToNextFrameSequencePart();
    xGetCurr().m_uiCurrPos = 0;
  }
  return *this;
}


TEncSeqStructure::Iterator& TEncSeqStructure::Iterator::traverseByPocDiff( Int64 iPocDiff )
{
  const PicOrderCnt cTargetPoc = getPoc() + iPocDiff;
  const Int64 iIncrement = Int64( xGetCurr().m_pcSeqPart->getIncrement( xGetCurr().m_uiCurrPos ) );
  if( iIncrement + iPocDiff < 0 )
  {
    xGoToPreviousFrameSequencePart();
    const Int64 iNewIncrement = Int64( xGetCurr().m_pcSeqPart->getIncrement( xGetCurr().m_uiCurrPos ) );
    const Int64 iNewSize = Int64( xGetCurr().m_pcSeqPart->getSize() );
    traverseByPocDiff( iPocDiff + iIncrement + iNewSize - iNewIncrement );
    TOF( cTargetPoc == getPoc() );
    return *this;
  }

  if( !xGetCurr().m_pcSeqPart->isInfinitelyLong() )
  {
    const Int64 iSize = Int64( xGetCurr().m_pcSeqPart->getSize() );
    if( iIncrement + iPocDiff >= iSize )
    {
      xGoToNextFrameSequencePart();
      traverseByPocDiff( iPocDiff - ( iSize - iIncrement ) );
      TOF( cTargetPoc == getPoc() );
      return *this;
    }
  }

  UInt64 uiPocDiffToFind = UInt64( iIncrement + iPocDiff );
  bool bFound = false;
  xGetCurr().m_uiCurrPos = xGetCurr().m_pcSeqPart->findIncrement( uiPocDiffToFind, bFound );
  TOF( bFound );
  TOF( cTargetPoc == getPoc() );
  return *this;
}


TEncSeqStructure::Iterator TEncSeqStructure::Iterator::getIterByPocDiff( Int64 iPocDiff ) const
{
  Iterator cCopy = *this;
  cCopy.traverseByPocDiff( iPocDiff );
  return cCopy;
}


void TEncSeqStructure::Iterator::xGoToPreviousFrameSequencePart()
{
  TOF( xGetCurr().m_pcSeqPart->isLeafNode() );
  TOF( m_acSeqPartPath.size() > 1 );
  m_acSeqPartPath.pop_back();
  while( xGetCurr().m_uiCurrPos == 0 )
  {
    TOF( m_acSeqPartPath.size() > 1 );
    m_acSeqPartPath.pop_back();
  }
  xGetCurr().m_uiCurrPos--;
  xGoToLeaf( true );
  m_cBasePoc -= xGetCurr().m_pcSeqPart->getSize();
}


void TEncSeqStructure::Iterator::xGoToNextFrameSequencePart()
{
  TOF( xGetCurr().m_pcSeqPart->isLeafNode() );
  TOT( xGetCurr().m_pcSeqPart->isInfinitelyLong() );
  m_cBasePoc += xGetCurr().m_pcSeqPart->getSize();
  m_acSeqPartPath.pop_back();
  while( !xGetCurr().m_pcSeqPart->isInfinitelyLong() && xGetCurr().m_uiCurrPos == xGetCurr().m_pcSeqPart->getSize() - 1 )
  {
    TOF( m_acSeqPartPath.size() > 1 );
    m_acSeqPartPath.pop_back();
  }
  xGetCurr().m_uiCurrPos++;
  xGoToLeaf( false );
}



