/*****************************************************************************
 *
 *  XVID MPEG-4 VIDEO CODEC
 *  - Prediction module -
 *
 *  Copyright (C) 2001-2003 Michael Militzer <isibaar@xvid.org>
 *                2001-2003 Peter Ross <pross@xvid.org>
 *
 *  This program is free software ; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation ; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY ; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program ; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 * $Id: mbprediction.cpp,v 1.1 2005/01/04 13:44:37 jeanlf Exp $
 *
 ****************************************************************************/

#include "global.h"
#include "mbprediction.h"

//----------------------------

#if 0

#define Divide(a,b)    (((a)>0) ? ((a)+((b)>>1))/(b) : ((a)-((b)>>1))/(b))

#else

#define RET_DIV(a, N) return (a*N+32768) >> 16;
#define RET_SHR(a, N, S) return (a+N) >> S;

static int Divide(int a, int b){
   switch(b){
   case 1: return a;
   case 2: return a >> 1;
   case 4: RET_SHR(a, 2, 2);
   case 8: RET_SHR(a, 4, 3);
   case 16: RET_SHR(a, 8, 4);
   case 32: RET_SHR(a, 16, 5);
   case 3: RET_DIV(a, 21845);
   case 5: RET_DIV(a, 13107);
   case 6: RET_DIV(a, 10923);
   case 7: RET_DIV(a, 9362);
   case 9: RET_DIV(a, 7282);
   case 10: RET_DIV(a, 6554);
   case 11: RET_DIV(a, 5958);
   case 12: RET_DIV(a, 5461);
   case 13: RET_DIV(a, 5041);
   case 14: RET_DIV(a, 4681);
   case 15: RET_DIV(a, 4369);
   case 17: RET_DIV(a, 3855);
   case 18: RET_DIV(a, 3641);
   case 19: RET_DIV(a, 3449);
   case 20: RET_DIV(a, 3277);
   case 21: RET_DIV(a, 3121);
   case 22: RET_DIV(a, 2979);
   case 23: RET_DIV(a, 2849);
   case 24: RET_DIV(a, 2731);
   case 25: RET_DIV(a, 2621);
   case 26: RET_DIV(a, 2521);
   case 27: RET_DIV(a, 2427);
   case 28: RET_DIV(a, 2341);
   case 29: RET_DIV(a, 2260);
   case 30: RET_DIV(a, 2185);
   case 31: RET_DIV(a, 2114);
   }
   return ((a>0) ? (a+(b>>1))/b : (a-(b>>1))/b);
}
#endif

//----------------------------

inline int rescale(int predict_quant, int current_quant, int coeff){

   if(!coeff)
      return 0;
   return Divide(coeff * predict_quant, current_quant);
}

//----------------------------

static const int default_acdc_values[15] = {
   1024,
   0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0
};

//----------------------------
/* get dc/ac prediction direction for a single block and place
   predictor values into MB->pred_values[j][..]
*/
void predict_acdc(MACROBLOCK *pMBs, dword x, dword y, dword mb_width, dword block, int qcoeff[64], dword current_quant, int iDcScaler, int predictors[8], int bound){

   const int mbpos = (y * mb_width) + x;
   int *left, *top, *diag, *current;

   int left_quant = current_quant;
   int top_quant = current_quant;

   const int *pLeft = default_acdc_values;
   const int *pTop = default_acdc_values;
   const int *pDiag = default_acdc_values;

   dword index = x + y * mb_width;  /* current macroblock */
   int *acpred_direction = &pMBs[index].acpred_directions[block];
   dword i;

   left = top = diag = current = 0;

   /* grab left,top and diag macroblocks */

   /* left macroblock */

   if (x && mbpos >= bound + 1  &&
      (pMBs[index - 1].mode == MODE_INTRA ||
       pMBs[index - 1].mode == MODE_INTRA_Q)) {

      left = pMBs[index - 1].pred_values[0];
      left_quant = pMBs[index - 1].quant;
   }
   /* top macroblock */

   if (mbpos >= bound + (int)mb_width &&
      (pMBs[index - mb_width].mode == MODE_INTRA ||
       pMBs[index - mb_width].mode == MODE_INTRA_Q)) {

      top = pMBs[index - mb_width].pred_values[0];
      top_quant = pMBs[index - mb_width].quant;
   }
   /* diag macroblock */

   if (x && mbpos >= bound + (int)mb_width + 1 &&
      (pMBs[index - 1 - mb_width].mode == MODE_INTRA ||
       pMBs[index - 1 - mb_width].mode == MODE_INTRA_Q)) {

      diag = pMBs[index - 1 - mb_width].pred_values[0];
   }

   current = pMBs[index].pred_values[0];

   /* now grab pLeft, pTop, pDiag _blocks_ */

   switch(block){
   case 0:
      if(left)
         pLeft = left + MBPRED_SIZE;
      if(top)
         pTop = top + (MBPRED_SIZE << 1);
      if(diag)
         pDiag = diag + 3 * MBPRED_SIZE;
      break;

   case 1:
      pLeft = current;
      left_quant = current_quant;
      if(top){
         pTop = top + 3 * MBPRED_SIZE;
         pDiag = top + (MBPRED_SIZE << 1);
      }
      break;

   case 2:
      if(left){
         pLeft = left + 3 * MBPRED_SIZE;
         pDiag = left + MBPRED_SIZE;
      }
      pTop = current;
      top_quant = current_quant;
      break;

   case 3:
      pLeft = current + (MBPRED_SIZE << 1);
      left_quant = current_quant;
      pTop = current + MBPRED_SIZE;
      top_quant = current_quant;
      pDiag = current;
      break;

   case 4:
      if(left)
         pLeft = left + (MBPRED_SIZE << 2);
      if(top)
         pTop = top + (MBPRED_SIZE << 2);
      if(diag)
         pDiag = diag + (MBPRED_SIZE << 2);
      break;

   case 5:
      if(left)
         pLeft = left + 5 * MBPRED_SIZE;
      if(top)
         pTop = top + 5 * MBPRED_SIZE;
      if(diag)
         pDiag = diag + 5 * MBPRED_SIZE;
      break;
   }

   /*
    * determine ac prediction direction & ac/dc predictor place rescaled ac/dc
    * predictions into predictors[] for later use
    */

   if(ABS(pLeft[0] - pDiag[0]) < ABS(pDiag[0] - pTop[0])){
                              //vertical
      *acpred_direction = 1;
      predictors[0] = Divide(pTop[0], iDcScaler);
      for(i = 1; i < 8; i++)
         predictors[i] = rescale(top_quant, current_quant, pTop[i]);
   }else{
                              //horizontal
      *acpred_direction = 2;
      predictors[0] = Divide(pLeft[0], iDcScaler);
      for(i = 1; i < 8; i++)
         predictors[i] = rescale(left_quant, current_quant, pLeft[i + 7]);
   }
}

//----------------------------
/* decoder: add predictors to dct_codes[] and
   store current coeffs to pred_values[] for future prediction
*/
void add_acdc(MACROBLOCK *pMB, dword block, int dct_codes[64], dword iDcScaler, int predictors[8]){

   byte acpred_direction = pMB->acpred_directions[block];
   int *pCurrent = pMB->pred_values[block];

   DPRINTF(XVID_DEBUG_COEFF,"predictor[0] %i\n", predictors[0]);

   dct_codes[0] += predictors[0];   /* dc prediction */
   pCurrent[0] = dct_codes[0] * iDcScaler;

   if(acpred_direction == 1){
      for(int i = 1; i < 8; i++){
         int level = dct_codes[i] + predictors[i];
         //DPRINTF(XVID_DEBUG_COEFF,"predictor[%i] %i\n",i, predictors[i]);
         dct_codes[i] = level;
         pCurrent[i] = level;
         pCurrent[i + 7] = dct_codes[i * 8];
      }
   }else
   if(acpred_direction == 2){
      for(int i = 1; i < 8; i++){
         int level = dct_codes[i * 8] + predictors[i];
         //DPRINTF(XVID_DEBUG_COEFF,"predictor[%i] %i\n",i*8, predictors[i]);
         dct_codes[i * 8] = level;
         pCurrent[i + 7] = level;
         pCurrent[i] = dct_codes[i];
      }
   }else{
      for(int i = 1; i < 8; i++) {
         pCurrent[i] = dct_codes[i];
         pCurrent[i + 7] = dct_codes[i * 8];
      }
   }
}

//----------------------------

static const VECTOR zeroMV = { 0, 0 };

VECTOR get_pmv2(const MACROBLOCK * const mbs, const int mb_width, const int bound, const int x, const int y, const int block){

   int lx, ly, lz;      /* left */
   int tx, ty, tz;      /* top */
   int rx, ry, rz;      /* top-right */
   int lpos, tpos, rpos;
   int num_cand = 0, last_cand = 1;

   VECTOR pmv[4]; /* left neighbour, top neighbour, top-right neighbour */

   switch(block){
   case 0:
      lx = x - 1; ly = y;     lz = 1;
      tx = x;     ty = y - 1; tz = 2;
      rx = x + 1; ry = y - 1; rz = 2;
      break;
   case 1:
      lx = x;     ly = y;     lz = 0;
      tx = x;     ty = y - 1; tz = 3;
      rx = x + 1; ry = y - 1; rz = 2;
      break;
   case 2:
      lx = x - 1; ly = y;     lz = 3;
      tx = x;     ty = y;     tz = 0;
      rx = x;     ry = y;     rz = 1;
      break;
   default:
      lx = x;     ly = y;     lz = 2;
      tx = x;     ty = y;     tz = 0;
      rx = x;     ry = y;     rz = 1;
   }

   lpos = lx + ly * mb_width;
   rpos = rx + ry * mb_width;
   tpos = tx + ty * mb_width;

   if(lpos >= bound && lx >= 0){
      num_cand++;
      pmv[1] = mbs[lpos].mvs[lz];
   }else
      pmv[1] = zeroMV;

   if(tpos >= bound){
      num_cand++;
      last_cand = 2;
      pmv[2] = mbs[tpos].mvs[tz];
   }else
      pmv[2] = zeroMV;

   if(rpos >= bound && rx < mb_width){
      num_cand++;
      last_cand = 3;
      pmv[3] = mbs[rpos].mvs[rz];
   }else
      pmv[3] = zeroMV;

                              //if there're more than one candidate, we return the median vector
   if(num_cand > 1){
                              //set median
      pmv[0].x =
         MIN(MAX(pmv[1].x, pmv[2].x),
            MIN(MAX(pmv[2].x, pmv[3].x), MAX(pmv[1].x, pmv[3].x)));
      pmv[0].y =
         MIN(MAX(pmv[1].y, pmv[2].y),
            MIN(MAX(pmv[2].y, pmv[3].y), MAX(pmv[1].y, pmv[3].y)));
      return pmv[0];
   }

   return pmv[last_cand];  //no point calculating median mv
}

//----------------------------
