/*
 * Copyright (c) 2021, Alliance for Open Media. All rights reserved
 *
 * This source code is subject to the terms of the BSD 3-Clause Clear License
 * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
 * License was not distributed with this source code in the LICENSE file, you
 * can obtain it at aomedia.org/license/software-license/bsd-3-c-c/.  If the
 * Alliance for Open Media Patent License 1.0 was not distributed with this
 * source code in the PATENTS file, you can obtain it at
 * aomedia.org/license/patent-license/.
 */

#ifndef AVM_AV2_ENCODER_MCOMP_H_
#define AVM_AV2_ENCODER_MCOMP_H_

#include "av2/common/mv.h"
#include "av2/encoder/block.h"
#include "av2/common/reconinter.h"

#include "avm_dsp/variance.h"

#ifdef __cplusplus
extern "C" {
#endif

// The maximum number of steps in a step search given the largest
// allowed initial step
#define MAX_MVSEARCH_STEPS 13
// Enable the use of motion vector in range [-8191, 8191].
#define MAX_FULL_PEL_VAL ((1 << MV_CLASSES) - 1)
// Enable the use of motion vector in range [-2047, 2047] for low motion
#define LOW_MOTION_MAX_FULL_PEL_VAL ((1 << (MV_CLASSES - 2)) - 1)
// Maximum size of the first step in full pel units
#define MAX_FIRST_STEP ((1 << MAX_MVSEARCH_STEPS) - 1)
// Maximum size of the first step in full pel units for low motion
#define LOW_MOTION_MAX_FIRST_STEP ((1 << (MAX_MVSEARCH_STEPS - 2)) - 1)

// Maximum number of neighbors to scan per iteration during
// WARP_CAUSAL refinement
// Note: The elements of warp_search_config.neighbor_mask must be at least
// MAX_WARP_SEARCH_NEIGHBORS many bits wide. So the type may need to be
// widened if this value is increased.
#define MAX_WARP_SEARCH_NEIGHBORS 8

#define SEARCH_RANGE_8P 3
#define SEARCH_GRID_STRIDE_8P (2 * SEARCH_RANGE_8P + 1)
#define SEARCH_GRID_CENTER_8P \
  (SEARCH_RANGE_8P * SEARCH_GRID_STRIDE_8P + SEARCH_RANGE_8P)

// motion search site
typedef struct search_site {
  FULLPEL_MV mv;
  int offset;
} search_site;

typedef struct search_site_config {
  search_site site[MAX_MVSEARCH_STEPS * 2][16 + 1];
  // Number of search steps.
  int num_search_steps;
  int searches_per_step[MAX_MVSEARCH_STEPS * 2];
  int radius[MAX_MVSEARCH_STEPS * 2];
  int stride;
  int enable_high_motion;
} search_site_config;

typedef struct {
  FULLPEL_MV coord;
  int coord_offset;
} search_neighbors;

struct AV2_COMP;
struct SPEED_FEATURES;

typedef struct {
  // The reference mv used to compute the mv cost
  const MV *ref_mv;
  FULLPEL_MV full_ref_mv;
  MV_COST_TYPE mv_cost_type;
  // Stores the entropy table needed to signal an mv. Includes the joint-mv cost
  // and the diff cost.
  const MvCosts *mv_costs;
  MvSubpelPrecision pb_mv_precision;
  int is_adaptive_mvd;
  int is_ibc_cost;
} MV_COST_PARAMS;

int av2_mv_sign_cost(const int sign, const int comp, const MvCosts *mv_costs,
                     int weight, int round_bit, const int is_adaptive_mvd);

int av2_mv_bit_cost(const MV *mv, const MV *ref_mv,
                    const MvSubpelPrecision pb_mv_precision,
                    const MvCosts *mv_costs, int weight,
                    const int is_adaptive_mvd);

int av2_intrabc_mv_bit_cost(const MV *mv, const MV *ref_mv,
                            const IntraBCMvCosts *mv_costs, int weight,
                            MvSubpelPrecision precision);

int av2_get_mvpred_sse(const MV_COST_PARAMS *mv_cost_params,
                       const FULLPEL_MV best_mv,
                       const avm_variance_fn_ptr_t *vfp,
                       const struct buf_2d *src, const struct buf_2d *pre);

// =============================================================================
//  Motion Search
// =============================================================================
typedef struct {
  // The reference buffer
  const struct buf_2d *ref;

  // The source and predictors/mask used by translational search
  const struct buf_2d *src;
  const uint16_t *second_pred;
  const uint8_t *mask;
  int mask_stride;
  int inv_mask;
} MSBuffers;

static INLINE void av2_set_ms_compound_refs(MSBuffers *ms_buffers,
                                            const uint16_t *second_pred,
                                            const uint8_t *mask,
                                            int mask_stride, int invert_mask) {
  ms_buffers->second_pred = second_pred;
  ms_buffers->mask = mask;
  ms_buffers->mask_stride = mask_stride;
  ms_buffers->inv_mask = invert_mask;
}

// =============================================================================
//  Fullpixel Motion Search
// =============================================================================
enum {
  // Search 8-points in the radius grid around center, up to 11 search stages.
  DIAMOND = 0,
  // Search 12-points in the radius/tan_radius grid around center,
  // up to 15 search stages.
  NSTEP = 1,
  // Search maximum 8-points in the radius grid around center,
  // up to 11 search stages. First stage consists of 8 search points
  // and the rest with 6 search points each in hex shape.
  HEX = 2,
  // Search maximum 8-points in the radius grid around center,
  // up to 11 search stages. First stage consists of 4 search
  // points and the rest with 8 search points each.
  BIGDIA = 3,
  // Search 8-points in the square grid around center, up to 11 search stages.
  SQUARE = 4,
  // HEX search with up to 2 stages.
  FAST_HEX = 5,
  // BIGDIA search with up to 2 stages.
  FAST_DIAMOND = 6,
  // BIGDIA search with up to 3 stages.
  FAST_BIGDIA = 7,
  // Total number of search methods.
  NUM_SEARCH_METHODS,
  // Number of distinct search methods.
  NUM_DISTINCT_SEARCH_METHODS = SQUARE + 1,
} UENUM1BYTE(SEARCH_METHODS);

typedef struct warp_search_config {
  int num_neighbors;
  MV neighbors[MAX_WARP_SEARCH_NEIGHBORS];
  // Bitmask which is used to prune the search neighbors at one iteration
  // based on which direction we chose in the previous iteration.
  // See comments in av2_refine_warped_mv for details.
  uint8_t neighbor_mask[MAX_WARP_SEARCH_NEIGHBORS];
} warp_search_config;

// Methods for refining WARP_CAUSAL motion vectors
enum {
  // Search 4 adjacent points in a diamond shape at each iteration
  WARP_SEARCH_DIAMOND,
  // Search 8 adjacent points in a square at each iteration
  WARP_SEARCH_SQUARE,
  WARP_SEARCH_METHODS
} UENUM1BYTE(WARP_SEARCH_METHOD);

// This struct holds fullpixel motion search parameters that should be constant
// during the search
typedef struct {
  BLOCK_SIZE bsize;
  // A function pointer to the simd function for fast computation
  const avm_variance_fn_ptr_t *vfp;

  const MACROBLOCKD *xd;
  int mib_size_log2;
  const AV2_COMMON *cm;
  int mi_row;
  int mi_col;
  MACROBLOCK *x;
  int ref_bv_cnt;
  MSBuffers ms_buffers;

  // WARNING: search_method should be regarded as a private variable and should
  // not be modified directly so it is in sync with search_sites. To modify it,
  // use av2_set_mv_search_method.
  SEARCH_METHODS search_method;
  const search_site_config *search_sites;
  FullMvLimits mv_limits;

  int run_mesh_search;    // Sets mesh search unless it got pruned by
                          // prune_mesh_search.
  int prune_mesh_search;  // Disables mesh search if the best_mv after a normal
                          // search if close to the start_mv.
  int force_mesh_thresh;  // Forces mesh search if the residue variance is
                          // higher than the threshold.
  const struct MESH_PATTERN *mesh_patterns[2];

  // Use maximum search interval of 4 if true. This helps motion search to find
  // the best motion vector for screen content types.
  int fine_search_interval;

  int is_intra_mode;

  // For calculating mv cost
  MV_COST_PARAMS mv_cost_params;

  // Stores the function used to compute the sad. This can be different from the
  // sdf in vfp (e.g. downsampled sad and not sad) to allow speed up.
  avm_sad_fn_t sdf;
  avm_sad_multi_d_fn_t sdx4df;
} FULLPEL_MOTION_SEARCH_PARAMS;

void av2_make_default_fullpel_ms_params(
    FULLPEL_MOTION_SEARCH_PARAMS *ms_params, const struct AV2_COMP *cpi,
    const MACROBLOCK *x, BLOCK_SIZE bsize, const MV *ref_mv,
    const MvSubpelPrecision pb_mv_precision, const int is_ibc_cost,
    const search_site_config search_sites[NUM_DISTINCT_SEARCH_METHODS],
    int fine_search_interval);

// Sets up configs for fullpixel diamond search method.
void av2_init_dsmotion_compensation(search_site_config *cfg,
                                    int enable_high_motion, int stride);
// Sets up configs for firstpass motion search.
void av2_init_motion_fpf(search_site_config *cfg, int enable_high_motion,
                         int stride);
// Sets up configs for all other types of motion search method.
void av2_init_motion_compensation_nstep(search_site_config *cfg,
                                        int enable_high_motion, int stride);
// Sets up configs for BIGDIA / FAST_DIAMOND / FAST_BIGDIA
// motion search method.
void av2_init_motion_compensation_bigdia(search_site_config *cfg,
                                         int enable_high_motion, int stride);
// Sets up configs for HEX or FAST_HEX motion search method.
void av2_init_motion_compensation_hex(search_site_config *cfg,
                                      int enable_high_motion, int stride);
// Sets up configs for SQUARE motion search method.
void av2_init_motion_compensation_square(search_site_config *cfg,
                                         int enable_high_motion, int stride);

// Mv beyond the range do not produce new/different prediction block.
static INLINE void av2_set_mv_search_method(
    FULLPEL_MOTION_SEARCH_PARAMS *ms_params,
    const search_site_config search_sites[NUM_DISTINCT_SEARCH_METHODS],
    SEARCH_METHODS search_method) {
  // Array to inform which all search methods are having
  // same candidates and different in number of search steps.
  static const SEARCH_METHODS search_method_lookup[NUM_SEARCH_METHODS] = {
    DIAMOND,  // DIAMOND
    NSTEP,    // NSTEP
    HEX,      // HEX
    BIGDIA,   // BIGDIA
    SQUARE,   // SQUARE
    HEX,      // FAST_HEX
    BIGDIA,   // FAST_DIAMOND
    BIGDIA    // FAST_BIGDIA
  };

  ms_params->search_method = search_method;
  if (search_sites && search_method < NUM_SEARCH_METHODS) {
    ms_params->search_sites =
        &search_sites[search_method_lookup[ms_params->search_method]];
  }
}

// Set up limit values for MV components.
// Mv beyond the range do not produce new/different prediction block.
static INLINE void av2_set_mv_row_limits(
    const CommonModeInfoParams *const mi_params, FullMvLimits *mv_limits,
    int mi_row, int mi_height, int border) {
  const int min1 = -(mi_row * MI_SIZE + border - 2 * AVM_INTERP_EXTEND);
  const int min2 = -(((mi_row + mi_height) * MI_SIZE) + 2 * AVM_INTERP_EXTEND);
  mv_limits->row_min = AVMMAX(min1, min2);
  const int max1 = (mi_params->mi_rows - mi_row - mi_height) * MI_SIZE +
                   border - 2 * AVM_INTERP_EXTEND;
  const int max2 =
      (mi_params->mi_rows - mi_row) * MI_SIZE + 2 * AVM_INTERP_EXTEND;
  mv_limits->row_max = AVMMIN(max1, max2);
}

static INLINE void av2_set_mv_col_limits(
    const CommonModeInfoParams *const mi_params, FullMvLimits *mv_limits,
    int mi_col, int mi_width, int border) {
  const int min1 = -(mi_col * MI_SIZE + border - 2 * AVM_INTERP_EXTEND);
  const int min2 = -(((mi_col + mi_width) * MI_SIZE) + 2 * AVM_INTERP_EXTEND);
  mv_limits->col_min = AVMMAX(min1, min2);
  const int max1 = (mi_params->mi_cols - mi_col - mi_width) * MI_SIZE + border -
                   2 * AVM_INTERP_EXTEND;
  const int max2 =
      (mi_params->mi_cols - mi_col) * MI_SIZE + 2 * AVM_INTERP_EXTEND;
  mv_limits->col_max = AVMMIN(max1, max2);
}

static INLINE void av2_set_mv_limits(
    const CommonModeInfoParams *const mi_params, FullMvLimits *mv_limits,
    int mi_row, int mi_col, int mi_height, int mi_width, int border) {
  av2_set_mv_row_limits(mi_params, mv_limits, mi_row, mi_height, border);
  av2_set_mv_col_limits(mi_params, mv_limits, mi_col, mi_width, border);
}

void av2_set_mv_search_range(FullMvLimits *mv_limits, const MV *mv,
                             MvSubpelPrecision pb_mv_precision

);

#define OMVS_AVG_POOLING 1
#define OMVS_RANGE_THR 2
#define OMVS_BIG_STEP 4
#define OMVS_EARLY_TERM 1
#define OMVS_SAD_THR 8

// Obtain the bits of upshift for the MVD derived by optical flow based MV
// search. The purpose for upscaling the MVD is to increase the search range and
// obtain a new search point not covered by the traditional local search.
static INLINE int get_opfl_mv_upshift_bits(const MB_MODE_INFO *mbmi) {
  if (mbmi->mode == NEWMV || mbmi->mode == WARP_NEWMV || mbmi->mode == WARPMV)
    return 3;
  return 0;
}

int get_opfl_mv_iterations(const struct AV2_COMP *cpi,
                           const MB_MODE_INFO *mbmi);

int av2_init_search_range(int size, int enable_high_motion);

int av2_refining_search_8p_c(const FULLPEL_MOTION_SEARCH_PARAMS *ms_params,
                             const FULLPEL_MV start_mv, FULLPEL_MV *best_mv);
int av2_refining_search_8p_c_low_precision(
    const FULLPEL_MOTION_SEARCH_PARAMS *ms_params, const FULLPEL_MV start_mv,
    FULLPEL_MV *best_mv, int fast_mv_refinement);

int av2_full_pixel_search(const FULLPEL_MV start_mv,
                          const FULLPEL_MOTION_SEARCH_PARAMS *ms_params,
                          const int step_param, int *cost_list,
                          FULLPEL_MV *best_mv, FULLPEL_MV *second_best_mv);

int av2_intrabc_hash_search(const struct AV2_COMP *cpi, const MACROBLOCKD *xd,
                            const FULLPEL_MOTION_SEARCH_PARAMS *ms_params,
                            IntraBCHashInfo *intrabc_hash_info,
                            FULLPEL_MV *best_mv);

static INLINE int av2_is_fullmv_in_range(const FullMvLimits *mv_limits,
                                         FULLPEL_MV mv,
                                         MvSubpelPrecision pb_mv_precision

) {
  if (pb_mv_precision < MV_PRECISION_ONE_PEL) {
    if (mv.col & ((1 << (MV_PRECISION_ONE_PEL - pb_mv_precision)) - 1))
      return 0;
    if (mv.row & ((1 << (MV_PRECISION_ONE_PEL - pb_mv_precision)) - 1))
      return 0;
  }

  return (mv.col >= mv_limits->col_min) && (mv.col <= mv_limits->col_max) &&
         (mv.row >= mv_limits->row_min) && (mv.row <= mv_limits->row_max);
}
// =============================================================================
//  Subpixel Motion Search
// =============================================================================
enum {
  EIGHTH_PEL,
  QUARTER_PEL,
  HALF_PEL,
  FULL_PEL
} UENUM1BYTE(SUBPEL_FORCE_STOP);

typedef struct {
  const avm_variance_fn_ptr_t *vfp;
  SUBPEL_SEARCH_TYPE subpel_search_type;
  // Source and reference buffers
  MSBuffers ms_buffers;
  int w, h;
} SUBPEL_SEARCH_VAR_PARAMS;

// This struct holds subpixel motion search parameters that should be constant
// during the search
typedef struct {
  // High level motion search settings
  const int *cost_list;
  SUBPEL_FORCE_STOP forced_stop;
  int iters_per_step;
  SubpelMvLimits mv_limits;

  // For calculating mv cost
  MV_COST_PARAMS mv_cost_params;

  // Distortion calculation params
  SUBPEL_SEARCH_VAR_PARAMS var_params;
} SUBPEL_MOTION_SEARCH_PARAMS;

#define MAX_COMP_MV_STATS 128

typedef struct {
  int8_t ref_frame_type;
  int ref_mv_idx_type;
  MvSubpelPrecision mv_precision;
  int use_amvd;
  int_mv mv[2];
} NEW_NEWMV_STATS;

typedef struct {
  int8_t ref_frame_type;
  int_mv start_mv;
  int_mv ref_mv;
  MvSubpelPrecision mv_precision;
  int use_amvd;
  int_mv mv[2];
} NEAR_NEWMV_STATS;

typedef struct {
  int8_t ref_frame_type;
  int ref_mv_idx_type;
  MvSubpelPrecision mv_precision;
  int use_amvd;
  int_mv mv;
} NEW_NEARMV_STATS;

typedef struct {
  int8_t ref_frame_type;
  int_mv start_mv;
  int_mv ref_mv;
  int use_amvd;
  int_mv mv[2];
} NEAR_NEWMV_AMVD_STATS;

typedef struct {
  int8_t ref_frame_type;
  int ref_mv_idx_type;
  int_mv ref_mv[2];
  int use_amvd;
  int_mv mv[2];
} NEW_NEWMV_AMVD_STATS;
typedef struct {
  int8_t ref_frame_type;
  int ref_mv_idx_type;
  int use_amvd;
  int_mv mv;
} NEW_NEARMV_AMVD_STATS;

typedef struct {
  int8_t ref_frame_type;
  int ref_mv_idx_type;
  MvSubpelPrecision mv_precision;
  int joint_newmv_scale_idx;
  int8_t cwp_idx;
  int use_amvd;
  int_mv mv[2];
} JOINT_NEWMV_STATS;

typedef struct {
  int8_t ref_frame_type;
  int ref_mv_idx_type;
  int joint_amvd_scale_idx;
  int8_t cwp_idx;
  int use_amvd;
  int_mv mv[2];
} JOINT_AMVDNEWMV_STATS;

int opfl_refine_fullpel_mv_one_sided(
    const AV2_COMMON *cm, MACROBLOCKD *xd,
    const FULLPEL_MOTION_SEARCH_PARAMS *ms_params, MB_MODE_INFO *mbmi,
    const FULLPEL_MV *const smv, int_mv *mv_refined, BLOCK_SIZE bsize);

// motion search for joint MVD coding
int joint_mvd_search(const AV2_COMMON *const cm, MACROBLOCKD *xd,
                     SUBPEL_MOTION_SEARCH_PARAMS *ms_params, MV ref_mv,
                     MV *start_mv, MV *bestmv, int *distortion,
                     unsigned int *sse1, int ref_idx, MV *other_mv,
                     MV *best_other_mv, uint16_t *second_pred,
                     InterPredParams *inter_pred_params,
                     int_mv *last_mv_search_list);

// motion search for 2/4/8 pel precision for joint MVD coding
int low_precision_joint_mvd_search(const AV2_COMMON *const cm, MACROBLOCKD *xd,
                                   SUBPEL_MOTION_SEARCH_PARAMS *ms_params,
                                   MV ref_mv, MV *start_mv, MV *bestmv,
                                   int *distortion, unsigned int *sse1,
                                   int ref_idx, MV *other_mv, MV *best_other_mv,
                                   uint16_t *second_pred,
                                   InterPredParams *inter_pred_params);

// motion search for near_new and new_near mode when adaptive MVD resolution is
// applied
int adaptive_mvd_search(const AV2_COMMON *const cm, MACROBLOCKD *xd,
                        SUBPEL_MOTION_SEARCH_PARAMS *ms_params, MV start_mv,
                        MV *bestmv, int *distortion, unsigned int *sse1);
void av2_amvd_joint_motion_search(const struct AV2_COMP *cpi, MACROBLOCK *x,
                                  BLOCK_SIZE bsize, int_mv *cur_mv,
                                  const uint8_t *mask, int mask_stride,
                                  int *rate_mv);
int av2_joint_amvd_motion_search(const AV2_COMMON *const cm, MACROBLOCKD *xd,
                                 SUBPEL_MOTION_SEARCH_PARAMS *ms_params,
                                 const MV *start_mv, MV *bestmv,
                                 int *distortion, unsigned int *sse1,
                                 int ref_idx, MV *other_mv, MV *best_other_mv,
                                 uint16_t *second_pred,
                                 InterPredParams *inter_pred_params);

void av2_make_default_subpel_ms_params(SUBPEL_MOTION_SEARCH_PARAMS *ms_params,
                                       const struct AV2_COMP *cpi,
                                       const MACROBLOCK *x, BLOCK_SIZE bsize,
                                       const MV *ref_mv,
                                       const MvSubpelPrecision pb_mv_precision,
                                       const int is_ibc_cost,
                                       const int *cost_list);

typedef int(fractional_mv_step_fp)(MACROBLOCKD *xd, const AV2_COMMON *const cm,
                                   const SUBPEL_MOTION_SEARCH_PARAMS *ms_params,
                                   MV start_mv, MV *bestmv, int *distortion,
                                   unsigned int *sse1,
                                   int_mv *last_mv_search_list);

extern fractional_mv_step_fp av2_find_best_sub_pixel_tree;
extern fractional_mv_step_fp av2_find_best_sub_pixel_tree_pruned;
extern fractional_mv_step_fp av2_find_best_sub_pixel_tree_pruned_more;
extern fractional_mv_step_fp av2_find_best_sub_pixel_tree_pruned_evenmore;
extern fractional_mv_step_fp av2_return_max_sub_pixel_mv;
extern fractional_mv_step_fp av2_return_min_sub_pixel_mv;

int upsampled_pref_error(MACROBLOCKD *xd, const AV2_COMMON *cm,
                         const MV *this_mv,
                         const SUBPEL_SEARCH_VAR_PARAMS *var_params,
                         unsigned int *sse);
int av2_find_best_sub_pixel_intraBC_dv(
    MACROBLOCKD *xd, const AV2_COMMON *const cm,
    const SUBPEL_MOTION_SEARCH_PARAMS *ms_params, MV start_mv, MV *bestmv,
    int *distortion, unsigned int *sse1, FullMvLimits *full_pel_mv_limits,
    BLOCK_SIZE bsize);
// Refinement of MVs
int av2_refine_low_precision_intraBC_dv(
    MACROBLOCKD *xd, const AV2_COMMON *const cm,
    const SUBPEL_MOTION_SEARCH_PARAMS *ms_params, MV start_mv, MV *bestmv,
    int *distortion, unsigned int *sse1, FullMvLimits *full_pel_mv_limits,
    BLOCK_SIZE bsize);

// Struct to store coding info for fast warp search
typedef struct {
  WarpedMotionParams prev_wm_params;
  int is_valid;
  int step_size;
  MB_MODE_INFO mbmi_stats;
} warp_mode_info;

typedef struct {
  warp_mode_info warp_param_info[WARP_STATS_BUFFER_SIZE];
  int model_count;
  int model_start_idx;
} warp_mode_info_array;

void reset_warp_stats_buffer(warp_mode_info_array *warp_stats);
void update_warp_stats_buffer(const warp_mode_info *const this_warp_stats,
                              warp_mode_info_array *warp_stats);

unsigned int av2_refine_warped_mv(MACROBLOCKD *xd, const AV2_COMMON *const cm,
                                  const SUBPEL_MOTION_SEARCH_PARAMS *ms_params,
                                  BLOCK_SIZE bsize, const int *pts0,
                                  const int *pts_inref0, int total_samples,
                                  int8_t ref, WARP_SEARCH_METHOD search_method,
                                  int num_iterations);
uint8_t need_mv_adjustment(MACROBLOCKD *xd, const AV2_COMMON *const cm,
                           MACROBLOCK *const x, MB_MODE_INFO *mbmi,
                           BLOCK_SIZE bsize, MV *mv_diffs, MV *ref_mvs,
                           MvSubpelPrecision pb_mv_precision,
                           int *num_signaled_mvd, int *start_signaled_mvd_idx,
                           int *num_nonzero_mvd);

// Returns 1 if able to select a good model, 0 if not
int av2_pick_warp_delta(const AV2_COMMON *const cm, MACROBLOCKD *xd,
                        MB_MODE_INFO *mbmi,
                        const SUBPEL_MOTION_SEARCH_PARAMS *ms_params,
                        const ModeCosts *mode_costs,
                        warp_mode_info_array *prev_best_models,
                        WARP_CANDIDATE *warp_param_stack);

int av2_refine_mv_for_base_param_warp_model(
    const AV2_COMMON *const cm, MACROBLOCKD *xd, MB_MODE_INFO *mbmi,
    const MB_MODE_INFO_EXT *mbmi_ext,
    const SUBPEL_MOTION_SEARCH_PARAMS *ms_params,
    WARP_SEARCH_METHOD search_method, int num_iterations);

void av2_refine_mv_for_warp_extend(const AV2_COMMON *cm, MACROBLOCKD *xd,
                                   const SUBPEL_MOTION_SEARCH_PARAMS *ms_params,
                                   bool neighbor_is_above, BLOCK_SIZE bsize,
                                   const WarpedMotionParams *neighbor_params,
                                   WARP_SEARCH_METHOD search_method,
                                   int num_iterations);

static INLINE void av2_set_fractional_mv(int_mv *fractional_best_mv) {
  for (int z = 0; z < 3; z++) {
    fractional_best_mv[z].as_int = INVALID_MV;
  }
}

static INLINE int is_valid_sign_mvd_single(const MV mvd,
                                           MvSubpelPrecision precision,
                                           const int is_adaptive_mvd,
                                           int th_for_num_nonzero) {
  (void)is_adaptive_mvd;
  int num_nonzero_mvd_comp = (mvd.row != 0) + (mvd.col != 0);
  if (num_nonzero_mvd_comp < th_for_num_nonzero) return 1;
  int precision_shift = MV_PRECISION_ONE_EIGHTH_PEL - precision;
  int last_sign = -1;
  int sum_mvd = 0;
  for (int comp = 0; comp < 2; comp++) {
    int this_mvd_comp = comp == 0 ? mvd.row : mvd.col;
    if (abs(this_mvd_comp) > MV_MAX) return 0;
    if (this_mvd_comp) {
      last_sign = (this_mvd_comp < 0);
      sum_mvd += (abs(this_mvd_comp) >> precision_shift);
    }
  }

  return (last_sign == (sum_mvd & 0x1));
}

// This function convert the mv value to the target precision
static INLINE int av2_lower_mv_limit(const int mv, const int shift) {
  int out = ((abs(mv) >> shift) << shift);
  return out * (mv < 0 ? -1 : 1);
}

static INLINE void av2_set_subpel_mv_search_range(
    SubpelMvLimits *subpel_limits, const FullMvLimits *mv_limits,
    const MV *ref_mv

    ,
    MvSubpelPrecision pb_mv_precision

) {
  //  We have to make sure the generated mv_limits
  //  are compatible with target precision.
  MV low_prec_ref_mv = *ref_mv;
  if (pb_mv_precision < MV_PRECISION_HALF_PEL)
    lower_mv_precision(&low_prec_ref_mv, pb_mv_precision);
  // sub_pel_prec_shift is the number of LSBs need to be 0 to make the
  // mv/mv_limit compatible
  const int sub_pel_prec_shift =
      (MV_PRECISION_ONE_EIGHTH_PEL - pb_mv_precision);

  const int max_mv =
      av2_lower_mv_limit(GET_MV_SUBPEL(MAX_FULL_PEL_VAL), sub_pel_prec_shift);

  int col_min =
      av2_lower_mv_limit(GET_MV_SUBPEL(mv_limits->col_min), sub_pel_prec_shift);
  int col_max =
      av2_lower_mv_limit(GET_MV_SUBPEL(mv_limits->col_max), sub_pel_prec_shift);
  int row_min =
      av2_lower_mv_limit(GET_MV_SUBPEL(mv_limits->row_min), sub_pel_prec_shift);
  int row_max =
      av2_lower_mv_limit(GET_MV_SUBPEL(mv_limits->row_max), sub_pel_prec_shift);

  int minc = AVMMAX(col_min, low_prec_ref_mv.col - max_mv);
  int maxc = AVMMIN(col_max, low_prec_ref_mv.col + max_mv);
  int minr = AVMMAX(row_min, low_prec_ref_mv.row - max_mv);
  int maxr = AVMMIN(row_max, low_prec_ref_mv.row + max_mv);

  maxc = AVMMAX(minc, maxc);
  maxr = AVMMAX(minr, maxr);

  subpel_limits->col_min = AVMMAX(MV_LOW + (1 << sub_pel_prec_shift), minc);
  subpel_limits->col_max = AVMMIN(MV_UPP - (1 << sub_pel_prec_shift), maxc);
  subpel_limits->row_min = AVMMAX(MV_LOW + (1 << sub_pel_prec_shift), minr);
  subpel_limits->row_max = AVMMIN(MV_UPP - (1 << sub_pel_prec_shift), maxr);
}

static INLINE int av2_is_subpelmv_in_range(const SubpelMvLimits *mv_limits,
                                           MV mv) {
  return (mv.col >= mv_limits->col_min) && (mv.col <= mv_limits->col_max) &&
         (mv.row >= mv_limits->row_min) && (mv.row <= mv_limits->row_max);
}

void get_default_ref_bv(int_mv *cur_ref_bv,
                        const FULLPEL_MOTION_SEARCH_PARAMS *fullms_params);
static INLINE void init_mv_cost_params(MV_COST_PARAMS *mv_cost_params,
                                       const MvCosts *mv_costs,
                                       int is_adaptive_mvd, const MV *ref_mv,
                                       MvSubpelPrecision pb_mv_precision,
                                       const int is_ibc_cost) {
  mv_cost_params->ref_mv = ref_mv;
  mv_cost_params->full_ref_mv = get_fullmv_from_mv(ref_mv);
  mv_cost_params->mv_cost_type = MV_COST_ENTROPY;

  mv_cost_params->mv_costs = mv_costs;
  mv_cost_params->pb_mv_precision = pb_mv_precision;

  mv_cost_params->is_adaptive_mvd = is_adaptive_mvd;

  mv_cost_params->is_ibc_cost = is_ibc_cost;
}

// Check if the MV is valid for IBC mode
static INLINE int is_sub_pel_bv_valid(const MV dv, const AV2_COMMON *cm,
                                      const MACROBLOCKD *xd, int mi_row,
                                      int mi_col, BLOCK_SIZE bsize,
                                      const SubpelMvLimits *sub_pel_mv_limits,
                                      const FullMvLimits *full_pel_mv_limits,
                                      MvSubpelPrecision pb_mv_precision) {
  return av2_is_fullmv_in_range(full_pel_mv_limits, get_fullmv_from_mv(&dv),
                                pb_mv_precision) &&
         av2_is_subpelmv_in_range(sub_pel_mv_limits, dv) &&
         av2_is_dv_valid(dv, cm, xd, mi_row, mi_col, bsize, cm->mib_size_log2);
}

// Returns the cost for signaling the index of compound weighted prediction
int av2_get_cwp_idx_cost(int8_t cwp_idx, const AV2_COMMON *const cm,
                         const MACROBLOCK *x);

// Returns the cost of using the current mv during the motion search
int av2_get_mv_err_cost(const MV *mv, const MV_COST_PARAMS *mv_cost_params);

// Set the reference MV for the motion search
void av2_init_ref_mv(MV_COST_PARAMS *mv_cost_params, const MV *ref_mv);

// Compute the cost for signalling the intrabc DRL index
int av2_get_intrabc_drl_idx_cost(int max_ref_bv_num, int intrabc_drl_idx);

// Compute the cost for signalling the intrabc mode and intrabc DRL index. This
// is only used during the motion search
int av2_get_ref_bv_rate_cost(int intrabc_mode, int intrabc_drl_idx,
                             int max_bvp_drl_bits, MACROBLOCK *x,
                             int errorperbit, int ref_bv_cnt);

// Pick the best reference BV for the current BV
int av2_pick_ref_bv(FULLPEL_MV *best_full_mv, int max_bvp_drl_bits,
                    const FULLPEL_MOTION_SEARCH_PARAMS *fullms_params);

#ifdef __cplusplus
}  // extern "C"
#endif

#endif  // AVM_AV2_ENCODER_MCOMP_H_
