/*
 * 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/.
 */

#include <stdint.h>

#include "config/avm_config.h"
#include "config/avm_scale_rtcd.h"

#include "avm/avm_codec.h"
#include "avm/avm_encoder.h"

#include "avm_ports/system_state.h"

#include "av2/common/av2_common_int.h"

#include "av2/encoder/encoder.h"
#include "av2/encoder/firstpass.h"
#include "av2/encoder/gop_structure.h"
#include "av2/encoder/subgop.h"

// Set parameters for frames between 'start' and 'end' (excluding both).
static void set_multi_layer_params(const TWO_PASS *twopass,
                                   GF_GROUP *const gf_group, RATE_CONTROL *rc,
                                   FRAME_INFO *frame_info, int start, int end,
                                   int *cur_frame_idx, int *frame_ind,
                                   int layer_depth, int num_mlayers) {
  const int num_frames_to_process = end - start;

  // Either we are at the last level of the pyramid, or we don't have enough
  // frames between 'l' and 'r' to create one more level.
  if (layer_depth > gf_group->max_layer_depth_allowed ||
      num_frames_to_process < 3) {
    // Leaf nodes.
    while (start < end) {
      gf_group->update_type[*frame_ind] = LF_UPDATE;
      gf_group->arf_src_offset[*frame_ind] = 0;
      gf_group->cur_frame_idx[*frame_ind] = *cur_frame_idx;
      gf_group->layer_depth[*frame_ind] = MAX_ARF_LAYERS;
      gf_group->arf_boost[*frame_ind] = av2_calc_arf_boost(
          twopass, rc, frame_info, start, end - start, 0, NULL, NULL);
      gf_group->max_layer_depth =
          AVMMAX(gf_group->max_layer_depth, layer_depth);
      ++(*frame_ind);
      ++(*cur_frame_idx);
      ++start;
    }
  } else {
    const int m = (start + end - 1) / 2;

    // Internal ARF.
    gf_group->update_type[*frame_ind] = INTNL_ARF_UPDATE;
    gf_group->arf_src_offset[*frame_ind] = num_mlayers * (m - start);
    gf_group->cur_frame_idx[*frame_ind] = *cur_frame_idx;
    gf_group->layer_depth[*frame_ind] = layer_depth;

    // Get the boost factor for intermediate ARF frames.
    gf_group->arf_boost[*frame_ind] = av2_calc_arf_boost(
        twopass, rc, frame_info, m, end - m, m - start, NULL, NULL);
    ++(*frame_ind);

    // Frames displayed before this internal ARF.
    set_multi_layer_params(twopass, gf_group, rc, frame_info, start, m,
                           cur_frame_idx, frame_ind, layer_depth + 1,
                           num_mlayers);

    // Overlay for internal ARF.
    gf_group->update_type[*frame_ind] = INTNL_OVERLAY_UPDATE;
    gf_group->arf_src_offset[*frame_ind] = 0;
    gf_group->cur_frame_idx[*frame_ind] = *cur_frame_idx;
    gf_group->arf_boost[*frame_ind] = 0;
    gf_group->layer_depth[*frame_ind] = layer_depth;
    ++(*frame_ind);
    ++(*cur_frame_idx);

    // Frames displayed after this internal ARF.
    set_multi_layer_params(twopass, gf_group, rc, frame_info, m + 1, end,
                           cur_frame_idx, frame_ind, layer_depth + 1,
                           num_mlayers);
  }
}

static int find_backward_alt_ref(const SubGOPCfg *subgop_cfg, int cur_idx) {
  int cur_disp_idx = subgop_cfg->step[cur_idx].disp_frame_idx;
  int cur_pyr_level = subgop_cfg->step[cur_idx].pyr_level;
  int bwd_alt_ref_disp_idx = 0;

  for (int i = 0; i < cur_idx; ++i) {
    int disp_idx = subgop_cfg->step[i].disp_frame_idx;
    int pyr_level = subgop_cfg->step[i].pyr_level;
    if (pyr_level < cur_pyr_level && disp_idx < cur_disp_idx) {
      if (disp_idx > bwd_alt_ref_disp_idx) bwd_alt_ref_disp_idx = disp_idx;
    }
  }

  return bwd_alt_ref_disp_idx;
}

static int find_forward_alt_ref(const SubGOPCfg *subgop_cfg, int cur_idx) {
  int cur_disp_idx = subgop_cfg->step[cur_idx].disp_frame_idx;
  int cur_pyr_level = subgop_cfg->step[cur_idx].pyr_level;
  int fwd_alt_ref_disp_idx = subgop_cfg->num_frames;

  for (int i = 0; i < cur_idx; ++i) {
    int disp_idx = subgop_cfg->step[i].disp_frame_idx;
    int pyr_level = subgop_cfg->step[i].pyr_level;
    if (pyr_level < cur_pyr_level && disp_idx > cur_disp_idx) {
      if (disp_idx < fwd_alt_ref_disp_idx) fwd_alt_ref_disp_idx = disp_idx;
    }
  }

  return fwd_alt_ref_disp_idx;
}

static void set_multi_layer_params_from_subgop_cfg(
    const TWO_PASS *twopass, GF_GROUP *const gf_group,
    const SubGOPCfg *subgop_cfg, RATE_CONTROL *rc, FRAME_INFO *frame_info,
    int *cur_frame_idx, int *frame_index, int is_ld_map_first_gop) {
  int last_shown_frame = 0;
  int min_pyr_level = MAX_ARF_LAYERS;

  for (int idx = 0; idx < subgop_cfg->num_steps; ++idx) {
    const SubGOPStepCfg *frame = &subgop_cfg->step[idx];

    if (frame->pyr_level < min_pyr_level) min_pyr_level = frame->pyr_level;
  }

  for (int idx = 0; idx < subgop_cfg->num_steps; ++idx) {
    const SubGOPStepCfg *frame = &subgop_cfg->step[idx];
    FRAME_TYPE_CODE type = frame->type_code;
    int pyr_level = frame->pyr_level;
    int disp_idx = frame->disp_frame_idx;
    gf_group->cur_frame_idx[*frame_index] = *cur_frame_idx;

    if (type == FRAME_TYPE_OOO_FILTERED ||
        type == FRAME_TYPE_OOO_UNFILTERED) {  // ARF
      gf_group->update_type[*frame_index] =
          pyr_level == min_pyr_level ? ARF_UPDATE : INTNL_ARF_UPDATE;
      if (pyr_level == min_pyr_level) {
        gf_group->arf_index = *frame_index;
        gf_group->arf_boost[*frame_index] = rc->gfu_boost;
      } else {
        int fwd_arf_disp_idx = find_forward_alt_ref(subgop_cfg, idx);
        int bwd_arf_disp_idx = find_backward_alt_ref(subgop_cfg, idx);
        gf_group->arf_boost[*frame_index] = av2_calc_arf_boost(
            twopass, rc, frame_info, disp_idx, fwd_arf_disp_idx - disp_idx,
            disp_idx - bwd_arf_disp_idx, NULL, NULL);
      }
      gf_group->arf_src_offset[*frame_index] = disp_idx - last_shown_frame - 1;
    } else if (type == FRAME_TYPE_INO_VISIBLE) {  // Leaf
      gf_group->update_type[*frame_index] =
          (pyr_level == min_pyr_level && !is_ld_map_first_gop) ? GF_UPDATE
                                                               : LF_UPDATE;

      int fwd_arf_disp_idx = find_forward_alt_ref(subgop_cfg, idx);
      gf_group->arf_boost[*frame_index] =
          av2_calc_arf_boost(twopass, rc, frame_info, disp_idx,
                             fwd_arf_disp_idx - disp_idx, 0, NULL, NULL);
      gf_group->arf_src_offset[*frame_index] = 0;
      last_shown_frame = disp_idx;

      (*cur_frame_idx)++;
    } else if (type == FRAME_TYPE_INO_REPEAT ||
               type == FRAME_TYPE_INO_SHOWEXISTING) {  // Overlay
      gf_group->update_type[*frame_index] =
          pyr_level == min_pyr_level ? OVERLAY_UPDATE : INTNL_OVERLAY_UPDATE;
      gf_group->arf_boost[*frame_index] = 0;
      gf_group->arf_src_offset[*frame_index] = 0;
      last_shown_frame = disp_idx;
      (*cur_frame_idx)++;
    }
    gf_group->is_filtered[*frame_index] = (type == FRAME_TYPE_OOO_FILTERED);
    gf_group->layer_depth[*frame_index] = pyr_level;
    gf_group->max_layer_depth = AVMMAX(gf_group->max_layer_depth, pyr_level);

    if (idx != (subgop_cfg->num_steps - 1)) (*frame_index)++;
  }
  for (int idx = 0; idx <= *frame_index; idx++) {
    if (gf_group->layer_depth[idx] == gf_group->max_layer_depth)
      gf_group->layer_depth[idx] = MAX_ARF_LAYERS;
  }
}

static const SubGOPCfg *get_subgop_config(SubGOPSetCfg *config_set,
                                          int num_frames, int is_last_subgop,
                                          int is_first_subgop, int use_alt_ref,
                                          int *is_ld_map) {
  const SubGOPCfg *cfg = av2_find_subgop_config(
      config_set, num_frames, is_last_subgop, is_first_subgop);
  if (!cfg) {
    *is_ld_map = 0;
    return NULL;
  }

  // Check if the configuration map is low-delay
  for (int idx = 0; idx < cfg->num_steps; ++idx) {
    const SubGOPStepCfg *frame = &cfg->step[idx];
    FRAME_TYPE_CODE type = frame->type_code;
    if (type == FRAME_TYPE_OOO_FILTERED || type == FRAME_TYPE_OOO_UNFILTERED) {
      *is_ld_map = 0;
      break;
    }
  }

  /*
   * Enable subgop configuration path only when altrefs are
   * enabled for non low-delay configurations and for low-delay
   * configurations.
   */
  if (cfg && (use_alt_ref || *is_ld_map))
    return cfg;
  else
    return NULL;
}

static int construct_multi_layer_gf_structure(
    AV2_COMP *cpi, TWO_PASS *twopass, GF_GROUP *const gf_group,
    RATE_CONTROL *rc, FRAME_INFO *const frame_info, int gf_interval,
    FRAME_UPDATE_TYPE first_frame_update_type) {
  int frame_index = 0;
  int cur_frame_index = 0;

  assert(first_frame_update_type == KF_UPDATE ||
         first_frame_update_type == ARF_UPDATE ||
         first_frame_update_type == GF_UPDATE);
  const int use_altref = gf_group->max_layer_depth_allowed > 0;
  SubGOPSetCfg *subgop_cfg_set = &cpi->subgop_config_set;
  gf_group->subgop_cfg = NULL;
  gf_group->is_user_specified = 0;
  const SubGOPCfg *subgop_cfg;
  int is_ld_map = 1;

  subgop_cfg = get_subgop_config(
      subgop_cfg_set, gf_interval, rc->frames_to_key <= gf_interval + 2,
      first_frame_update_type == KF_UPDATE, use_altref, &is_ld_map);
  int is_ld_map_first_gop = is_ld_map && first_frame_update_type == KF_UPDATE;

  if (first_frame_update_type == KF_UPDATE && !cpi->olk_encountered &&
      cpi->oxcf.kf_cfg.enable_keyframe_filtering > 1 &&
      has_enough_frames_for_key_filtering(cpi->rc.frames_to_key,
                                          cpi->oxcf.algo_cfg.arnr_max_frames,
                                          cpi->oxcf.gf_cfg.lag_in_frames)) {
    // Note: for OLK, we treat it as ARF in the gf group, so will not enter this
    // if condition.
    gf_group->has_overlay_for_key_frame = 1;
    gf_group->update_type[frame_index] = KFFLT_UPDATE;
    gf_group->arf_src_offset[frame_index] = 0;
    gf_group->cur_frame_idx[frame_index] = cur_frame_index;
    gf_group->layer_depth[frame_index] = 0;
    gf_group->max_layer_depth = 0;
    ++frame_index;

    gf_group->update_type[frame_index] = KFFLT_OVERLAY_UPDATE;
    gf_group->arf_src_offset[frame_index] = 0;
    gf_group->cur_frame_idx[frame_index] = cur_frame_index;
    gf_group->layer_depth[frame_index] = MAX_ARF_LAYERS;
    gf_group->arf_boost[frame_index] = NORMAL_BOOST;
    ++frame_index;
    cur_frame_index++;
  } else if ((first_frame_update_type != ARF_UPDATE && !is_ld_map) ||
             is_ld_map_first_gop) {
    gf_group->update_type[frame_index] = first_frame_update_type;
    gf_group->arf_src_offset[frame_index] = 0;
    gf_group->cur_frame_idx[frame_index] = cur_frame_index;
    gf_group->layer_depth[frame_index] = 0;
    gf_group->max_layer_depth = 0;
    ++frame_index;
    ++cur_frame_index;
  }

  int has_hidden_fwd_kf = 0;
  int use_fwd_kf_overlay = 0;
  if (subgop_cfg) {
    gf_group->subgop_cfg = subgop_cfg;
    gf_group->is_user_specified = 1;
    set_multi_layer_params_from_subgop_cfg(twopass, gf_group, subgop_cfg, rc,
                                           frame_info, &cur_frame_index,
                                           &frame_index, is_ld_map_first_gop);
    frame_index++;
  } else {
    if (first_frame_update_type == KF_UPDATE) gf_interval++;
    // ALTREF.
    if (use_altref) {
      gf_group->update_type[frame_index] = ARF_UPDATE;
      gf_group->arf_src_offset[frame_index] =
          cpi->common.number_mlayers * (gf_interval - cur_frame_index - 1);
      gf_group->cur_frame_idx[frame_index] = cur_frame_index;
      gf_group->layer_depth[frame_index] = 1;
      gf_group->arf_boost[frame_index] = cpi->rc.gfu_boost;
      gf_group->max_layer_depth = 1;
      gf_group->arf_index = frame_index;

      // If the first frame is a CLK, then we need to count it here since it is
      // not counted in the frames_to_key yet.
      if (gf_group->arf_src_offset[frame_index] ==
              (cpi->rc.frames_to_key -
               (first_frame_update_type == KF_UPDATE ? 1 : 0)) *
                  (int)cpi->common.number_mlayers &&
          gf_group->arf_src_offset[frame_index] != 0 &&
          cpi->oxcf.kf_cfg.fwd_kf_enabled &&
          (cpi->oxcf.kf_cfg.enable_keyframe_filtering > 1 ||
           cpi->oxcf.ref_frm_cfg.add_sef_for_hidden_frames)) {
        // This is a no-show fw kf, and we either need to send an overlay or an
        // SEF obu for it.
        has_hidden_fwd_kf = 1;
        if (cpi->oxcf.kf_cfg.enable_keyframe_filtering > 1) {
          use_fwd_kf_overlay = 1;
        }
      }

      ++frame_index;
    } else {
      gf_group->arf_index = -1;
    }

    if (has_hidden_fwd_kf) {
      gf_group->update_type[frame_index] =
          use_fwd_kf_overlay ? FWD_KF_OVERLAY_UPDATE : FWD_KF_SUCCESSOR_UPDATE;
      gf_group->arf_src_offset[frame_index] =
          cpi->common.number_mlayers *
          ((use_fwd_kf_overlay ? 0 : 1) + gf_interval - cur_frame_index - 1);
      gf_group->cur_frame_idx[frame_index] = cur_frame_index;
      gf_group->layer_depth[frame_index] = MAX_ARF_LAYERS;
      gf_group->arf_boost[frame_index] = NORMAL_BOOST;
      ++frame_index;
    }

    set_multi_layer_params(twopass, gf_group, rc, frame_info, cur_frame_index,
                           gf_interval - 1, &cur_frame_index, &frame_index,
                           use_altref + 1, cpi->common.number_mlayers);
    if (has_hidden_fwd_kf) {
      // If use_fwd_kf_overlay = 1, then this added overlay is for the implicit
      // output of that kf overlay.  If use_fwd_kf_overlay = 0, then we need an
      // SEF for the fwd kf. This is for that SEF.
      gf_group->update_type[frame_index] = OVERLAY_UPDATE;
      gf_group->arf_src_offset[frame_index] = 0;
      gf_group->cur_frame_idx[frame_index] = cur_frame_index;
      gf_group->layer_depth[frame_index] = MAX_ARF_LAYERS;
      gf_group->arf_boost[frame_index] = NORMAL_BOOST;
      ++frame_index;
      ++cur_frame_index;
      if (!use_fwd_kf_overlay) {
        // Add one more for the regular frame after the fwd kf sef (implicit
        // output).
        gf_group->update_type[frame_index] = OVERLAY_UPDATE;
        gf_group->arf_src_offset[frame_index] = 0;
        gf_group->cur_frame_idx[frame_index] = cur_frame_index;
        gf_group->layer_depth[frame_index] = MAX_ARF_LAYERS;
        gf_group->arf_boost[frame_index] = NORMAL_BOOST;
        ++frame_index;
        ++cur_frame_index;
      }
    } else if (use_altref) {
      gf_group->update_type[frame_index] = OVERLAY_UPDATE;
      gf_group->arf_src_offset[frame_index] = 0;
      gf_group->cur_frame_idx[frame_index] = cur_frame_index;
      gf_group->layer_depth[frame_index] = MAX_ARF_LAYERS;
      gf_group->arf_boost[frame_index] = NORMAL_BOOST;
      ++frame_index;
      ++cur_frame_index;
    } else {
      for (; cur_frame_index < gf_interval; ++cur_frame_index) {
        gf_group->update_type[frame_index] = LF_UPDATE;
        gf_group->arf_src_offset[frame_index] = 0;
        gf_group->cur_frame_idx[frame_index] = cur_frame_index;
        gf_group->layer_depth[frame_index] = MAX_ARF_LAYERS;
        gf_group->arf_boost[frame_index] = NORMAL_BOOST;
        gf_group->max_layer_depth = AVMMAX(gf_group->max_layer_depth, 2);
        ++frame_index;
      }
    }
  }

  return frame_index;
}

#define CHECK_GF_PARAMETER 0
#if CHECK_GF_PARAMETER
void check_frame_params(GF_GROUP *const gf_group, int gf_interval) {
  static const char *update_type_strings[FRAME_UPDATE_TYPES] = {
    "KF_UPDATE",        "LF_UPDATE",      "GF_UPDATE",
    "ARF_UPDATE",       "OVERLAY_UPDATE", "INTNL_OVERLAY_UPDATE",
    "INTNL_ARF_UPDATE", "KFFLT_UPDATE",   "KFFLT_OVERLAY_UPDATE",
  };
  FILE *fid = fopen("GF_PARAMS.txt", "a");

  fprintf(fid, "\ngf_interval = {%d}\n", gf_interval);
  for (int i = 0; i < gf_group->size; ++i) {
    fprintf(fid, "#%2d : %s %d %d %d %d\n", i,
            update_type_strings[gf_group->update_type[i]],
            gf_group->arf_src_offset[i], gf_group->arf_pos_in_gf[i],
            gf_group->arf_update_idx[i], gf_group->pyramid_level[i]);
  }

  fprintf(fid, "number of nodes in each level: \n");
  for (int i = 0; i < gf_group->pyramid_height; ++i) {
    fprintf(fid, "lvl %d: %d ", i, gf_group->pyramid_lvl_nodes[i]);
  }
  fprintf(fid, "\n");
  fclose(fid);
}
#endif  // CHECK_GF_PARAMETER

void av2_gop_setup_structure(AV2_COMP *cpi) {
  RATE_CONTROL *const rc = &cpi->rc;
  GF_GROUP *const gf_group = &cpi->gf_group;
  TWO_PASS *const twopass = &cpi->twopass;
  FRAME_INFO *const frame_info = &cpi->frame_info;
  const int key_frame =
      rc->frames_since_key == 0 && !cpi->gf_state.olk_overlay_last;

  const int use_altref = gf_group->max_layer_depth_allowed > 0;
  const FRAME_UPDATE_TYPE update_type = cpi->gf_state.arf_gf_boost_lst ||
                                                use_altref ||
                                                (rc->baseline_gf_interval == 1)
                                            ? ARF_UPDATE
                                            : GF_UPDATE;
  const FRAME_UPDATE_TYPE first_frame_update_type =
      key_frame ? KF_UPDATE : update_type;
  gf_group->is_user_specified = 0;
  gf_group->has_overlay_for_key_frame = 0;
  if (cpi->print_per_frame_stats) {
    printf("baseline_gf_interval = %d\n", rc->baseline_gf_interval);
  }
  gf_group->size = construct_multi_layer_gf_structure(
      cpi, twopass, gf_group, rc, frame_info, rc->baseline_gf_interval,
      first_frame_update_type);
  cpi->rc.level1_qp = -1;  // Set to uninitialized.

#if CHECK_GF_PARAMETER
  check_frame_params(gf_group, rc->baseline_gf_interval);
#endif
}
