/*****************************************************************************
 *
 * This software module was originally developed by
 *
 *   Isabelle Corset (Philips / ACTS-MoMuSyS)
 *
 * and edited by
 * 
 *   Paulo Nunes (IST / ACTS-MoMuSyS)
 *   Cor Quist (KPN / ACTS-MoMuSyS)
 *   Jan De Lameillieure (HHI / ACTS-MoMuSyS)
 *   Michael Wollborn (TUH / ACTS-MoMuSyS)
 *   Robert Danielsen (Telenor / ACTS-MoMuSyS)
 *   Bob Eifrig (NextLevel Systems)
 *   Michael Frater (UNSW)
 *
 * in the course of development of the MPEG-4 Video (ISO/IEC 14496-2) standard.
 * This software module is an implementation of a part of one or more MPEG-4
 * Video (ISO/IEC 14496-2) tools as specified by the MPEG-4 Video (ISO/IEC
 * 14496-2) standard.
 *
 * ISO/IEC gives users of the MPEG-4 Video (ISO/IEC 14496-2) standard free
 * license to this software module or modifications thereof for use in hardware
 * or software products claiming conformance to the MPEG-4 Video (ISO/IEC
 * 14496-2) standard.
 *
 * Those intending to use this software module in hardware or software products
 * are advised that its use may infringe existing patents. The original
 * developer of this software module and his/her company, the subsequent
 * editors and their companies, and ISO/IEC have no liability for use of this
 * software module or modifications thereof in an implementation. Copyright is
 * not released for non MPEG-4 Video (ISO/IEC 14496-2) Standard conforming
 * products.
 *
 * ACTS-MoMuSys partners retain full right to use the code for his/her own
 * purpose, assign or donate the code to a third party and to inhibit third
 * parties from using the code for non MPEG-4 Video (ISO/IEC 14496-2) Standard
 * conforming products. This copyright notice must be included in all copies or
 * derivative works.
 *
 * Copyright (c) 1996
 *
 *****************************************************************************/

/***********************************************************HeaderBegin*******
 *
 * File:        text_decode_mb.c
 *
 * Author:      Isabelle Corset (LEP) <corset@lep-philips.fr>
 * Created:     5-Mar-96
 *
 * Description: Decoding of the Macroblock Layer
 *
 * Notes:
 *
 * Modified:    9-May-96 Paulo Nunes: Reformatted. New headers.
 *             23-May-96 Cor Quist : Added the INTRA DC prediction
 *             13-Jun-96 Cor Quist : Bug fixes for combined mode
 *             31-Jul-96 Jan De Lameillieure (HHI) : added MPEG-1/-2 
 *			 like inverse quantisation
 *	       04-OCT-96 M.Wollborn: Removed reading of NO_DCT_FLAG from the
 *				     bitstream for I-VOPs (GetMBText)
 *	23.10.96 Robert Danielsen: Added DC/AC prediction. Not tested.
 *			Two new functions: doDCACrecon(), nullfill().
 *	22-APR-97 Michael Wollborn: changed name of BlockDequant to 
 *			BlockDequantH263 and changed
 *			stuff for MPEG-like quantization
 *	23-APR-97 Michael Wollborn: Removed function "GetMBMotText()"
 *				    since it is no longer used
 *      04.08.97 Minhua Zhou: added intra_dc_vlc_thr (switched)
 *      04.01.98 Michael Frater: marker bit for DC coefficients > 8 bits
 *
 ***********************************************************HeaderEnd*********/

/************************    INCLUDE FILES    ********************************/

#include "text_decode_mb.h"
#include "text_quant.h"
#include "zigzag.h"		/* Added 14-NOV-1996, MW */

                          

/***********************************************************CommentBegin******
 *
 * -- MBzero -- Fills one macrocblock with zeros
 *
 * Author :
 *      Karl.Lillevold@nta.no
 *
 * Created :
 *      29-Aug-94
 *
 * Purpose :
 *      To fill one macrocblock with zeros.
 *
 * Arguments in :
 *
 *
 * Arguments in/out :
 *      Macroblock     *mblock : macroblock (cleared)
 *
 * Arguments out :
 *
 *
 * Return values :
 *
 *
 * Side effects :
 *
 *
 * Description :
 *
 *
 * See also :
 *
 *
 * Modified :
 *      28-Nov-95 Robert Inge Danielsen - C++-ifying the code. Is now a
 *                                        member function.
 *
 *
 ***********************************************************CommentEnd********/

Void
MBzero (Macroblock * mblock)

{
  Int                 n;
  Int                 m;

  for (n = 0; n < MB_SIZE; n++)
    for (m = 0; m < MB_SIZE; m++)
      mblock->lum[n][m] = 0;
  for (n = 0; n < (MB_SIZE >> 1); n++)
    for (m = 0; m < (MB_SIZE >> 1); m++)
      {
        mblock->Cr[n][m] = 0;
        mblock->Cb[n][m] = 0;
      }

}                               /* MBzero */

/***********************************************************CommentBegin******
 *
 * -- Bzero -- Fills one block with zeros
 *
 * Author :
 *      Isabelle Corset (LEP) <corset@lep-philips.fr>
 *
 * Created :
 *      5-Mar-96
 *
 * Purpose :
 *      To fill one block with zeros.
 *
 * Arguments in :
 *
 *
 * Arguments in/out :
 *      Int *block,              block (already allocated).
 *
 *
 * Arguments out :
 *
 *
 * Return values :
 *
 *
 * Side effects :
 *
 *
 * Description :
 *
 *
 * See also :
 *
 *
 * Modified :
 *
 *
 ***********************************************************CommentEnd********/

Void
Bzero (Int * block)

{
  Int                 n;

  for (n = 0; n < B_SIZE * B_SIZE; n++)
    block[n] = 0;

}                               /* Bzero */



/***********************************************************CommentBegin******
 *
 * -- PutBlock -- Copy a block into a macroblock
 *
 * Author :
 *      Isabelle Corset (LEP) <corset@lep-philips.fr>
 *
 * Created :
 *      27-Feb-96
 *
 * Purpose :
 *      To copy the blocks of texture component into a macroblock structure.
 *
 *
 * Arguments in :
 *      Int comp,               the component number.
 *      Int * block,            the block data.
 *
 *
 * Arguments in/out :
 *      Macroblock *mblock,     the filled macroblock structure.
 *
 *
 * Arguments out :
 *
 *
 * Return values :
 *
 *
 * Side effects :
 *
 *
 * Description :
 *
 *
 * See also :
 *
 *
 * Modified :
 *
 *
 ***********************************************************CommentEnd********/

Void
PutBlock (Int comp,
          Int * block,
          Macroblock * mblock)

{
  Int                 m, n;
  Int                 begin_l, begin_c;

  if (comp < 4)
    {
      begin_l = (comp / 2) * B_SIZE;
      begin_c = (comp % 2) * B_SIZE;

      for (n = 0; n < B_SIZE; n++)
        for (m = 0; m < B_SIZE; m++)
          mblock->lum[n + begin_l][m + begin_c] = *(block + n * B_SIZE + m);
    }
  else
    {
      if (comp == 4)
        {
          for (n = 0; n < B_SIZE; n++)
            for (m = 0; m < B_SIZE; m++)
              mblock->Cr[n][m] = *(block + n * B_SIZE + m);
        }
      else
        {
          for (n = 0; n < B_SIZE; n++)
            for (m = 0; m < B_SIZE; m++)
              mblock->Cb[n][m] = *(block + n * B_SIZE + m);
        }
    }

}                               /* PutBlock */




/***********************************************************CommentBegin******
 *
 * -- UInt DecodePredictedIntraDC --
 *
 * Author : Cor Quist, KPN Research - The Netherlands
 *
 *
 * Created : 23-May-1996
 *
 *
 * Purpose : To decode the predicted intra dc coefficient for intra
 *           macroblocks
 *
 *
 * Arguments in : Int mbnum : macroblock number
 *                Int compnum : block number (0..5)
 *                Bitstream *stream : a pointer to the bitstream
 *                Trace *trace : trace info
 *                Int x_width : horizontal dimension of the VOP
 *                UInt *new_vop : indicates if this MB is the first MB for
 *                                this VOP
 *                Int trace_mode : mode info. for trace
 *
 * Arguments in/out :
 *
 *
 * Arguments out :
 *
 *
 * Return values : UInt DC_coeff : the dc coefficient for one block
 *
 *
 * Side effects :
 *
 *
 * Description :
 *
 *
 * See also : This algorithm is based upon VM2.0.
 *            Not compatible with VM2.1
 *
 *
 * Modified :
 *
 *
 ***********************************************************CommentEnd********/
/* This should be "Int" since prediction can be negative!!! MW 15-NOV-1996 */
Int DecodePredictedIntraDC(Int compnum,
			    Bitstream *stream,
			    Trace *trace,
			    Int trace_mode)
{
  UInt DC_size;
  UInt code;
  Int INTRADC_delta;
  Int error_flag=0;
  Int first_bit;
  
  /* read DC size 2 - 8 bits */

  DC_size = VlcDecIntraDCPredSize (stream, &error_flag, compnum);
  
  if ( error_flag == RETURN)
    {
      fprintf(stderr, "Error decoding INTRADC pred. size\n");
      return -1;
    }
  
  if (DC_size == 0)
    {
      INTRADC_delta = 0;
    }
  else
    {
      /* read delta DC 0 - 8 bits */

      code = BitstreamReadBits (stream, DC_size, "DC coeff", trace,
                                trace_mode);
     
      first_bit = code >> (DC_size-1);

      if (first_bit == 0 )
        { /* negative delta INTRA DC */
          INTRADC_delta  = -1 * (code ^( (Int)pow(2.0,(Double)DC_size)-1) );
        }
      else
        { /* positive delta INTRA DC */
          INTRADC_delta = code;
        }
      if (DC_size > 8)
	(Void) BitstreamReadBits (stream, 1, "Marker bit", trace,
				  trace_mode);
    }

  return INTRADC_delta;
}

/***********************************************************CommentBegin******
 *
 * -- UInt VlcDecIntraDCPredSize --
 *
 * Author : Cor Quist, KPN Research - The Netherlands.
 *
 *
 * Created : 23-May-1996
 *
 *
 * Purpose : VLC decoding of the SIZE field off the predicted INTRA DC
 *           coefficient.
 *
 *
 * Arguments in : Bitstream *stream : a pointer to the bitstream
 *                Int *error_flag : indicates the occurence of an error
 *                Int compnum : the block number (0..5)
 *
 * Arguments in/out :
 *
 *
 * Arguments out :
 *
 *
 * Return values : UInt DC_size : The number of bits of the INTRA DC
 *                                difference
 *
 *
 * Side effects :
 *
 *
 * Description :
 *
 *
 * See also : This algorithm is based upon VM2.0.
 *            Not compatible with VM2.1
 *
 *
 * Modified :
 *
 *
 ***********************************************************CommentEnd********/

UInt
VlcDecIntraDCPredSize (Bitstream *stream, Int *error_flag, Int compnum)

{
  UInt  code;
  UInt  DC_size=0;

   if( compnum >=0 && compnum < 4 ) /* luminance block */
    {
      code = BitstreamShowBits (stream, 11);
      if ( code == 1)
        {
          DC_size = 12;
          BitstreamFlushBits(stream, 11);
          return DC_size;
        }
      
      code = BitstreamShowBits (stream, 10);
      if ( code == 1)
        {
          DC_size = 11;
          BitstreamFlushBits(stream, 10);
          return DC_size;
        }

       code = BitstreamShowBits (stream, 9);
      if ( code == 1)
        {
          DC_size = 10;
          BitstreamFlushBits(stream, 9);
          return DC_size;
        }

    code = BitstreamShowBits (stream, 8);
      if ( code == 1)
        {
          DC_size = 9;
          BitstreamFlushBits(stream, 8);
          return DC_size;
        }

     code = BitstreamShowBits (stream, 7);
      if ( code == 1)
        {
          DC_size = 8;
          BitstreamFlushBits(stream, 7);
          return DC_size;
        }
    code = BitstreamShowBits (stream, 6);
      if ( code == 1)
        {
          DC_size = 7;
          BitstreamFlushBits(stream, 6);
          return DC_size;
        }
    code = BitstreamShowBits (stream, 5);
      if ( code == 1)
        {
          DC_size = 6;
          BitstreamFlushBits(stream, 5);
          return DC_size;
        }
    code = BitstreamShowBits (stream, 4);
      if ( code == 1)
        {
          DC_size = 5;
          BitstreamFlushBits(stream, 4);
          return DC_size;
        }
     code = BitstreamShowBits (stream, 3);
      if ( code == 1)
        {
          DC_size = 4;
          BitstreamFlushBits(stream, 3);
          return DC_size;
        } else if (code == 2) {
          DC_size = 3;
          BitstreamFlushBits(stream, 3);
          return DC_size;
        } else if (code ==3) {
          DC_size = 0;
          BitstreamFlushBits(stream, 3);
          return DC_size;
        }
      code = BitstreamShowBits (stream, 2);
      if ( code == 2)
        {
          DC_size = 2;
          BitstreamFlushBits(stream, 2);
          return DC_size;
        } else if (code == 3) {
          DC_size = 1;
          BitstreamFlushBits(stream, 2);
          return DC_size;
        }     

    }
   else /* chrominance block */
    {
     code = BitstreamShowBits (stream, 12);
      if ( code == 1)
        {
          DC_size = 12;
          BitstreamFlushBits(stream, 12);
          return DC_size;
        }
      
      code = BitstreamShowBits (stream, 11);
      if ( code == 1)
        {
          DC_size = 11;
          BitstreamFlushBits(stream, 11);
          return DC_size;
        }

       code = BitstreamShowBits (stream, 10);
      if ( code == 1)
        {
          DC_size = 10;
          BitstreamFlushBits(stream, 10);
          return DC_size;
        }

    code = BitstreamShowBits (stream, 9);
      if ( code == 1)
        {
          DC_size = 9;
          BitstreamFlushBits(stream, 9);
          return DC_size;
        }

     code = BitstreamShowBits (stream, 8);
      if ( code == 1)
        {
          DC_size = 8;
          BitstreamFlushBits(stream, 8);
          return DC_size;
        }
    code = BitstreamShowBits (stream, 7);
      if ( code == 1)
        {
          DC_size = 7;
          BitstreamFlushBits(stream, 7);
          return DC_size;
        }
    code = BitstreamShowBits (stream, 6);
      if ( code == 1)
        {
          DC_size = 6;
          BitstreamFlushBits(stream, 6);
          return DC_size;
        }
    code = BitstreamShowBits (stream, 5);
      if ( code == 1)
        {
          DC_size = 5;
          BitstreamFlushBits(stream, 5);
          return DC_size;
        }
     code = BitstreamShowBits (stream, 4);
      if ( code == 1)
        {
          DC_size = 4;
          BitstreamFlushBits(stream, 4);
          return DC_size;
        } 
      code = BitstreamShowBits (stream, 3);
      if ( code == 1)
        {
          DC_size = 3;
          BitstreamFlushBits(stream, 3);
          return DC_size;
        } 
      code = BitstreamShowBits (stream, 2);
        {
          DC_size = 3-code;
          BitstreamFlushBits(stream, 2);
          return DC_size;
        } 
  }

 return DC_size;

}     /* VlcDecIntraDCPredSize */

/***********************************************************CommentBegin******
 *
 * -- doDCACrecon -- Does DC/AC reconstruction. Changes qcoeff values as
 *		     appropriate.
 *
 * Author :		
 *	Robert Danielsen, Telenor <Robert.Danielsen@nta.no>
 *
 * Created :		
 *	23.10.96
 *
 * Purpose :		
 *	Does DC/AC reconstruction. Changes qcoeff values as appropriate. 
 * 
 * Arguments in : 	
 *	Int ncoeffs
 *	Int x_pos
 *	Int y_pos
 *	Int DC_store[][6][15]  	Stores coefficient values per MB for
 *			       	prediction (for one Vop)
 *	SInt *QP_store		Stores QP values for MBs (for one Vop)
 *	Int QP			QP value for this MB
 *	Int MB_width
 *
 * Arguments in/out :	
 *	Int *q_block		Coefficients
 *
 * Arguments out :	
 *	
 *
 * Return values :	
 *
 * Side effects :	
 *	Modifies q_block if needed for the reconstruction.
 *
 * Description :	
 *	
 *
 * See also :
 *	
 *
 * Modified :		
 *	28.01.97 Robert Danielsen: Changed zigzag-scanning.
 *  04.11.97 Minhua Zhou     : updated DC/AC prediction
 *  12.12.97 Bob Eifrig      : interlaced video alternate scan
 *
 ***********************************************************CommentEnd********/

Void doDCACrecon(Int *q_block, Int ncoeffs, Int ***DC_store,
		 SInt *QP_store, Int QP, Int x_pos, Int y_pos, Int MB_width,
		 Int ACpred_flag,
         Int AlternateScanFlag,
         Int comp, Int mid_grey)
{
    Int m, n;
    Int block_A, block_B, block_C;
    Int Xpos[6] = {-1, 0, -1, 0, -1, -1};
    Int Ypos[6] = {-1, -1, 0, 0, -1, -1};
    Int Xtab[6] = {1, 0, 3, 2, 4, 5};
    Int Ytab[6] = {2, 3, 0, 1, 4, 5};
    Int Ztab[6] = {3, 2, 1, 0, 4, 5};
    Int grad_hor, grad_ver, DC_pred;
    Int direction;		/* 0: HORIZONTAL, 1: VERTICAL */
    Int pred_A[15], pred_C[15];
    Int pcoeff[64];

    /* Find the direction of prediction and the DC prediction */
    if ((x_pos == 0) && y_pos == 0) {	/* top left corner */
	block_A = (comp == 1 || comp == 3) ? DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][0] : mid_grey*8;
	block_B = (comp == 3) ? DC_store[(y_pos+Ypos[comp])*MB_width+(x_pos+Xpos[comp])][Ztab[comp]][0] : mid_grey*8;
	block_C = (comp == 2 || comp == 3) ? DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][0] : mid_grey*8;
    }
    else if (x_pos == 0) {	/* left edge */
	block_A = (comp == 1 || comp == 3) ? DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][0] : mid_grey*8;
	block_B = (comp == 1 || comp == 3) ? DC_store[(y_pos+Ypos[comp])*MB_width+(x_pos+Xpos[comp])][Ztab[comp]][0] : mid_grey*8;
	block_C = DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][0];
    }
    else if (y_pos == 0) { /* top row */
	block_A = DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][0];
	block_B = (comp == 2 || comp == 3) ? DC_store[(y_pos+Ypos[comp])*MB_width+(x_pos+Xpos[comp])][Ztab[comp]][0] : mid_grey*8;
	block_C = (comp == 2 || comp == 3) ? DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][0] : mid_grey*8;
    }
    else {
	block_A = DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][0];
	block_B = (DC_store[(y_pos+Ypos[comp])*MB_width+(x_pos+Xpos[comp])]
		   [Ztab[comp]][0]);
	block_C = DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][0];
    }
    grad_hor = block_B - block_C;
    grad_ver = block_A - block_B;

    if ((ABS(grad_ver)) < (ABS(grad_hor))) {
	DC_pred = block_C;
	direction = 1;
    }
    else {
	DC_pred = block_A;
	direction = 0;
    }

    if (AlternateScanFlag)
        for (m = 0; m < 64; m++)
            pcoeff[m] = q_block[zigzag_v[m]];
    else
    /* Do inverse zigzag-scanning */
    if (ACpred_flag == 1)
	if (direction == 0)
	    for (m = 0; m < 64; m++)
		pcoeff[m] = q_block[zigzag_v[m]];
	else
	    for (m = 0; m < 64; m++)
		pcoeff[m] = q_block[zigzag_h[m]];
    else
	for (m = 0; m < 64; m++)
	    pcoeff[m] = q_block[zigzag[m]];
	
    /* Now reconstruct the DC coefficient */
    q_block[0] = pcoeff[0] + (DC_pred+cal_dc_scaler(QP,(comp<4)?1:2)/2)/cal_dc_scaler(QP,(comp<4)?1:2);

    
    /* Do AC prediction if required */
    if (ACpred_flag == 1)
    {
	/* Find AC predictions */
	if ((x_pos == 0) && y_pos == 0) {	/* top left corner */
	    if (comp == 1 || comp == 3)
		for (m = 0; m < 15; m++) 
		    pred_A[m] = Idir_d(((DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][m]) * (QP_store[y_pos*MB_width+(x_pos+Xpos[comp])])*2) , 2*QP);
	    else
		nullfill(pred_A,mid_grey);
	    if (comp == 2 || comp == 3)
		for (m = 0; m < 15; m++) 
		    pred_C[m] = Idir_d(((DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][m]) * (QP_store[(y_pos+Ypos[comp])*MB_width+x_pos])*2) , 2*QP);
	    else
		nullfill(pred_C,mid_grey);
	}
	else if (x_pos == 0) {	/* left edge */
	    if (comp == 1 || comp == 3)
		for (m = 0; m < 15; m++) 
		    pred_A[m] = Idir_d(((DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][m]) * (QP_store[y_pos*MB_width+(x_pos+Xpos[comp])])*2) , 2*QP);
	    else
		nullfill(pred_A,mid_grey);
	    for (m = 0; m < 15; m++) 
		pred_C[m] = Idir_d(((DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][m]) * (QP_store[(y_pos+Ypos[comp])*MB_width+x_pos])*2) , 2*QP);
	}
	else if (y_pos == 0) { /* top row */
	    for (m = 0; m < 15; m++) 
		pred_A[m] = Idir_d(((DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][m]) * (QP_store[y_pos*MB_width+(x_pos+Xpos[comp])])*2) , 2*QP);
	    if (comp == 2 || comp == 3)
		for (m = 0; m < 15; m++) 
		    pred_C[m] = Idir_d(((DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][m]) * (QP_store[(y_pos+Ypos[comp])*MB_width+x_pos])*2) , 2*QP);
	    else
		nullfill(pred_C,mid_grey);
	}
	else {
	    for (m = 0; m < 15; m++) {
		pred_A[m] = Idir_d(((DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][m]) * (QP_store[y_pos*MB_width+(x_pos+Xpos[comp])])*2) , 2*QP);
		pred_C[m] = Idir_d(((DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][m]) * (QP_store[(y_pos+Ypos[comp])*MB_width+x_pos])*2) , 2*QP);
	    }
	}

	/* Now reconstruct the AC coefficients */
	if (direction == 0) { /* Horizontal, left COLUMN of block A */
	    for (m = 0; m < 7; m++) {
		q_block[(m+1)*8]= pcoeff[(m+1)*8] + pred_A[m+8];
	    }
	    for (m = 1; m < 8; m++) {
		q_block[m] = pcoeff[m];
	    }
	}
	else { /* Vertical, top ROW of block C */
	    for (m = 0; m < 7; m++) {
		q_block[(m+1)*8]= pcoeff[(m+1)*8];
	    }
	    for (m = 1; m < 8; m++) {
		q_block[m] = pcoeff[m] + pred_C[m];
	    }
	}

	/* Copy rest of coefficients */
	for (m = 1; m < 8; m++)
	    for (n = 1; n < 8; n++)
		q_block[m*8+n] = pcoeff[m*8+n];
    }
    else
	/* Copy all AC coefficients directly */
	for (m = 1; m < 64; m++)
	    q_block[m] = pcoeff[m];
}

/***********************************************************CommentBegin******
 *
 * -- doDCACreconErrRes -- Does DC/AC reconstruction. Changes qcoeff values as
 *		           appropriate.
 *
 * Author :		
 *	Luis Ducla Soares (IST) - lds@lx.it.pt
 *
 * Created :		
 *	23.10.96
 *
 * Purpose :		
 *	Does DC/AC reconstruction. Changes qcoeff values as appropriate. 
 * 
 * Arguments in : 	
 *	Int ncoeffs
 *	Int x_pos
 *	Int y_pos
 *	Int DC_store[][6][15]  	Stores coefficient values per MB for
 *			       	prediction (for one Vop)
 *	SInt *QP_store		Stores QP values for MBs (for one Vop)
 *	Int QP			QP value for this MB
 *	Int MB_width
 *
 * Arguments in/out :	
 *	Int *q_block		Coefficients
 *
 * Arguments out :	
 *	
 *
 * Return values :	
 *
 * Side effects :	
 *	Modifies q_block if needed for the reconstruction.
 *
 * Description :	
 *	
 *
 * See also :
 *	
 *
 * Modified :		
 *	28.01.97 Robert Danielsen: Changed zigzag-scanning.
 *
 ***********************************************************CommentEnd********/

Void doDCACreconErrRes(Int *q_block, Int ncoeffs, Int ***DC_store,
		       SInt *QP_store, Int QP, Int x_pos, Int y_pos, Int MB_width,
		       Int ACpred_flag, Int comp, Int **slice_nb, Int mid_grey)
{
    Int m, n;
    Int block_A, block_B, block_C;
    Int Xpos[6] = {-1, 0, -1, 0, -1, -1};
    Int Ypos[6] = {-1, -1, 0, 0, -1, -1};
    Int Xtab[6] = {1, 0, 3, 2, 4, 5};
    Int Ytab[6] = {2, 3, 0, 1, 4, 5};
    Int Ztab[6] = {3, 2, 1, 0, 4, 5};
    Int grad_hor, grad_ver, DC_pred;
    Int direction;		/* 0: HORIZONTAL, 1: VERTICAL */
    Int pred_A[15], pred_C[15];
    Int pcoeff[64];

    /* Find the direction of prediction and the DC prediction */
    if ((x_pos == 0) && y_pos == 0) {	/* top left corner */
	block_A = (comp == 1 || comp == 3) ? DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][0] : mid_grey*8;
	block_B = (comp == 3) ? DC_store[(y_pos+Ypos[comp])*MB_width+(x_pos+Xpos[comp])][Ztab[comp]][0] : mid_grey*8;
	block_C = (comp == 2 || comp == 3) ? DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][0] : mid_grey*8;
    }
    else if (x_pos == 0) {	/* left edge */
	block_A = (comp == 1 || comp == 3) ? DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][0] : mid_grey*8;
	block_B = ((comp == 1 && slice_nb[x_pos][y_pos] == slice_nb[x_pos][y_pos-1]) || comp == 3) ? 
	  DC_store[(y_pos+Ypos[comp])*MB_width+(x_pos+Xpos[comp])][Ztab[comp]][0] : mid_grey*8;
	block_C = (comp == 2 || comp == 3 || ((comp == 0 || comp == 1 || comp == 4 || comp == 5) &&
					      slice_nb[x_pos][y_pos] == slice_nb[x_pos][y_pos-1])) ?
	  DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][0] : mid_grey*8;
    }
    else if (y_pos == 0) { /* top row */
	block_A = (comp == 1 || comp == 3 || ((comp == 0 || comp == 2 || comp == 4 || comp == 5) &&
					      slice_nb[x_pos][y_pos] == slice_nb[x_pos-1][y_pos])) ?
	  DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][0] : mid_grey*8;
	block_B = ((comp == 2 && slice_nb[x_pos][y_pos] == slice_nb[x_pos-1][y_pos]) || comp == 3) ? 
	  DC_store[(y_pos+Ypos[comp])*MB_width+(x_pos+Xpos[comp])][Ztab[comp]][0] : mid_grey*8;
	block_C = (comp == 2 || comp == 3) ? DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][0] : mid_grey*8;
    }
    else {
	block_A = (comp == 1 || comp == 3 || ((comp == 0 || comp == 2 || comp == 4 || comp == 5 ) &&
					      slice_nb[x_pos][y_pos] == slice_nb[x_pos-1][y_pos])) ?
	  DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][0] : mid_grey*8;
	block_B = (((comp == 0 || comp == 4 || comp == 5) && slice_nb[x_pos][y_pos] == slice_nb[x_pos-1][y_pos-1]) ||
		   (comp == 1 && slice_nb[x_pos][y_pos] == slice_nb[x_pos][y_pos-1]) || 
                   (comp == 2 && slice_nb[x_pos][y_pos] == slice_nb[x_pos-1][y_pos]) || (comp == 3)) ? 
         	   (DC_store[(y_pos+Ypos[comp])*MB_width+(x_pos+Xpos[comp])][Ztab[comp]][0]) : mid_grey*8;
	block_C = (comp == 2 || comp == 3 || ((comp == 0 || comp == 1 || comp == 4 || comp == 5) &&
					      slice_nb[x_pos][y_pos] == slice_nb[x_pos][y_pos-1])) ?
	  DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][0] : mid_grey*8;
    }
    grad_hor = block_B - block_C;
    grad_ver = block_A - block_B;

    if ((ABS(grad_ver)) < (ABS(grad_hor))) {
	DC_pred = block_C;
	direction = 1;
    }
    else {
	DC_pred = block_A;
	direction = 0;
    }

    /* Do inverse zigzag-scanning */
    if (ACpred_flag == 1)
	if (direction == 0)
	    for (m = 0; m < 64; m++)
		pcoeff[m] = q_block[zigzag_v[m]];
	else
	    for (m = 0; m < 64; m++)
		pcoeff[m] = q_block[zigzag_h[m]];
    else
	for (m = 0; m < 64; m++)
	    pcoeff[m] = q_block[zigzag[m]];
	
    /* Now reconstruct the DC coefficient */
        q_block[0] = pcoeff[0] + (DC_pred+cal_dc_scaler(QP,(comp<4)?1:2)/2)/cal_dc_scaler(QP,(comp<4)?1:2);
    
    /* Do AC prediction if required */
    if (ACpred_flag == 1)
    {
	/* Find AC predictions */
	if ((x_pos == 0) && y_pos == 0) {	/* top left corner */
	    if (comp == 1 || comp == 3)
		for (m = 0; m < 15; m++) 
		    pred_A[m] = Idir_d(((DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][m]) * (QP_store[y_pos*MB_width+(x_pos+Xpos[comp])])*2) , 2*QP);
	    else
		nullfill(pred_A,mid_grey);
	    if (comp == 2 || comp == 3)
		for (m = 0; m < 15; m++) 
		    pred_C[m] = Idir_d(((DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][m]) * (QP_store[(y_pos+Ypos[comp])*MB_width+x_pos])*2) , 2*QP);
	    else
		nullfill(pred_C,mid_grey);
	}
	else if (x_pos == 0) {	/* left edge */
	    if (comp == 1 || comp == 3)
		for (m = 0; m < 15; m++) 
		    pred_A[m] = Idir_d(((DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][m]) * (QP_store[y_pos*MB_width+(x_pos+Xpos[comp])])*2) , 2*QP);
	    else
		nullfill(pred_A,mid_grey);
	    if (((comp == 0 || comp == 1 || comp == 4 || comp == 5) && slice_nb[x_pos][y_pos] == slice_nb[x_pos][y_pos-1]) || 
		comp == 2 || comp == 3)
	      for (m = 0; m < 15; m++) 
		pred_C[m] =
		  Idir_d(((DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][m]) * (QP_store[(y_pos+Ypos[comp])*MB_width+x_pos])*2) , 2*QP);
	    else 
	      nullfill(pred_C,mid_grey);
	}
	else if (y_pos == 0) { /* top row */
	    if (((comp == 0 || comp == 2 || comp == 4 || comp == 5) && slice_nb[x_pos][y_pos] == slice_nb[x_pos-1][y_pos]) ||
		comp == 1 || comp == 3)
	      for (m = 0; m < 15; m++) 
		pred_A[m] =
		  Idir_d(((DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][m]) * (QP_store[y_pos*MB_width+(x_pos+Xpos[comp])])*2) , 2*QP);
	    else
	      nullfill(pred_A,mid_grey);
	    if (comp == 2 || comp == 3)
		for (m = 0; m < 15; m++) 
		    pred_C[m] = Idir_d(((DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][m]) * (QP_store[(y_pos+Ypos[comp])*MB_width+x_pos])*2) , 2*QP);
	    else
		nullfill(pred_C,mid_grey);
	}
	else {
	    if (((comp == 0 || comp == 2 || comp == 4 || comp == 5) && slice_nb[x_pos][y_pos] == slice_nb[x_pos-1][y_pos]) || 
		comp == 1 || comp == 3)
	      for (m = 0; m < 15; m++) 
		pred_A[m] =
		  Idir_d(((DC_store[y_pos*MB_width+(x_pos+Xpos[comp])][Xtab[comp]][m]) * (QP_store[y_pos*MB_width+(x_pos+Xpos[comp])])*2) , 2*QP);
	    else
	      nullfill(pred_A,mid_grey);
	    if (((comp == 0 || comp == 1 || comp == 4 || comp == 5) && slice_nb[x_pos][y_pos] == slice_nb[x_pos][y_pos-1]) ||
		comp  == 2 || comp == 3)
	      for (m = 0; m < 15; m++)
		pred_C[m] =
		  Idir_d(((DC_store[(y_pos+Ypos[comp])*MB_width+x_pos][Ytab[comp]][m]) * (QP_store[(y_pos+Ypos[comp])*MB_width+x_pos])*2) , 2*QP);
	    else
	      nullfill(pred_C,mid_grey);
	    
	}

	/* Now reconstruct the AC coefficients */
	if (direction == 0) { /* Horizontal, left COLUMN of block A */
	    for (m = 0; m < 7; m++) {
		q_block[(m+1)*8]= pcoeff[(m+1)*8] + pred_A[m+8];
	    }
	    for (m = 1; m < 8; m++) {
		q_block[m] = pcoeff[m];
	    }
	}
	else { /* Vertical, top ROW of block C */
	    for (m = 0; m < 7; m++) {
		q_block[(m+1)*8]= pcoeff[(m+1)*8];
	    }
	    for (m = 1; m < 8; m++) {
		q_block[m] = pcoeff[m] + pred_C[m];
	    }
	}

	/* Copy rest of coefficients */
	for (m = 1; m < 8; m++)
	    for (n = 1; n < 8; n++)
		q_block[m*8+n] = pcoeff[m*8+n];
    }
    else
	/* Copy all AC coefficients directly */
	for (m = 1; m < 64; m++)
	    q_block[m] = pcoeff[m];
}


/**
 * Small routine to fill default prediction values into a DC_store entry
 *
 * Author: 	Robert Danielsen
 * Created: 	23.10.96
 */

Void nullfill(Int pred[],Int mid_grey)
{
    Int i;

    pred[0] = mid_grey*8;
    for (i = 1; i < 15; i++) {
	pred[i] = 0;
    }
}    


Int Idir_d(Int val, Int QP) {

   if (val<0) return (val-QP/2)/QP;
     else return (val+QP/2)/QP;
}
