/*
   File: motion.cc

   Description:
   Motion Vectors
*/

#ifdef __GNUG__
#pragma implementation
#endif

#include <stdio.h>
#include <fstream.h>
#include <sys/time.h>

#include "athread.hh"

#include "error.hh"
#include "debug.hh"
#include "util.hh"

#include "sync.hh"
#include "mpeg2const.hh"
#include "mpeg2buff.hh"

#include "videoconst.hh"
#include "display.hh"
#include "idct.hh"
#include "vstream.hh"
#include "layerdata.hh"
#include "global.hh"

inline void calcMV(int *pred, int r_size, int motion_code, int motion_r, int full_pel_vector);

void LayerData::motion_vectors(int PMV[2][2][2], int dmvector[2], int mv_field_sel[2][2],
int s, int mv_count, int mv_format, int h_r_size, int v_r_size, int dmv, int mvscale){
  if (mv_count==1){
    if (mv_format==MV_FIELD && !dmv){
      mv_field_sel[1][s] = mv_field_sel[0][s] = input->getbits(1);
#ifdef TRACE
      if (trace){
        printf("motion_vertical_field_select[][%d] (%d): %d\n",s,
          mv_field_sel[0][s],mv_field_sel[0][s]);
      }
#endif
    }

    motion_vector(PMV[0][s],dmvector,h_r_size,v_r_size,dmv,mvscale,0);

    /* update other motion vector predictors */
    PMV[1][s][0] = PMV[0][s][0];
    PMV[1][s][1] = PMV[0][s][1];
  }
  else {
    mv_field_sel[0][s] = input->getbits(1);
#ifdef TRACE
    if (trace){
      printf("motion_vertical_field_select[0][%d] (%d): %d\n",s,
        mv_field_sel[0][s],mv_field_sel[0][s]);
    }
#endif
    motion_vector(PMV[0][s],dmvector,h_r_size,v_r_size,dmv,mvscale,0);
    if (fault)
      return;

    mv_field_sel[1][s] = input->getbits(1);
#ifdef TRACE
    if (trace){
      printf("motion_vertical_field_select[1][%d] (%d): %d\n",s,
        mv_field_sel[1][s],mv_field_sel[1][s]);
    }
#endif
    motion_vector(PMV[1][s],dmvector,h_r_size,v_r_size,dmv,mvscale,0);
    if (fault)
      return;
  }
}


/* get and decode motion vector and differential motion vector */
void LayerData::motion_vector(int *PMV, int *dmvector, int h_r_size, int v_r_size,
int dmv, int mvscale, int full_pel_vector){
/*
int dmv, * MPEG-2 only: get differential motion vectors *
int mvscale, * MPEG-2 only: field vector in frame pic *
int full_pel_vector * MPEG-1 only *
*/
  int motion_code = getMV();
  if (fault)
    return;
  int motion_r = (h_r_size!=0 && motion_code!=0) ? input->getbits(h_r_size) : 0;

#ifdef TRACE
  if (trace){
    if (h_r_size!=0 && motion_code!=0){
      printf("motion_residual (");
      printbits(motion_r,h_r_size,h_r_size);
      printf("): %d\n",motion_r);
    }
  }
#endif

  calcMV(&PMV[0],h_r_size,motion_code,motion_r,full_pel_vector);

  if (dmv) dmvector[0] = getDMV();

  motion_code = getMV();
  if (fault)
    return;
  motion_r = (v_r_size!=0 && motion_code!=0) ? input->getbits(v_r_size) : 0;

#ifdef TRACE
  if (trace){
    if (v_r_size!=0 && motion_code!=0){
      printf("motion_residual (");
      printbits(motion_r,v_r_size,v_r_size);
      printf("): %d\n",motion_r);
    }
  }
#endif

  if (mvscale) PMV[1] >>= 1; /* DIV 2 */

  calcMV(&PMV[1],v_r_size,motion_code,motion_r,full_pel_vector);

  if (mvscale) PMV[1] <<= 1;

  if (dmv) dmvector[1] = getDMV();

#ifdef TRACE
  if (trace)
    printf("PMV = %d,%d\n",PMV[0],PMV[1]);
#endif
}


/* calculate motion vector component */
inline void calcMV(int *pred, int r_size, int motion_code, int motion_r, int full_pel_vector){
  int lim = 16<<r_size;
  int vec = full_pel_vector ? (*pred >> 1) : (*pred);

  if (motion_code>0){
    vec+= ((motion_code-1)<<r_size) + motion_r + 1;
    if (vec>=lim) vec-= lim + lim;
  }
  else if (motion_code<0){
    vec-= ((-motion_code-1)<<r_size) + motion_r + 1;
    if (vec<-lim) vec+= lim + lim;
  }
  *pred = full_pel_vector ? (vec<<1) : vec;
}


void LayerData::calc_DMV(int DMV[][2], int *dmvector, int mvx, int mvy){
/*
int *dmvector, * differential motion vector *
int mvx, int mvy  * decoded mv components (always in field format) *
*/
  if (pict_struct==FRAME_PICTURE){
    if (topfirst){
      /* vector for prediction of top field from bottom field */
      DMV[0][0] = ((mvx  +(mvx>0))>>1) + dmvector[0];
      DMV[0][1] = ((mvy  +(mvy>0))>>1) + dmvector[1] - 1;

      /* vector for prediction of bottom field from top field */
      DMV[1][0] = ((3*mvx+(mvx>0))>>1) + dmvector[0];
      DMV[1][1] = ((3*mvy+(mvy>0))>>1) + dmvector[1] + 1;
    }
    else {
      /* vector for prediction of top field from bottom field */
      DMV[0][0] = ((3*mvx+(mvx>0))>>1) + dmvector[0];
      DMV[0][1] = ((3*mvy+(mvy>0))>>1) + dmvector[1] - 1;

      /* vector for prediction of bottom field from top field */
      DMV[1][0] = ((mvx  +(mvx>0))>>1) + dmvector[0];
      DMV[1][1] = ((mvy  +(mvy>0))>>1) + dmvector[1] + 1;
    }
  }
  else {
    /* vector for prediction from field of opposite 'parity' */
    DMV[0][0] = ((mvx+(mvx>0))>>1) + dmvector[0];
    DMV[0][1] = ((mvy+(mvy>0))>>1) + dmvector[1];

    /* correct for vertical field shift */
    if (pict_struct==TOP_FIELD) DMV[0][1]--;
    else                        DMV[0][1]++;
  }
}

