/*
 ** Copyright 2003-2010, VisualOn, Inc.
 **
 ** 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.
 */
/*******************************************************************************
	File:		bitenc.c

	Content:	Bitstream encoder functions

*******************************************************************************/

#include "bitenc.h"
#include "bit_cnt.h"
#include "dyn_bits.h"
#include "qc_data.h"
#include "interface.h"

#define UNUSED(x) (void)(x)

static const  Word16 globalGainOffset = 100;
static const  Word16 icsReservedBit   = 0;


/*****************************************************************************
*
* function name: encodeSpectralData
* description:  encode spectral data
* returns:      spectral bits used
*
*****************************************************************************/
static Word32 encodeSpectralData(Word16             *sfbOffset,
                                 SECTION_DATA       *sectionData,
                                 Word16             *quantSpectrum,
                                 HANDLE_BIT_BUF      hBitStream)
{
  Word16 i,sfb;
  Word16 dbgVal;
  SECTION_INFO* psectioninfo;
  dbgVal = GetBitsAvail(hBitStream);

  for(i=0; i<sectionData->noOfSections; i++) {
    psectioninfo = &(sectionData->sectionInfo[i]);
	/*
       huffencode spectral data for this section
    */
    for(sfb=psectioninfo->sfbStart;
        sfb<psectioninfo->sfbStart+psectioninfo->sfbCnt;
        sfb++) {
      codeValues(quantSpectrum+sfbOffset[sfb],
                 sfbOffset[sfb+1] - sfbOffset[sfb],
                 psectioninfo->codeBook,
                 hBitStream);
    }
  }

  return(GetBitsAvail(hBitStream)-dbgVal);
}

/*****************************************************************************
*
* function name:encodeGlobalGain
* description: encodes Global Gain (common scale factor)
* returns:     none
*
*****************************************************************************/
static void encodeGlobalGain(Word16 globalGain,
                             Word16 logNorm,
                             Word16 scalefac,
                             HANDLE_BIT_BUF hBitStream)
{
  WriteBits(hBitStream, ((globalGain - scalefac) + globalGainOffset-(logNorm << 2)), 8);
}


/*****************************************************************************
*
* function name:encodeIcsInfo
* description: encodes Ics Info
* returns:     none
*
*****************************************************************************/

static void encodeIcsInfo(Word16 blockType,
                          Word16 windowShape,
                          Word16 groupingMask,
                          SECTION_DATA *sectionData,
                          HANDLE_BIT_BUF  hBitStream)
{
  WriteBits(hBitStream,icsReservedBit,1);
  WriteBits(hBitStream,blockType,2);
  WriteBits(hBitStream,windowShape,1);


  switch(blockType){
    case LONG_WINDOW:
    case START_WINDOW:
    case STOP_WINDOW:
      WriteBits(hBitStream,sectionData->maxSfbPerGroup,6);

      /* No predictor data present */
      WriteBits(hBitStream, 0, 1);
      break;

    case SHORT_WINDOW:
      WriteBits(hBitStream,sectionData->maxSfbPerGroup,4);

      /*
      Write grouping bits
      */
      WriteBits(hBitStream,groupingMask,TRANS_FAC-1);
      break;
  }
}

/*****************************************************************************
*
* function name: encodeSectionData
* description:  encode section data (common Huffman codebooks for adjacent
*               SFB's)
* returns:      none
*
*****************************************************************************/
static Word32 encodeSectionData(SECTION_DATA *sectionData,
                                HANDLE_BIT_BUF hBitStream)
{
  Word16 sectEscapeVal=0,sectLenBits=0;
  Word16 sectLen;
  Word16 i;
  Word16 dbgVal=GetBitsAvail(hBitStream);



  switch(sectionData->blockType)
  {
    case LONG_WINDOW:
    case START_WINDOW:
    case STOP_WINDOW:
      sectEscapeVal = SECT_ESC_VAL_LONG;
      sectLenBits   = SECT_BITS_LONG;
      break;

    case SHORT_WINDOW:
      sectEscapeVal = SECT_ESC_VAL_SHORT;
      sectLenBits   = SECT_BITS_SHORT;
      break;
  }

  for(i=0;i<sectionData->noOfSections;i++) {
    WriteBits(hBitStream,sectionData->sectionInfo[i].codeBook,4);
    sectLen = sectionData->sectionInfo[i].sfbCnt;

    while(sectLen >= sectEscapeVal) {

      WriteBits(hBitStream,sectEscapeVal,sectLenBits);
      sectLen = sectLen - sectEscapeVal;
    }
    WriteBits(hBitStream,sectLen,sectLenBits);
  }
  return(GetBitsAvail(hBitStream)-dbgVal);
}

/*****************************************************************************
*
* function name: encodeScaleFactorData
* description:  encode DPCM coded scale factors
* returns:      none
*
*****************************************************************************/
static Word32 encodeScaleFactorData(UWord16        *maxValueInSfb,
                                    SECTION_DATA   *sectionData,
                                    Word16         *scalefac,
                                    HANDLE_BIT_BUF  hBitStream)
{
  Word16 i,j,lastValScf,deltaScf;
  Word16 dbgVal = GetBitsAvail(hBitStream);
  SECTION_INFO* psectioninfo;

  lastValScf=scalefac[sectionData->firstScf];

  for(i=0;i<sectionData->noOfSections;i++){
    psectioninfo = &(sectionData->sectionInfo[i]);
    if (psectioninfo->codeBook != CODE_BOOK_ZERO_NO){
      for (j=psectioninfo->sfbStart;
           j<psectioninfo->sfbStart+psectioninfo->sfbCnt; j++){

        if(maxValueInSfb[j] == 0) {
          deltaScf = 0;
        }
        else {
          deltaScf = lastValScf - scalefac[j];
          lastValScf = scalefac[j];
        }

        if(codeScalefactorDelta(deltaScf,hBitStream)){
          return(1);
        }
      }
    }

  }
  return(GetBitsAvail(hBitStream)-dbgVal);
}

/*****************************************************************************
*
* function name:encodeMsInfo
* description: encodes MS-Stereo Info
* returns:     none
*
*****************************************************************************/
static void encodeMSInfo(Word16          sfbCnt,
                         Word16          grpSfb,
                         Word16          maxSfb,
                         Word16          msDigest,
                         Word16         *jsFlags,
                         HANDLE_BIT_BUF  hBitStream)
{
  Word16 sfb, sfbOff;


  switch(msDigest)
  {
    case MS_NONE:
      WriteBits(hBitStream,SI_MS_MASK_NONE,2);
      break;

    case MS_ALL:
      WriteBits(hBitStream,SI_MS_MASK_ALL,2);
      break;

    case MS_SOME:
      WriteBits(hBitStream,SI_MS_MASK_SOME,2);
      for(sfbOff = 0; sfbOff < sfbCnt; sfbOff+=grpSfb) {
        for(sfb=0; sfb<maxSfb; sfb++) {

          if(jsFlags[sfbOff+sfb] & MS_ON) {
            WriteBits(hBitStream,1,1);
          }
          else{
            WriteBits(hBitStream,0,1);
          }
        }
      }
      break;
  }

}

/*****************************************************************************
*
* function name: encodeTnsData
* description:  encode TNS data (filter order, coeffs, ..)
* returns:      none
*
*****************************************************************************/
static void encodeTnsData(TNS_INFO tnsInfo,
                          Word16 blockType,
                          HANDLE_BIT_BUF hBitStream) {
  Word16 i,k;
  Flag tnsPresent;
  Word16 numOfWindows;
  Word16 coefBits;
  Flag isShort;


  if (blockType==2) {
    isShort = 1;
    numOfWindows = TRANS_FAC;
  }
  else {
    isShort = 0;
    numOfWindows = 1;
  }

  tnsPresent=0;
  for (i=0; i<numOfWindows; i++) {

    if (tnsInfo.tnsActive[i]) {
      tnsPresent=1;
    }
  }

  if (tnsPresent==0) {
    WriteBits(hBitStream,0,1);
  }
  else{ /* there is data to be written*/
    WriteBits(hBitStream,1,1); /*data_present */
    for (i=0; i<numOfWindows; i++) {

      WriteBits(hBitStream,tnsInfo.tnsActive[i],(isShort?1:2));

      if (tnsInfo.tnsActive[i]) {

        WriteBits(hBitStream,((tnsInfo.coefRes[i] - 4)==0?1:0),1);

        WriteBits(hBitStream,tnsInfo.length[i],(isShort?4:6));

        WriteBits(hBitStream,tnsInfo.order[i],(isShort?3:5));

        if (tnsInfo.order[i]){
          WriteBits(hBitStream, FILTER_DIRECTION, 1);

          if(tnsInfo.coefRes[i] == 4) {
            coefBits = 3;
            for(k=0; k<tnsInfo.order[i]; k++) {

              if (tnsInfo.coef[i*TNS_MAX_ORDER_SHORT+k] > 3 ||
                  tnsInfo.coef[i*TNS_MAX_ORDER_SHORT+k] < -4) {
                coefBits = 4;
                break;
              }
            }
          }
          else {
            coefBits = 2;
            for(k=0; k<tnsInfo.order[i]; k++) {

              if (tnsInfo.coef[i*TNS_MAX_ORDER_SHORT+k] > 1 ||
                  tnsInfo.coef[i*TNS_MAX_ORDER_SHORT+k] < -2) {
                coefBits = 3;
                break;
              }
            }
          }
          WriteBits(hBitStream, tnsInfo.coefRes[i] - coefBits, 1); /*coef_compres*/
          for (k=0; k<tnsInfo.order[i]; k++ ) {
            static const Word16 rmask[] = {0,1,3,7,15};

            WriteBits(hBitStream,tnsInfo.coef[i*TNS_MAX_ORDER_SHORT+k] & rmask[coefBits],coefBits);
          }
        }
      }
    }
  }

}

/*****************************************************************************
*
* function name: encodeGainControlData
* description:  unsupported
* returns:      none
*
*****************************************************************************/
static void encodeGainControlData(HANDLE_BIT_BUF hBitStream)
{
  WriteBits(hBitStream,0,1);
}

/*****************************************************************************
*
* function name: encodePulseData
* description:  not supported yet (dummy)
* returns:      none
*
*****************************************************************************/
static void encodePulseData(HANDLE_BIT_BUF hBitStream)
{
  WriteBits(hBitStream,0,1);
}


/*****************************************************************************
*
* function name: WriteIndividualChannelStream
* description:  management of write process of individual channel stream
* returns:      none
*
*****************************************************************************/
static void
writeIndividualChannelStream(Flag   commonWindow,
                             Word16 mdctScale,
                             Word16 windowShape,
                             Word16 groupingMask,
                             Word16 *sfbOffset,
                             Word16 scf[],
                             UWord16 *maxValueInSfb,
                             Word16 globalGain,
                             Word16 quantSpec[],
                             SECTION_DATA *sectionData,
                             HANDLE_BIT_BUF hBitStream,
                             TNS_INFO tnsInfo)
{
  Word16 logNorm;

  logNorm = LOG_NORM_PCM - (mdctScale + 1);

  encodeGlobalGain(globalGain, logNorm,scf[sectionData->firstScf], hBitStream);


  if(!commonWindow) {
    encodeIcsInfo(sectionData->blockType, windowShape, groupingMask, sectionData, hBitStream);
  }

  encodeSectionData(sectionData, hBitStream);

  encodeScaleFactorData(maxValueInSfb,
                        sectionData,
                        scf,
                        hBitStream);

  encodePulseData(hBitStream);

  encodeTnsData(tnsInfo, sectionData->blockType, hBitStream);

  encodeGainControlData(hBitStream);

  encodeSpectralData(sfbOffset,
                     sectionData,
                     quantSpec,
                     hBitStream);

}

/*****************************************************************************
*
* function name: writeSingleChannelElement
* description:  write single channel element to bitstream
* returns:      none
*
*****************************************************************************/
static Word16 writeSingleChannelElement(Word16 instanceTag,
                                        Word16 *sfbOffset,
                                        QC_OUT_CHANNEL* qcOutChannel,
                                        HANDLE_BIT_BUF hBitStream,
                                        TNS_INFO tnsInfo)
{
  WriteBits(hBitStream,ID_SCE,3);
  WriteBits(hBitStream,instanceTag,4);
  writeIndividualChannelStream(0,
                               qcOutChannel->mdctScale,
                               qcOutChannel->windowShape,
                               qcOutChannel->groupingMask,
                               sfbOffset,
                               qcOutChannel->scf,
                               qcOutChannel->maxValueInSfb,
                               qcOutChannel->globalGain,
                               qcOutChannel->quantSpec,
                               &(qcOutChannel->sectionData),
                               hBitStream,
                               tnsInfo
                               );
  return(0);
}



/*****************************************************************************
*
* function name: writeChannelPairElement
* description:
* returns:      none
*
*****************************************************************************/
static Word16 writeChannelPairElement(Word16 instanceTag,
                                      Word16 msDigest,
                                      Word16 msFlags[MAX_GROUPED_SFB],
                                      Word16 *sfbOffset[2],
                                      QC_OUT_CHANNEL qcOutChannel[2],
                                      HANDLE_BIT_BUF hBitStream,
                                      TNS_INFO tnsInfo[2])
{
  WriteBits(hBitStream,ID_CPE,3);
  WriteBits(hBitStream,instanceTag,4);
  WriteBits(hBitStream,1,1); /* common window */

  encodeIcsInfo(qcOutChannel[0].sectionData.blockType,
                qcOutChannel[0].windowShape,
                qcOutChannel[0].groupingMask,
                &(qcOutChannel[0].sectionData),
                hBitStream);

  encodeMSInfo(qcOutChannel[0].sectionData.sfbCnt,
               qcOutChannel[0].sectionData.sfbPerGroup,
               qcOutChannel[0].sectionData.maxSfbPerGroup,
               msDigest,
               msFlags,
               hBitStream);

  writeIndividualChannelStream(1,
                               qcOutChannel[0].mdctScale,
                               qcOutChannel[0].windowShape,
                               qcOutChannel[0].groupingMask,
                               sfbOffset[0],
                               qcOutChannel[0].scf,
                               qcOutChannel[0].maxValueInSfb,
                               qcOutChannel[0].globalGain,
                               qcOutChannel[0].quantSpec,
                               &(qcOutChannel[0].sectionData),
                               hBitStream,
                               tnsInfo[0]);

  writeIndividualChannelStream(1,
                               qcOutChannel[1].mdctScale,
                               qcOutChannel[1].windowShape,
                               qcOutChannel[1].groupingMask,
                               sfbOffset[1],
                               qcOutChannel[1].scf,
                               qcOutChannel[1].maxValueInSfb,
                               qcOutChannel[1].globalGain,
                               qcOutChannel[1].quantSpec,
                               &(qcOutChannel[1].sectionData),
                               hBitStream,
                               tnsInfo[1]);

  return(0);
}



/*****************************************************************************
*
* function name: writeFillElement
* description:  write fill elements to bitstream
* returns:      none
*
*****************************************************************************/
static void writeFillElement( const UWord8 *ancBytes,
                              Word16 totFillBits,
                              HANDLE_BIT_BUF hBitStream)
{
  Word16 i;
  Word16 cnt,esc_count;

  /*
    Write fill Element(s):
    amount of a fill element can be 7+X*8 Bits, X element of [0..270]
  */

  while(totFillBits >= (3+4)) {
    cnt = min(((totFillBits - (3+4)) >> 3), ((1<<4)-1));

    WriteBits(hBitStream,ID_FIL,3);
    WriteBits(hBitStream,cnt,4);

    totFillBits = totFillBits - (3+4);


    if (cnt == (1<<4)-1) {

      esc_count = min( ((totFillBits >> 3) - ((1<<4)-1)), (1<<8)-1);
      WriteBits(hBitStream,esc_count,8);
      totFillBits = (totFillBits - 8);
      cnt = cnt + (esc_count - 1);
    }

    for(i=0;i<cnt;i++) {

      if(ancBytes)
        WriteBits(hBitStream, *ancBytes++,8);
      else
        WriteBits(hBitStream,0,8);
      totFillBits = totFillBits - 8;
    }
  }
}

/*****************************************************************************
*
* function name: WriteBitStream
* description:  main function of write bitsteam process
* returns:      0 if success
*
*****************************************************************************/
Word16 WriteBitstream (HANDLE_BIT_BUF hBitStream,
                       ELEMENT_INFO elInfo,
                       QC_OUT *qcOut,
                       PSY_OUT *psyOut,
                       Word16 *globUsedBits,
                       const UWord8 *ancBytes,
					   Word16 sampindex
                       ) /* returns error code */
{
  Word16 bitMarkUp;
  Word16 elementUsedBits;
  Word16 frameBits=0;

  UNUSED(ancBytes);

  /*   struct bitbuffer bsWriteCopy; */
  bitMarkUp = GetBitsAvail(hBitStream);
  if(qcOut->qcElement.adtsUsed)  /*  write adts header*/
  {
	  WriteBits(hBitStream, 0xFFF, 12); /* 12 bit Syncword */
	  WriteBits(hBitStream, 1, 1); /* ID == 0 for MPEG4 AAC, 1 for MPEG2 AAC */
	  WriteBits(hBitStream, 0, 2); /* layer == 0 */
	  WriteBits(hBitStream, 1, 1); /* protection absent */
	  WriteBits(hBitStream, 1, 2); /* profile */
	  WriteBits(hBitStream, sampindex, 4); /* sampling rate */
	  WriteBits(hBitStream, 0, 1); /* private bit */
	  WriteBits(hBitStream, elInfo.nChannelsInEl, 3); /* ch. config (must be > 0) */
								   /* simply using numChannels only works for
									6 channels or less, else a channel
									configuration should be written */
	  WriteBits(hBitStream, 0, 1); /* original/copy */
	  WriteBits(hBitStream, 0, 1); /* home */

	  /* Variable ADTS header */
	  WriteBits(hBitStream, 0, 1); /* copyr. id. bit */
	  WriteBits(hBitStream, 0, 1); /* copyr. id. start */
	  WriteBits(hBitStream, *globUsedBits >> 3, 13);
	  WriteBits(hBitStream, 0x7FF, 11); /* buffer fullness (0x7FF for VBR) */
	  WriteBits(hBitStream, 0, 2); /* raw data blocks (0+1=1) */
  }

  *globUsedBits=0;

  {

    Word16 *sfbOffset[2];
    TNS_INFO tnsInfo[2];
    elementUsedBits = 0;

    switch (elInfo.elType) {

      case ID_SCE:      /* single channel */
        sfbOffset[0] = psyOut->psyOutChannel[elInfo.ChannelIndex[0]].sfbOffsets;
        tnsInfo[0] = psyOut->psyOutChannel[elInfo.ChannelIndex[0]].tnsInfo;

        writeSingleChannelElement(elInfo.instanceTag,
                                  sfbOffset[0],
                                  &qcOut->qcChannel[elInfo.ChannelIndex[0]],
                                  hBitStream,
                                  tnsInfo[0]);
        break;

      case ID_CPE:     /* channel pair */
        {
          Word16 msDigest;
          Word16 *msFlags = psyOut->psyOutElement.toolsInfo.msMask;
          msDigest = psyOut->psyOutElement.toolsInfo.msDigest;
          sfbOffset[0] =
            psyOut->psyOutChannel[elInfo.ChannelIndex[0]].sfbOffsets;
          sfbOffset[1] =
            psyOut->psyOutChannel[elInfo.ChannelIndex[1]].sfbOffsets;

          tnsInfo[0]=
            psyOut->psyOutChannel[elInfo.ChannelIndex[0]].tnsInfo;
          tnsInfo[1]=
            psyOut->psyOutChannel[elInfo.ChannelIndex[1]].tnsInfo;
          writeChannelPairElement(elInfo.instanceTag,
                                  msDigest,
                                  msFlags,
                                  sfbOffset,
                                  &qcOut->qcChannel[elInfo.ChannelIndex[0]],
                                  hBitStream,
                                  tnsInfo);
        }
        break;

      default:
        return(1);

      }   /* switch */

    elementUsedBits = elementUsedBits - bitMarkUp;
    bitMarkUp = GetBitsAvail(hBitStream);
    frameBits = frameBits + elementUsedBits + bitMarkUp;

  }

  writeFillElement(NULL,
                   qcOut->totFillBits,
                   hBitStream);

  WriteBits(hBitStream,ID_END,3);

  /* byte alignement */
  WriteBits(hBitStream,0, (8 - (hBitStream->cntBits & 7)) & 7);

  *globUsedBits = *globUsedBits- bitMarkUp;
  bitMarkUp = GetBitsAvail(hBitStream);
  *globUsedBits = *globUsedBits + bitMarkUp;
  frameBits = frameBits + *globUsedBits;


  if (frameBits !=  (qcOut->totStaticBitsUsed+qcOut->totDynBitsUsed + qcOut->totAncBitsUsed +
                     qcOut->totFillBits + qcOut->alignBits)) {
    return(-1);
  }
  return(0);
}
