/*!
 ***************************************************************************
 * \file md_common.c
 *
 * \brief
 *    Mode Decision common functions
 *
 * \author
 *    - Alexis Michael Tourapis    <alexismt@ieee.org>
 * \date
 *    04. October 2008
 **************************************************************************
 */

#include <limits.h>
#include "global.h"
#include "image.h"
#include "macroblock.h"
#include "mc_prediction.h"

/*!
 *************************************************************************************
 * \brief
 *    Copy rdo mv info to 16xN picture mv buffer
 *************************************************************************************
 */
static inline void CopyMVBlock16(PicMotionParams **mv_info, MotionVector **rdo_mv, int list, int block_x, int block_y, int start, int end)
{
  int j, i;
  for (j = start; j < end; j++)
  {
    for (i = 0; i < BLOCK_MULTIPLE; i++)
    {
      mv_info[block_y + j][block_x + i].mv[list] = rdo_mv[j][i];
    }
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Reset corresponding 16xN block picture mv buffer
 *************************************************************************************
 */
static inline void ResetMotionBlock16(PicMotionParams **mv_info, int list, int block_x, int block_y, int start, int end)
{
  int j, i;
  for (j = block_y + start; j < block_y + end; j++)
  {
    for (i = block_x; i < block_x + BLOCK_MULTIPLE; i++)
    {
      mv_info[j][i].mv[list] = zero_mv;
      mv_info[j][i].ref_idx[list] = -1;
      mv_info[j][i].ref_pic[list] = NULL;
    }
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Reset corresponding 16xN block picture mv buffer
 *************************************************************************************
 */
static inline void ResetMVBlock16(PicMotionParams **mv_info, int list, int block_x, int block_y, int start, int end)
{
  int j, i;
  for (j = block_y + start; j < block_y + end; j++)
  {
    for (i = block_x; i < block_x + BLOCK_MULTIPLE; i++)
      mv_info[j][i].mv[list] = zero_mv;
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Reset corresponding 16xN block picture ref buffer (intra)
 *************************************************************************************
 */
static inline void ResetRefBlock16(PicMotionParams **mv_info, int list, int block_x, int block_y, int start, int end)
{
  int j, i;
  for (j = block_y + start; j < block_y + end; j++)
  {
    for (i = block_x; i < block_x + BLOCK_MULTIPLE; ++i)
    {
      mv_info[j][i].ref_idx[list] = -1;
      mv_info[j][i].ref_pic[list] = NULL;
    }
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Copy rdo mv info to 8xN picture mv buffer
 *************************************************************************************
 */
static inline void CopyMVBlock8(PicMotionParams **mv_info, MotionVector **rdo_mv, int list, int block_x, int start, int end, int offset)
{
  int j;
  for (j = start; j < end; j++)
  {
    mv_info[j][block_x    ].mv[list] = rdo_mv[j][offset];
    mv_info[j][block_x + 1].mv[list] = rdo_mv[j][offset + 1];
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Reset corresponding 8xN block picture mv buffer
 *************************************************************************************
 */
static inline void ResetMotionBlock8(PicMotionParams **mv_info, int list, int block_x, int start, int end)
{
  int j;

  for (j = start; j < end; j++)
  {
    mv_info[j][block_x    ].mv[list] = zero_mv;
    mv_info[j][block_x    ].ref_idx[list] = -1;
    mv_info[j][block_x    ].ref_pic[list] = NULL;
    mv_info[j][block_x + 1].mv[list] = zero_mv;
    mv_info[j][block_x + 1].ref_idx[list] = -1;
    mv_info[j][block_x + 1].ref_pic[list] = NULL;
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Reset corresponding 8xN block picture mv buffer
 *************************************************************************************
 */
static inline void ResetMVBlock8(PicMotionParams **mv_info, int list, int block_x, int start, int end)
{
  int j;

  for (j = start; j < end; j++)
  {
    mv_info[j][block_x    ].mv[list] = zero_mv;
    mv_info[j][block_x + 1].mv[list] = zero_mv;
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Reset corresponding 8xN block picture ref buffer (intra)
 *************************************************************************************
 */
static inline void ResetRefBlock8(PicMotionParams **mv_info, int list, int block_x, int start, int end)
{
  int j;
  for (j = start; j < end; j++)
  {
    mv_info[j][block_x    ].ref_idx[list] = -1;
    mv_info[j][block_x    ].ref_pic[list] = NULL;
    mv_info[j][block_x + 1].ref_idx[list] = -1;
    mv_info[j][block_x + 1].ref_pic[list] = NULL;
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Sets motion vectors for a macroblock in a P Slice
 *************************************************************************************
 */
void SetMotionVectorsMBPSlice (Macroblock* currMB)
{
  Slice *currSlice = currMB->p_Slice;
  VideoParameters *p_Vid = currMB->p_Vid;
  PicMotionParams **mv_info = p_Vid->enc_picture->mv_info;

  RD_DATA *rdopt = currSlice->rddata;
  MotionVector ****all_mv  = currSlice->all_mv[LIST_0];
  int  l0_ref, mode8;

  if (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1))
  {
#if QC_GVSP_B0054
    memcpy(&rdopt->all_mv[LIST_0][0][0][0][0], &all_mv[0][0][0][0], p_Vid->max_num_references * 26 * MB_BLOCK_PARTITIONS * sizeof(MotionVector));
#else
#if MERL_VSP
    memcpy(&rdopt->all_mv[LIST_0][0][0][0][0], &all_mv[0][0][0][0], p_Vid->max_num_references * 11 * MB_BLOCK_PARTITIONS * sizeof(MotionVector));
#else
    memcpy(&rdopt->all_mv[LIST_0][0][0][0][0], &all_mv[0][0][0][0], p_Vid->max_num_references * 9 * MB_BLOCK_PARTITIONS * sizeof(MotionVector));
#endif
#endif
  }

  if (currMB->mb_type == PSKIP) // Skip mode
  {
#if QC_GVSP_B0054
    if (currMB->isVspRef)
       ResetMotionBlock16 (mv_info, LIST_0, currMB->block_x, currMB->block_y, 0, 4);
    else
    {
#endif
#if DEPTH_BASED_MVP || MTK_SKIP_DIRECT_A0045
	  if(currSlice->depth_based_mvp_flag)
		  l0_ref = mv_info[currMB->block_y][currMB->block_x].ref_idx[LIST_0];
	  else
		  l0_ref=0;
	  CopyMVBlock16(mv_info, all_mv[l0_ref][PSKIP], LIST_0, currMB->block_x, currMB->block_y, 0, 4);
#else
	  CopyMVBlock16(mv_info, all_mv[0][PSKIP], LIST_0, currMB->block_x, currMB->block_y, 0, 4);
#endif
#if QC_GVSP_B0054
    }
#endif
  }
#if MERL_VSP
  else if (currMB->mb_type == PVSP_SKIP)
  {
    // Set zero MVs, it should be OK to copy from all_mv
#if SMRC_BUGFIXES_Oct2012 && !NOKIA_USTC_IVP_VSP_D0126
		l0_ref = mv_info[currMB->block_y][currMB->block_x].ref_idx[LIST_0];
		CopyMVBlock16(mv_info, all_mv[l0_ref][10], LIST_0, currMB->block_x, currMB->block_y, 0, 4);
#else
    ResetMVBlock16(mv_info, LIST_0, currMB->block_x, currMB->block_y, 0, 4);
#endif
  }
#endif
  else if (currMB->mb_type == P16x16) // 16x16
  {
#if QC_GVSP_B0054
    if (currMB->isVspRef)
       ResetMotionBlock16 (mv_info, LIST_0, currMB->block_x, currMB->block_y, 0, 4);
    else
    {
#endif
    l0_ref = mv_info[currMB->block_y][currMB->block_x].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock16(mv_info, all_mv[l0_ref][9],  LIST_0, currMB->block_x, currMB->block_y, 0, 4);
	else
#endif
    CopyMVBlock16(mv_info, all_mv[l0_ref][P16x16], LIST_0, currMB->block_x, currMB->block_y, 0, 4);
#if QC_GVSP_B0054
    }
#endif
  }
  else if (currMB->mb_type == P16x8) // 16x8
  {
#if QC_GVSP_B0054
  if (currMB->isPartVspRef[0])
  {
    ResetMotionBlock16(mv_info, LIST_0, currMB->block_x, currMB->block_y, 0, 2);
  }
  else
  {
#endif
    l0_ref = mv_info[currMB->block_y][currMB->block_x].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock16(mv_info, all_mv[l0_ref][9],     LIST_0, currMB->block_x, currMB->block_y, 0, 2);
	else
#endif
    CopyMVBlock16(mv_info, all_mv[l0_ref][P16x8], LIST_0, currMB->block_x, currMB->block_y, 0, 2);
#if QC_GVSP_B0054
  }
#endif

#if QC_GVSP_B0054
  if (currMB->isPartVspRef[2])
  {
    ResetMotionBlock16(mv_info, LIST_0, currMB->block_x, currMB->block_y, 2, 4);
  }
  else
  {
#endif
    l0_ref = mv_info[currMB->block_y + 2][currMB->block_x].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock16(mv_info, all_mv[l0_ref][9], LIST_0, currMB->block_x, currMB->block_y, 2, 4);
	else
#endif
    CopyMVBlock16(mv_info, all_mv[l0_ref][P16x8], LIST_0, currMB->block_x, currMB->block_y, 2, 4);
#if QC_GVSP_B0054
  }
#endif
  }
  else if (currMB->mb_type == P8x16) // 8x16
  {
#if QC_GVSP_B0054
  if (currMB->isPartVspRef[0])
  {
    ResetMotionBlock8(&mv_info[currMB->block_y], LIST_0, currMB->block_x, 0, 4);
  }
  else
  {
#endif
    l0_ref = mv_info[currMB->block_y][currMB->block_x].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][9], LIST_0, currMB->block_x    , 0, 4, 0);
	else
#endif
    CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][P8x16], LIST_0, currMB->block_x    , 0, 4, 0);
#if QC_GVSP_B0054
  }
#endif

    l0_ref = mv_info[currMB->block_y][currMB->block_x + 2].ref_idx[LIST_0];
#if QC_GVSP_B0054
  if (currMB->isPartVspRef[1])
  {
    ResetMotionBlock8(&mv_info[currMB->block_y], LIST_0, currMB->block_x+2, 0, 4);
  }
  else
  {
#endif
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][9], LIST_0, currMB->block_x + 2, 0, 4, 2);
	else
#endif
    CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][P8x16], LIST_0, currMB->block_x + 2, 0, 4, 2);
#if QC_GVSP_B0054
  }
#endif
  } 
  else if (currMB->mb_type == P8x8) // 8x8
  {
#if QC_GVSP_B0054
  if (currMB->isPartVspRef[0])
  {
    ResetMotionBlock8(&mv_info[currMB->block_y], LIST_0, currMB->block_x, 0, 2);
  }
  else
  {
#endif
    mode8 = currMB->b8x8[0].mode;
    l0_ref = mv_info[currMB->block_y    ][currMB->block_x    ].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][9], LIST_0, currMB->block_x    , 0, 2, 0);
	else
#endif
    CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][mode8], LIST_0, currMB->block_x    , 0, 2, 0);
#if QC_GVSP_B0054
  }
#endif
#if QC_GVSP_B0054
  if (currMB->isPartVspRef[1])
  {
    ResetMotionBlock8(&mv_info[currMB->block_y], LIST_0, currMB->block_x+2, 0, 2);
  }
  else
  {
#endif
    mode8 = currMB->b8x8[1].mode;
    l0_ref = mv_info[currMB->block_y    ][currMB->block_x + 2].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][9], LIST_0, currMB->block_x + 2, 0, 2, 2);
	else
#endif
    CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][mode8], LIST_0, currMB->block_x + 2, 0, 2, 2);
#if QC_GVSP_B0054
  }
#endif

#if QC_GVSP_B0054
  if (currMB->isPartVspRef[2])
  {
    ResetMotionBlock8(&mv_info[currMB->block_y], LIST_0, currMB->block_x, 2, 4);
  }
  else
  {
#endif
    mode8 = currMB->b8x8[2].mode;
    l0_ref = mv_info[currMB->block_y + 2][currMB->block_x    ].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][9], LIST_0, currMB->block_x, 2, 4, 0);
	else
#endif
    CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][mode8], LIST_0, currMB->block_x, 2, 4, 0);
#if QC_GVSP_B0054
  }
#endif

#if QC_GVSP_B0054
  if (currMB->isPartVspRef[3])
  {
    ResetMotionBlock8(&mv_info[currMB->block_y], LIST_0, currMB->block_x+2, 2, 4);
  }
  else
  {
#endif
    mode8 = currMB->b8x8[3].mode;
    l0_ref = mv_info[currMB->block_y + 2][currMB->block_x + 2].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][9], LIST_0, currMB->block_x + 2, 2, 4, 2);
	else
#endif
    CopyMVBlock8(&mv_info[currMB->block_y], all_mv[l0_ref][mode8], LIST_0, currMB->block_x + 2, 2, 4, 2);
#if QC_GVSP_B0054
  }
#endif
  }
  else // Intra modes
  {
    ResetMotionBlock16 (mv_info, LIST_0, currMB->block_x, currMB->block_y, 0, 4);
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Sets motion vectors for a 16x8 partition in a B Slice
 *************************************************************************************
 */
static void SetMVBSlice16x8(Slice *currSlice, PicMotionParams **motion, Macroblock* currMB, int pos)
{
  int l0_ref, l1_ref;
  int pdir = currMB->b8x8[pos].pdir;

#if QC_GVSP_B0054
  if (currMB->isPartVspRef[pos])
  {
    ResetMotionBlock16 (motion, LIST_0, currMB->block_x, currMB->block_y, pos, pos + 2);
    ResetMotionBlock16 (motion, LIST_1, currMB->block_x, currMB->block_y, pos, pos + 2);
    return;
  }
#endif

  if (pdir == LIST_0) 
  {
    l0_ref = motion[currMB->block_y + pos][currMB->block_x].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock16(motion, currSlice->all_mv [LIST_0][l0_ref][9], LIST_0, currMB->block_x, currMB->block_y, pos, pos + 2);
	else
#endif
    CopyMVBlock16(motion, currSlice->all_mv [LIST_0][l0_ref][P16x8], LIST_0, currMB->block_x, currMB->block_y, pos, pos + 2);
    ResetMotionBlock16 (motion, LIST_1, currMB->block_x, currMB->block_y, pos, pos + 2);
  }
  else if (pdir == LIST_1)
  {
    ResetMotionBlock16 (motion, LIST_0, currMB->block_x, currMB->block_y, pos, pos + 2);
    l1_ref = motion[currMB->block_y + pos][currMB->block_x].ref_idx[LIST_1];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock16(motion, currSlice->all_mv [LIST_1][l1_ref][9],      LIST_1, currMB->block_x, currMB->block_y, pos, pos + 2);
	else
#endif
    CopyMVBlock16 (motion, currSlice->all_mv [LIST_1][l1_ref][P16x8], LIST_1, currMB->block_x, currMB->block_y, pos, pos + 2);
  }
  else
  {
    int bipred_me = currMB->b8x8[pos].bipred;
    MotionVector *****all_mv = bipred_me ? currSlice->bipred_mv[bipred_me - 1]: currSlice->all_mv;
    l0_ref = motion[currMB->block_y + pos][currMB->block_x].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock16 (motion, all_mv [LIST_0][l0_ref][9], LIST_0, currMB->block_x, currMB->block_y, pos, pos + 2);
	else
#endif
    CopyMVBlock16 (motion, all_mv [LIST_0][l0_ref][P16x8], LIST_0, currMB->block_x, currMB->block_y, pos, pos + 2);
    
    l1_ref = motion[currMB->block_y + pos][currMB->block_x].ref_idx[LIST_1];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock16 (motion, all_mv [LIST_1][l1_ref][9], LIST_1, currMB->block_x, currMB->block_y, pos, pos + 2);
	else
#endif
    CopyMVBlock16 (motion, all_mv [LIST_1][l1_ref][P16x8], LIST_1, currMB->block_x, currMB->block_y, pos, pos + 2);

    if (bipred_me && (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1)))
    {
#if IVMP
		if(currMB->ivmp_used)
		{
			memcpy(currSlice->rddata->all_mv [LIST_0][l0_ref][9][pos], all_mv [LIST_0][l0_ref][9][pos], 2 * BLOCK_MULTIPLE * sizeof(MotionVector));
			memcpy(currSlice->rddata->all_mv [LIST_1][l1_ref][9][pos], all_mv [LIST_1][l1_ref][9][pos], 2 * BLOCK_MULTIPLE * sizeof(MotionVector));
		}else{
#endif
      memcpy(currSlice->rddata->all_mv [LIST_0][l0_ref][P16x8][pos], all_mv [LIST_0][l0_ref][P16x8][pos], 2 * BLOCK_MULTIPLE * sizeof(MotionVector));
      memcpy(currSlice->rddata->all_mv [LIST_1][l1_ref][P16x8][pos], all_mv [LIST_1][l1_ref][P16x8][pos], 2 * BLOCK_MULTIPLE * sizeof(MotionVector));
#if IVMP
		}
#endif
    }
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Sets motion vectors for a 8x16 partition in a B Slice
 *************************************************************************************
 */
static void SetMVBSlice8x16(Slice *currSlice, PicMotionParams **motion, Macroblock* currMB, int pos)
{
  int l0_ref, l1_ref;
  int pdir = currMB->b8x8[pos >> 1].pdir;

#if QC_GVSP_B0054
  if (currMB->isPartVspRef[pos>>1])
  {
    ResetMotionBlock8(&motion[currMB->block_y], LIST_0, currMB->block_x + pos, 0, 4);
    ResetMotionBlock8(&motion[currMB->block_y], LIST_1, currMB->block_x + pos, 0, 4);
    return;
  }
#endif

  if (pdir == LIST_0) 
  {
    l0_ref = motion[currMB->block_y][currMB->block_x + pos].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8 (&motion[currMB->block_y], currSlice->all_mv [LIST_0][l0_ref][9], LIST_0, currMB->block_x + pos, 0, 4, pos);
	else
#endif
    CopyMVBlock8 (&motion[currMB->block_y], currSlice->all_mv [LIST_0][l0_ref][P8x16], LIST_0, currMB->block_x + pos, 0, 4, pos);
    ResetMotionBlock8(&motion[currMB->block_y], LIST_1, currMB->block_x + pos, 0, 4);
  }
  else if (pdir == LIST_1)
  {
    ResetMotionBlock8(&motion[currMB->block_y], LIST_0, currMB->block_x + pos, 0, 4);
    l1_ref = motion[currMB->block_y][currMB->block_x + pos].ref_idx[LIST_1];    
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8 (&motion[currMB->block_y], currSlice->all_mv [LIST_1][l1_ref][9], LIST_1, currMB->block_x + pos, 0, 4, pos);
	else
#endif
    CopyMVBlock8 (&motion[currMB->block_y], currSlice->all_mv [LIST_1][l1_ref][P8x16], LIST_1, currMB->block_x + pos, 0, 4, pos);
  }
  else
  {
    int bipred_me = currMB->b8x8[pos >> 1].bipred;
    MotionVector *****all_mv = bipred_me ? currSlice->bipred_mv[bipred_me - 1]: currSlice->all_mv;

    l0_ref = motion[currMB->block_y][currMB->block_x + pos].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8(&motion[currMB->block_y], all_mv [LIST_0][l0_ref][9], LIST_0, currMB->block_x + pos, 0, 4, pos);
	else
#endif
    CopyMVBlock8(&motion[currMB->block_y], all_mv [LIST_0][l0_ref][P8x16], LIST_0, currMB->block_x + pos, 0, 4, pos);
    l1_ref = motion[currMB->block_y][currMB->block_x + pos].ref_idx[LIST_1];    
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8(&motion[currMB->block_y], all_mv [LIST_1][l1_ref][9], LIST_1, currMB->block_x + pos, 0, 4, pos);
	else
#endif
    CopyMVBlock8(&motion[currMB->block_y], all_mv [LIST_1][l1_ref][P8x16], LIST_1, currMB->block_x + pos, 0, 4, pos);

    if (bipred_me && (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1)))
    {
      int j;
      for (j = 0; j < BLOCK_MULTIPLE; j++)
      {
#if IVMP
		  if(currMB->ivmp_used)
			  memcpy(&currSlice->rddata->all_mv [LIST_0][l0_ref][9][j][pos],     &all_mv [LIST_0][l0_ref][9][j][pos], 2 * sizeof(MotionVector));
		  else
#endif
        memcpy(&currSlice->rddata->all_mv [LIST_0][l0_ref][P8x16][j][pos], &all_mv [LIST_0][l0_ref][P8x16][j][pos], 2 * sizeof(MotionVector));
      }
      for (j = 0; j < BLOCK_MULTIPLE; j++)
      {
#if IVMP
		  if(currMB->ivmp_used)
			  memcpy(&currSlice->rddata->all_mv [LIST_1][l1_ref][9][j][pos], &all_mv [LIST_1][l1_ref][9][j][pos], 2 * sizeof(MotionVector));
		  else
#endif
        memcpy(&currSlice->rddata->all_mv [LIST_1][l1_ref][P8x16][j][pos], &all_mv [LIST_1][l1_ref][P8x16][j][pos], 2 * sizeof(MotionVector));
      }
    }
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Sets motion vectors for a 8x8 partition in a B Slice
 *************************************************************************************
 */
static void SetMVBSlice8x8(Slice *currSlice, PicMotionParams **motion, Macroblock* currMB, int pos_y, int pos_x)
{
  int l0_ref, l1_ref;
  int pos = pos_y + (pos_x >> 1);
  int pdir = currMB->b8x8[pos].pdir;
  int mode = currMB->b8x8[pos].mode;
  int block_y = currMB->block_y + pos_y;
  int block_x = currMB->block_x + pos_x;

#if QC_GVSP_B0054
  if (currMB->isPartVspRef[pos])
  {
    ResetMotionBlock8 (&motion[currMB->block_y], LIST_0, currMB->block_x + pos_x, pos_y, pos_y + 2);
    ResetMotionBlock8 (&motion[currMB->block_y], LIST_1, currMB->block_x + pos_x, pos_y, pos_y + 2);
    return;
  }
#endif

  if (pdir == LIST_0) 
  {
    l0_ref = motion[block_y][block_x].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8 (&motion[currMB->block_y], currSlice->all_mv [LIST_0][l0_ref][9], LIST_0, currMB->block_x + pos_x, pos_y, pos_y + 2, pos_x);
	else
#endif
    CopyMVBlock8 (&motion[currMB->block_y], currSlice->all_mv [LIST_0][l0_ref][mode], LIST_0, currMB->block_x + pos_x, pos_y, pos_y + 2, pos_x);
    ResetMotionBlock8 (&motion[currMB->block_y], LIST_1, currMB->block_x + pos_x, pos_y, pos_y + 2);
  }
  else if (pdir == LIST_1)
  {
    ResetMotionBlock8(&motion[currMB->block_y], LIST_0, currMB->block_x + pos_x, pos_y, pos_y + 2);
    l1_ref = motion[block_y][block_x].ref_idx[LIST_1];    
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8 (&motion[currMB->block_y], currSlice->all_mv [LIST_1][l1_ref][9], LIST_1, currMB->block_x + pos_x, pos_y, pos_y + 2, pos_x);
	else
#endif
    CopyMVBlock8 (&motion[currMB->block_y], currSlice->all_mv [LIST_1][l1_ref][mode], LIST_1, currMB->block_x + pos_x, pos_y, pos_y + 2, pos_x);
  }
  else if (pdir == BI_PRED)
  {
    int bipred_me = currMB->b8x8[pos].bipred;
    MotionVector *****all_mv = bipred_me ? currSlice->bipred_mv[bipred_me - 1]: currSlice->all_mv;
    l0_ref = motion[block_y][block_x].ref_idx[LIST_0];
#if IVMP
	if(currMB->ivmp_used)
		   CopyMVBlock8(&motion[currMB->block_y], all_mv [LIST_0][l0_ref][9], LIST_0, currMB->block_x + pos_x, pos_y, pos_y + 2, pos_x);
	else
#endif
    CopyMVBlock8(&motion[currMB->block_y], all_mv [LIST_0][l0_ref][mode], LIST_0, currMB->block_x + pos_x, pos_y, pos_y + 2, pos_x);
    
    l1_ref = motion[block_y][block_x].ref_idx[LIST_1];    
#if IVMP
	if(currMB->ivmp_used)
		CopyMVBlock8(&motion[currMB->block_y], all_mv [LIST_1][l1_ref][9], LIST_1, currMB->block_x + pos_x, pos_y, pos_y + 2, pos_x);
	else
#endif
    CopyMVBlock8(&motion[currMB->block_y], all_mv [LIST_1][l1_ref][mode], LIST_1, currMB->block_x + pos_x, pos_y, pos_y + 2, pos_x);

    if (bipred_me && (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1)))
    {
#if IVMP
		if(currMB->ivmp_used)
		{
			memcpy(&currSlice->rddata->all_mv [LIST_0][l0_ref][9][pos_y    ][pos_x], &all_mv [LIST_0][l0_ref][mode][pos_y    ][pos_x], 2 * sizeof(MotionVector));
			memcpy(&currSlice->rddata->all_mv [LIST_0][l0_ref][9][pos_y + 1][pos_x], &all_mv [LIST_0][l0_ref][mode][pos_y + 1][pos_x], 2 * sizeof(MotionVector));
			memcpy(&currSlice->rddata->all_mv [LIST_1][l1_ref][9][pos_y    ][pos_x], &all_mv [LIST_1][l1_ref][mode][pos_y    ][pos_x], 2 * sizeof(MotionVector));
			memcpy(&currSlice->rddata->all_mv [LIST_1][l1_ref][9][pos_y + 1][pos_x], &all_mv [LIST_1][l1_ref][mode][pos_y + 1][pos_x], 2 * sizeof(MotionVector));
		}else{
#endif
      memcpy(&currSlice->rddata->all_mv [LIST_0][l0_ref][mode][pos_y    ][pos_x], &all_mv [LIST_0][l0_ref][mode][pos_y    ][pos_x], 2 * sizeof(MotionVector));
      memcpy(&currSlice->rddata->all_mv [LIST_0][l0_ref][mode][pos_y + 1][pos_x], &all_mv [LIST_0][l0_ref][mode][pos_y + 1][pos_x], 2 * sizeof(MotionVector));
      memcpy(&currSlice->rddata->all_mv [LIST_1][l1_ref][mode][pos_y    ][pos_x], &all_mv [LIST_1][l1_ref][mode][pos_y    ][pos_x], 2 * sizeof(MotionVector));
      memcpy(&currSlice->rddata->all_mv [LIST_1][l1_ref][mode][pos_y + 1][pos_x], &all_mv [LIST_1][l1_ref][mode][pos_y + 1][pos_x], 2 * sizeof(MotionVector));
#if IVMP
		}
#endif
    }
  }
  else // Invalid direct modes (out of range mvs). Adding this here for precaution purposes.
  {
    ResetMotionBlock8(&motion[currMB->block_y], LIST_0, currMB->block_x + pos_x, pos_y, pos_y + 2);
    ResetMotionBlock8(&motion[currMB->block_y], LIST_1, currMB->block_x + pos_x, pos_y, pos_y + 2);
  }
}

void SetMotionVectorsMBISlice (Macroblock* currMB)
{
  UNREFERENCED_PARAMETER(currMB);
}
/*!
 *************************************************************************************
 * \brief
 *    Sets motion vectors for a macroblock in a P Slice
 *************************************************************************************
 */
void SetMotionVectorsMBBSlice (Macroblock* currMB)
{
  VideoParameters *p_Vid = currMB->p_Vid;
  Slice *currSlice = currMB->p_Slice;
  PicMotionParams **motion = p_Vid->enc_picture->mv_info;

  int  l0_ref, l1_ref;

  // copy all the motion vectors into rdopt structure
  // Can simplify this by copying the MV's of the best mode (TBD)
  // Should maybe add code to check for Intra only profiles

  if (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1))
  {
#if QC_GVSP_B0054
    memcpy(&currSlice->rddata->all_mv[LIST_0][0][0][0][0], &currSlice->all_mv[LIST_0][0][0][0][0], 2 * p_Vid->max_num_references * 26 * MB_BLOCK_PARTITIONS * sizeof(MotionVector));
#else
#if MERL_VSP
    memcpy(&currSlice->rddata->all_mv[LIST_0][0][0][0][0], &currSlice->all_mv[LIST_0][0][0][0][0], 2 * p_Vid->max_num_references * 11 * MB_BLOCK_PARTITIONS * sizeof(MotionVector));
#else
    memcpy(&currSlice->rddata->all_mv[LIST_0][0][0][0][0], &currSlice->all_mv[LIST_0][0][0][0][0], 2 * p_Vid->max_num_references * 9 * MB_BLOCK_PARTITIONS * sizeof(MotionVector));
#endif
#endif
  }

  if (currMB->mb_type == P16x16) // 16x16
  {
#if QC_GVSP_B0054
  if (currMB->isVspRef)
  {
    ResetMotionBlock16 (motion, LIST_0, currMB->block_x, currMB->block_y, 0, 4);
    ResetMotionBlock16 (motion, LIST_1, currMB->block_x, currMB->block_y, 0, 4);
  }
  else
  {
#endif
    int pdir = currMB->b8x8[0].pdir;      
    if (pdir == LIST_0) 
    {
      l0_ref = motion[currMB->block_y][currMB->block_x].ref_idx[LIST_0];
#if IVMP
	  if(currMB->ivmp_used)
		  CopyMVBlock16 (motion, currSlice->all_mv [LIST_0][l0_ref][9],  LIST_0, currMB->block_x, currMB->block_y, 0, 4);
	  else
#endif
      CopyMVBlock16 (motion, currSlice->all_mv [LIST_0][l0_ref][P16x16], LIST_0, currMB->block_x, currMB->block_y, 0, 4);
      ResetMotionBlock16(motion, LIST_1, currMB->block_x, currMB->block_y, 0, 4);
    }
    else if (pdir == LIST_1)
    {
      l1_ref = motion[currMB->block_y][currMB->block_x].ref_idx[LIST_1];
      ResetMotionBlock16 (motion, LIST_0, currMB->block_x, currMB->block_y, 0, 4);
#if IVMP
	  if(currMB->ivmp_used)
		  CopyMVBlock16 (motion, currSlice->all_mv [LIST_1][l1_ref][9],      LIST_1, currMB->block_x, currMB->block_y, 0, 4);
	  else
#endif
      CopyMVBlock16      (motion, currSlice->all_mv [LIST_1][l1_ref][P16x16], LIST_1, currMB->block_x, currMB->block_y, 0, 4);
    }
    else
    {
      int bipred_me = currMB->b8x8[0].bipred;
      MotionVector *****all_mv  = bipred_me ? currSlice->bipred_mv[bipred_me - 1]: currSlice->all_mv;
      l0_ref = motion[currMB->block_y][currMB->block_x].ref_idx[LIST_0];
#if IVMP
	  if(currMB->ivmp_used)
		  CopyMVBlock16 (motion, all_mv [LIST_0][l0_ref][9],  LIST_0, currMB->block_x, currMB->block_y, 0, 4);
	  else
#endif
      CopyMVBlock16 (motion, all_mv [LIST_0][l0_ref][P16x16], LIST_0, currMB->block_x, currMB->block_y, 0, 4);
      l1_ref = motion[currMB->block_y][currMB->block_x].ref_idx[LIST_1];
#if IVMP
	  if(currMB->ivmp_used)
		  CopyMVBlock16 (motion, all_mv [LIST_1][l1_ref][9],      LIST_1, currMB->block_x, currMB->block_y, 0, 4);
	  else
#endif
      CopyMVBlock16 (motion, all_mv [LIST_1][l1_ref][P16x16], LIST_1, currMB->block_x, currMB->block_y, 0, 4);

      // Is this necessary here? Can this be moved somewhere else?
      if (bipred_me && (currSlice->mb_aff_frame_flag || (currSlice->UseRDOQuant && currSlice->RDOQ_QP_Num > 1)))
      {
#if IVMP
		  if(currMB->ivmp_used)
		  {
			  memcpy(&currSlice->rddata->all_mv [LIST_0][l0_ref][9][0][0], &all_mv [LIST_0][l0_ref][9][0][0], MB_BLOCK_PARTITIONS * sizeof(MotionVector));
			  memcpy(&currSlice->rddata->all_mv [LIST_1][l1_ref][9][0][0], &all_mv [LIST_1][l1_ref][9][0][0], MB_BLOCK_PARTITIONS * sizeof(MotionVector));
		  }else
		  {
#endif
        memcpy(&currSlice->rddata->all_mv [LIST_0][l0_ref][P16x16][0][0], &all_mv [LIST_0][l0_ref][P16x16][0][0], MB_BLOCK_PARTITIONS * sizeof(MotionVector));
        memcpy(&currSlice->rddata->all_mv [LIST_1][l1_ref][P16x16][0][0], &all_mv [LIST_1][l1_ref][P16x16][0][0], MB_BLOCK_PARTITIONS * sizeof(MotionVector));
#if IVMP
		  }
#endif
      }
    }
#if QC_GVSP_B0054
  }
#endif
  }
  else if (currMB->mb_type == P16x8) // 16x8
  {        
    SetMVBSlice16x8(currSlice, motion, currMB, 0);
    SetMVBSlice16x8(currSlice, motion, currMB, 2);
  }
  else if (currMB->mb_type == P8x16) // 16x8
  {        
    SetMVBSlice8x16(currSlice, motion, currMB, 0);
    SetMVBSlice8x16(currSlice, motion, currMB, 2);
  }
#if MERL_VSP && !NOKIA_USTC_IVP_VSP_D0126
  else if (currMB->mb_type == P8x8 || currMB->mb_type == BSKIP_DIRECT || currMB->mb_type == BVSP_DIRECT) // 8x8 & Direct/SKIP
#else
  else if (currMB->mb_type == P8x8 || currMB->mb_type == BSKIP_DIRECT) // 8x8 & Direct/SKIP
#endif
  { 
#if QC_GVSP_B0054
  if (currMB->isVspRef && currMB->mb_type == BSKIP_DIRECT)
  {
    ResetMotionBlock16 (motion, LIST_0, currMB->block_x, currMB->block_y, 0, 4);
    ResetMotionBlock16 (motion, LIST_1, currMB->block_x, currMB->block_y, 0, 4);
  }
  else
  {
#endif
    SetMVBSlice8x8(currSlice, motion, currMB, 0, 0);
    SetMVBSlice8x8(currSlice, motion, currMB, 0, 2);
    SetMVBSlice8x8(currSlice, motion, currMB, 2, 0);
    SetMVBSlice8x8(currSlice, motion, currMB, 2, 2);
#if QC_GVSP_B0054
  }
#endif
  }
#if NOKIA_USTC_IVP_VSP_D0126
  else if(currMB->mb_type == BVSP_DIRECT)
  {
    ResetMVBlock16(motion, LIST_0, currMB->block_x, currMB->block_y, 0, 4);
    ResetMVBlock16(motion, LIST_1, currMB->block_x, currMB->block_y, 0, 4);
  }
#endif
  else // Intra modes
  {
    ResetMotionBlock16 (motion, LIST_0, currMB->block_x, currMB->block_y, 0, 4);
    ResetMotionBlock16 (motion, LIST_1, currMB->block_x, currMB->block_y, 0, 4);
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Copy ImgPel Data from one structure to another (16x16)
 *************************************************************************************
 */
void copy_image_data_16x16(imgpel  **imgBuf1, imgpel  **imgBuf2, int off1, int off2)
{
  int j;
  for(j=0; j<MB_BLOCK_SIZE; j++)
  {
    memcpy(&imgBuf1[j][off1], &imgBuf2[j][off2], MB_BLOCK_SIZE * sizeof (imgpel));
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Copy ImgPel Data from one structure to another (8x8)
 *************************************************************************************
 */
void copy_image_data_8x8(imgpel  **imgBuf1, imgpel  **imgBuf2, int off1, int off2)
{
  int j;
  for(j = 0; j < BLOCK_SIZE_8x8; j++)
  {
    memcpy(&imgBuf1[j][off1], &imgBuf2[j][off2], BLOCK_SIZE_8x8 * sizeof (imgpel));
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Copy ImgPel Data from one structure to another (4x4)
 *************************************************************************************
 */
void copy_image_data_4x4(imgpel  **imgBuf1, imgpel  **imgBuf2, int off1, int off2)
{
  int j;
  for(j = 0; j < BLOCK_SIZE; j++)
  {
    memcpy(&imgBuf1[j][off1], &imgBuf2[j][off2], BLOCK_SIZE * sizeof (imgpel));
  }
}

/*!
 *************************************************************************************
 * \brief
 *    Copy ImgPel Data from one structure to another (8x8)
 *************************************************************************************
 */
void copy_image_data(imgpel  **imgBuf1, imgpel  **imgBuf2, int off1, int off2, int width, int height)
{
  int j;
  for(j = 0; j < height; j++)
  {
    memcpy(&imgBuf1[j][off1], &imgBuf2[j][off2], width * sizeof (imgpel));
  }
}

/*
*************************************************************************************
* \brief
*    for an 8x8 sub-macroblock: initialization of RD_8x8DATA
*************************************************************************************
*/
void ResetRD8x8Data(VideoParameters *p_Vid, RD_8x8DATA *rd_data)
{
  int block;
  p_Vid->giRDOpt_B8OnlyFlag = TRUE;

  rd_data->mb_p8x8_cost = 0;
  rd_data->cbp8x8 = 0;
  rd_data->cbp_blk8x8 = 0;
  rd_data->cnt_nonz_8x8 = 0;

  for (block = 0; block < 4; block++)
  {
    rd_data->smb_p8x8_cost[block] = DISTBLK_MAX;
    rd_data->smb_p8x8_rdcost[block] = DISTBLK_MAX;
    rd_data->part[block].mode = -1;
#if QC_GVSP_B0054
    rd_data->cnt_nonz_8x8_blk[block] = 0;
#endif
  }
}

/*!
*************************************************************************************
* \brief
*    set the range of chroma prediction mode
*************************************************************************************
*/
void set_chroma_pred_mode(Macroblock *currMB, RD_PARAMS enc_mb, int *mb_available, char chroma_pred_mode_range[2])
{
  VideoParameters *p_Vid = currMB->p_Vid;
  InputParameters *p_Inp = currMB->p_Inp;

  if ((p_Vid->yuv_format != YUV400) && (p_Inp->separate_colour_plane_flag == 0))
  {
    Slice *currSlice = currMB->p_Slice;
    // precompute all new chroma intra prediction modes
    currSlice->intra_chroma_prediction(currMB, &mb_available[0], &mb_available[1], &mb_available[2]);

    if (p_Inp->FastCrIntraDecision) 
    {          
      currSlice->intra_chroma_RD_decision(currMB, &enc_mb);

      chroma_pred_mode_range[0] = currMB->c_ipred_mode;
      chroma_pred_mode_range[1] = currMB->c_ipred_mode;
    }
    else 
    {
      chroma_pred_mode_range[0] = DC_PRED_8;
      chroma_pred_mode_range[1] = PLANE_8;
    }
  }
  else
  {
    chroma_pred_mode_range[0] = DC_PRED_8;        
    chroma_pred_mode_range[1] = DC_PRED_8;
  }
}
