/*
 ** 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:		qc_main.c

	Content:	Quantizing & coding functions

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

#include "basic_op.h"
#include "oper_32b.h"
#include "qc_main.h"
#include "quantize.h"
#include "interface.h"
#include "adj_thr.h"
#include "sf_estim.h"
#include "stat_bits.h"
#include "bit_cnt.h"
#include "dyn_bits.h"
#include "channel_map.h"
#include "memalign.h"

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

typedef enum{
  FRAME_LEN_BYTES_MODULO =  1,
  FRAME_LEN_BYTES_INT    =  2
}FRAME_LEN_RESULT_MODE;

static const Word16 maxFillElemBits = 7 + 270*8;

/* forward declarations */

static Word16 calcMaxValueInSfb(Word16 sfbCnt,
                                Word16 maxSfbPerGroup,
                                Word16 sfbPerGroup,
                                Word16 sfbOffset[MAX_GROUPED_SFB],
                                Word16 quantSpectrum[FRAME_LEN_LONG],
                                UWord16 maxValue[MAX_GROUPED_SFB]);


/*****************************************************************************
*
* function name: calcFrameLen
* description: estimate the frame length according the bitrates
*
*****************************************************************************/
static Word16 calcFrameLen(Word32 bitRate,
                           Word32 sampleRate,
                           FRAME_LEN_RESULT_MODE mode)
{

  Word32 result;
  Word32 quot;

  result = (FRAME_LEN_LONG >> 3) * bitRate;
  quot = result / sampleRate;


  if (mode == FRAME_LEN_BYTES_MODULO) {
    result -= quot * sampleRate;
  }
  else { /* FRAME_LEN_BYTES_INT */
    result = quot;
  }

  return result;
}

/*****************************************************************************
*
*  function name:framePadding
*  description: Calculates if padding is needed for actual frame
*  returns: paddingOn or not
*
*****************************************************************************/
static Word16 framePadding(Word32 bitRate,
                           Word32 sampleRate,
                           Word32 *paddingRest)
{
  Word16 paddingOn;
  Word16 difference;

  paddingOn = 0;

  difference = calcFrameLen( bitRate,
                             sampleRate,
                             FRAME_LEN_BYTES_MODULO );
  *paddingRest = *paddingRest - difference;


  if (*paddingRest <= 0 ) {
    paddingOn = 1;
    *paddingRest = *paddingRest + sampleRate;
  }

  return paddingOn;
}


/*********************************************************************************
*
* function name: QCOutNew
* description: init qcout parameter
* returns:     0 if success
*
**********************************************************************************/

Word16 QCOutNew(QC_OUT *hQC, Word16 nChannels, VO_MEM_OPERATOR *pMemOP)
{
  Word32 i;
  Word16 *quantSpec;
  Word16 *scf;
  UWord16 *maxValueInSfb;

  quantSpec = (Word16 *)mem_malloc(pMemOP, nChannels * FRAME_LEN_LONG * sizeof(Word16), 32, VO_INDEX_ENC_AAC);
  if(NULL == quantSpec)
	  return 1;
  scf = (Word16 *)mem_malloc(pMemOP, nChannels * MAX_GROUPED_SFB * sizeof(Word16), 32, VO_INDEX_ENC_AAC);
  if(NULL == scf)
  {
	  return 1;
  }
  maxValueInSfb = (UWord16 *)mem_malloc(pMemOP, nChannels * MAX_GROUPED_SFB * sizeof(UWord16), 32, VO_INDEX_ENC_AAC);
  if(NULL == maxValueInSfb)
  {
	  return 1;
  }

  for (i=0; i<nChannels; i++) {
    hQC->qcChannel[i].quantSpec = quantSpec + i*FRAME_LEN_LONG;

    hQC->qcChannel[i].maxValueInSfb = maxValueInSfb + i*MAX_GROUPED_SFB;

    hQC->qcChannel[i].scf = scf + i*MAX_GROUPED_SFB;
  }

  return 0;
}


/*********************************************************************************
*
* function name: QCOutDelete
* description: unint qcout parameter
* returns:      0 if success
*
**********************************************************************************/
void QCOutDelete(QC_OUT* hQC, VO_MEM_OPERATOR *pMemOP)
{
   Word32 i;
   if(hQC)
   {
      if(hQC->qcChannel[0].quantSpec)
		 mem_free(pMemOP, hQC->qcChannel[0].quantSpec, VO_INDEX_ENC_AAC);

      if(hQC->qcChannel[0].maxValueInSfb)
		  mem_free(pMemOP, hQC->qcChannel[0].maxValueInSfb, VO_INDEX_ENC_AAC);

	  if(hQC->qcChannel[0].scf)
		  mem_free(pMemOP, hQC->qcChannel[0].scf, VO_INDEX_ENC_AAC);

	  for (i=0; i<MAX_CHANNELS; i++) {
		  hQC->qcChannel[i].quantSpec = NULL;

		  hQC->qcChannel[i].maxValueInSfb = NULL;

		  hQC->qcChannel[i].scf = NULL;
	  }
   }
}

/*********************************************************************************
*
* function name: QCNew
* description: set QC to zero
* returns:     0 if success
*
**********************************************************************************/
Word16 QCNew(QC_STATE *hQC, VO_MEM_OPERATOR *pMemOP)
{
  pMemOP->Set(VO_INDEX_ENC_AAC, hQC,0,sizeof(QC_STATE));

  return (0);
}

/*********************************************************************************
*
* function name: QCDelete
* description: unint qcout parameter
*
**********************************************************************************/
void QCDelete(QC_STATE *hQC, VO_MEM_OPERATOR *pMemOP)
{
  UNUSED(hQC);
  UNUSED(pMemOP);
}

/*********************************************************************************
*
* function name: QCInit
* description: init QD parameter
* returns:     0 if success
*
**********************************************************************************/
Word16 QCInit(QC_STATE *hQC,
              struct QC_INIT *init)
{
  hQC->nChannels       = init->elInfo->nChannelsInEl;
  hQC->maxBitsTot      = init->maxBits;
  hQC->bitResTot       = sub(init->bitRes, init->averageBits);
  hQC->averageBitsTot  = init->averageBits;
  hQC->maxBitFac       = init->maxBitFac;

  hQC->padding.paddingRest = init->padding.paddingRest;

  hQC->globStatBits    = 3;                          /* for ID_END */

  /* channel elements init */
  InitElementBits(&hQC->elementBits,
                  *init->elInfo,
                  init->bitrate,
                  init->averageBits,
                  hQC->globStatBits);

  /* threshold parameter init */
  AdjThrInit(&hQC->adjThr,
             init->meanPe,
             hQC->elementBits.chBitrate);

  return 0;
}


/*********************************************************************************
*
* function name: QCMain
* description:  quantization and coding the spectrum
* returns:      0 if success
*
**********************************************************************************/
Word16 QCMain(QC_STATE* hQC,
              ELEMENT_BITS* elBits,
              ATS_ELEMENT* adjThrStateElement,
              PSY_OUT_CHANNEL  psyOutChannel[MAX_CHANNELS],  /* may be modified in-place */
              PSY_OUT_ELEMENT* psyOutElement,
              QC_OUT_CHANNEL  qcOutChannel[MAX_CHANNELS],    /* out                      */
              QC_OUT_ELEMENT* qcOutElement,
              Word16 nChannels,
			  Word16 ancillaryDataBytes)
{
  Word16 maxChDynBits[MAX_CHANNELS];
  Word16 chBitDistribution[MAX_CHANNELS];
  Word32 ch;

  if (elBits->bitResLevel < 0) {
    return -1;
  }

  if (elBits->bitResLevel > elBits->maxBitResBits) {
    return -1;
  }

  qcOutElement->staticBitsUsed = countStaticBitdemand(psyOutChannel,
                                                      psyOutElement,
                                                      nChannels,
													  qcOutElement->adtsUsed);


  if (ancillaryDataBytes) {
    qcOutElement->ancBitsUsed = 7 + (ancillaryDataBytes << 3);

    if (ancillaryDataBytes >= 15)
      qcOutElement->ancBitsUsed = qcOutElement->ancBitsUsed + 8;
  }
  else {
    qcOutElement->ancBitsUsed = 0;
  }

  CalcFormFactor(hQC->logSfbFormFactor, hQC->sfbNRelevantLines, hQC->logSfbEnergy, psyOutChannel, nChannels);

  /*adjust thresholds for the desired bitrate */
  AdjustThresholds(&hQC->adjThr,
                   adjThrStateElement,
                   psyOutChannel,
                   psyOutElement,
                   chBitDistribution,
                   hQC->logSfbEnergy,
                   hQC->sfbNRelevantLines,
                   qcOutElement,
				   elBits,
				   nChannels,
				   hQC->maxBitFac);

  /*estimate scale factors */
  EstimateScaleFactors(psyOutChannel,
                       qcOutChannel,
                       hQC->logSfbEnergy,
                       hQC->logSfbFormFactor,
                       hQC->sfbNRelevantLines,
                       nChannels);

  /* condition to prevent empty bitreservoir */
  for (ch = 0; ch < nChannels; ch++) {
    Word32 maxDynBits;
    maxDynBits = elBits->averageBits + elBits->bitResLevel - 7; /* -7 bec. of align bits */
    maxDynBits = maxDynBits - qcOutElement->staticBitsUsed + qcOutElement->ancBitsUsed;
    maxChDynBits[ch] = extract_l(chBitDistribution[ch] * maxDynBits / 1000);
  }

  qcOutElement->dynBitsUsed = 0;
  for (ch = 0; ch < nChannels; ch++) {
    Word32 chDynBits;
    Flag   constraintsFulfilled;
    Word32 iter;
    iter = 0;
    do {
      constraintsFulfilled = 1;

      QuantizeSpectrum(psyOutChannel[ch].sfbCnt,
                       psyOutChannel[ch].maxSfbPerGroup,
                       psyOutChannel[ch].sfbPerGroup,
                       psyOutChannel[ch].sfbOffsets,
                       psyOutChannel[ch].mdctSpectrum,
                       qcOutChannel[ch].globalGain,
                       qcOutChannel[ch].scf,
                       qcOutChannel[ch].quantSpec);

      if (calcMaxValueInSfb(psyOutChannel[ch].sfbCnt,
                            psyOutChannel[ch].maxSfbPerGroup,
                            psyOutChannel[ch].sfbPerGroup,
                            psyOutChannel[ch].sfbOffsets,
                            qcOutChannel[ch].quantSpec,
                            qcOutChannel[ch].maxValueInSfb) > MAX_QUANT) {
        constraintsFulfilled = 0;
      }

      chDynBits = dynBitCount(qcOutChannel[ch].quantSpec,
                              qcOutChannel[ch].maxValueInSfb,
                              qcOutChannel[ch].scf,
                              psyOutChannel[ch].windowSequence,
                              psyOutChannel[ch].sfbCnt,
                              psyOutChannel[ch].maxSfbPerGroup,
                              psyOutChannel[ch].sfbPerGroup,
                              psyOutChannel[ch].sfbOffsets,
                              &qcOutChannel[ch].sectionData);

      if (chDynBits >= maxChDynBits[ch]) {
        constraintsFulfilled = 0;
      }

      if (!constraintsFulfilled) {
        qcOutChannel[ch].globalGain = qcOutChannel[ch].globalGain + 1;
      }

      iter = iter + 1;

    } while(!constraintsFulfilled);

    qcOutElement->dynBitsUsed = qcOutElement->dynBitsUsed + chDynBits;

    qcOutChannel[ch].mdctScale    = psyOutChannel[ch].mdctScale;
    qcOutChannel[ch].groupingMask = psyOutChannel[ch].groupingMask;
    qcOutChannel[ch].windowShape  = psyOutChannel[ch].windowShape;
  }

  /* save dynBitsUsed for correction of bits2pe relation */
  AdjThrUpdate(adjThrStateElement, qcOutElement->dynBitsUsed);

  {
    Word16 bitResSpace = elBits->maxBitResBits - elBits->bitResLevel;
    Word16 deltaBitRes = elBits->averageBits -
                        (qcOutElement->staticBitsUsed +
                         qcOutElement->dynBitsUsed + qcOutElement->ancBitsUsed);

    qcOutElement->fillBits = max(0, (deltaBitRes - bitResSpace));
  }

  return 0; /* OK */
}


/*********************************************************************************
*
* function name: calcMaxValueInSfb
* description:  search the max Spectrum in one sfb
*
**********************************************************************************/
static Word16 calcMaxValueInSfb(Word16 sfbCnt,
                                Word16 maxSfbPerGroup,
                                Word16 sfbPerGroup,
                                Word16 sfbOffset[MAX_GROUPED_SFB],
                                Word16 quantSpectrum[FRAME_LEN_LONG],
                                UWord16 maxValue[MAX_GROUPED_SFB])
{
  Word16 sfbOffs, sfb;
  Word16 maxValueAll;

  maxValueAll = 0;

  for(sfbOffs=0;sfbOffs<sfbCnt;sfbOffs+=sfbPerGroup) {
    for (sfb = 0; sfb < maxSfbPerGroup; sfb++) {
      Word16 line;
      Word16 maxThisSfb;
      maxThisSfb = 0;

      for (line = sfbOffset[sfbOffs+sfb]; line < sfbOffset[sfbOffs+sfb+1]; line++) {
        Word16 absVal;
        absVal = abs_s(quantSpectrum[line]);
        maxThisSfb = max(maxThisSfb, absVal);
      }

      maxValue[sfbOffs+sfb] = maxThisSfb;
      maxValueAll = max(maxValueAll, maxThisSfb);
    }
  }
  return maxValueAll;
}


/*********************************************************************************
*
* function name: updateBitres
* description: update bitreservoir
*
**********************************************************************************/
void updateBitres(QC_STATE* qcKernel,
                  QC_OUT*   qcOut)

{
  ELEMENT_BITS *elBits;

  qcKernel->bitResTot = 0;

  elBits = &qcKernel->elementBits;


  if (elBits->averageBits > 0) {
    /* constant bitrate */
    Word16 bitsUsed;
    bitsUsed = (qcOut->qcElement.staticBitsUsed + qcOut->qcElement.dynBitsUsed) +
                   (qcOut->qcElement.ancBitsUsed + qcOut->qcElement.fillBits);
    elBits->bitResLevel = elBits->bitResLevel + (elBits->averageBits - bitsUsed);
    qcKernel->bitResTot = qcKernel->bitResTot + elBits->bitResLevel;
  }
  else {
    /* variable bitrate */
    elBits->bitResLevel = elBits->maxBits;
    qcKernel->bitResTot = qcKernel->maxBitsTot;
  }
}

/*********************************************************************************
*
* function name: FinalizeBitConsumption
* description: count bits used
*
**********************************************************************************/
Word16 FinalizeBitConsumption(QC_STATE *qcKernel,
                              QC_OUT* qcOut)
{
  Word32 nFullFillElem;
  Word32 totFillBits;
  Word16 diffBits;
  Word16 bitsUsed;

  totFillBits = 0;

  qcOut->totStaticBitsUsed = qcKernel->globStatBits;
  qcOut->totStaticBitsUsed += qcOut->qcElement.staticBitsUsed;
  qcOut->totDynBitsUsed    = qcOut->qcElement.dynBitsUsed;
  qcOut->totAncBitsUsed    = qcOut->qcElement.ancBitsUsed;
  qcOut->totFillBits       = qcOut->qcElement.fillBits;

  if (qcOut->qcElement.fillBits) {
    totFillBits += qcOut->qcElement.fillBits;
  }

  nFullFillElem = (max((qcOut->totFillBits - 1), 0) / maxFillElemBits) * maxFillElemBits;

  qcOut->totFillBits = qcOut->totFillBits - nFullFillElem;

  /* check fill elements */

  if (qcOut->totFillBits > 0) {
    /* minimum Fillelement contains 7 (TAG + byte cnt) bits */
    qcOut->totFillBits = max(7, qcOut->totFillBits);
    /* fill element size equals n*8 + 7 */
    qcOut->totFillBits = qcOut->totFillBits + ((8 - ((qcOut->totFillBits - 7) & 0x0007)) & 0x0007);
  }

  qcOut->totFillBits = qcOut->totFillBits + nFullFillElem;

  /* now distribute extra fillbits and alignbits over channel elements */
  qcOut->alignBits = 7 - ((qcOut->totDynBitsUsed + qcOut->totStaticBitsUsed +
                           qcOut->totAncBitsUsed + qcOut->totFillBits - 1) & 0x0007);


  if ( (qcOut->alignBits + qcOut->totFillBits - totFillBits == 8) &&
       (qcOut->totFillBits > 8))
    qcOut->totFillBits = qcOut->totFillBits - 8;


  diffBits = qcOut->alignBits + qcOut->totFillBits - totFillBits;

  if(diffBits>=0) {
    qcOut->qcElement.fillBits += diffBits;
  }

  bitsUsed = qcOut->totDynBitsUsed + qcOut->totStaticBitsUsed + qcOut->totAncBitsUsed;
  bitsUsed = bitsUsed + qcOut->totFillBits + qcOut->alignBits;

  if (bitsUsed > qcKernel->maxBitsTot) {
    return -1;
  }
  return bitsUsed;
}


/*********************************************************************************
*
* function name: AdjustBitrate
* description:  adjusts framelength via padding on a frame to frame basis,
*               to achieve a bitrate that demands a non byte aligned
*               framelength
* return:       errorcode
*
**********************************************************************************/
Word16 AdjustBitrate(QC_STATE        *hQC,
                     Word32           bitRate,    /* total bitrate */
                     Word32           sampleRate) /* output sampling rate */
{
  Word16 paddingOn;
  Word16 frameLen;
  Word16 codeBits;
  Word16 codeBitsLast;

  /* Do we need a extra padding byte? */
  paddingOn = framePadding(bitRate,
                           sampleRate,
                           &hQC->padding.paddingRest);

  /* frame length */
  frameLen = paddingOn + calcFrameLen(bitRate,
                                      sampleRate,
                                      FRAME_LEN_BYTES_INT);

  frameLen = frameLen << 3;
  codeBitsLast = hQC->averageBitsTot - hQC->globStatBits;
  codeBits     = frameLen - hQC->globStatBits;

  /* calculate bits for every channel element */
  if (codeBits != codeBitsLast) {
    Word16 totalBits = 0;

    hQC->elementBits.averageBits = (hQC->elementBits.relativeBits * codeBits) >> 16; /* relativeBits was scaled down by 2 */
    totalBits += hQC->elementBits.averageBits;

    hQC->elementBits.averageBits = hQC->elementBits.averageBits + (codeBits - totalBits);
  }

  hQC->averageBitsTot = frameLen;

  return 0;
}
