/**
 * Parse SVC encoding options
 * Format: encoding-mode=<svc_mode>,layers=<layer_count>
 *         scale-factors=<n1>/<d1>,<n2>/<d2>,...
 *         quantizers=<q1>,<q2>,...
 * svc_mode = [i|ip|alt_ip|gf]
 */
static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
  char *input_string;
  char *option_name;
  char *option_value;
  char *input_ptr = NULL;
  SvcInternal_t *const si = get_svc_internal(svc_ctx);
  vpx_codec_err_t res = VPX_CODEC_OK;
  int i, alt_ref_enabled = 0;

  if (options == NULL) return VPX_CODEC_OK;
  input_string = strdup(options);

  // parse option name
  option_name = strtok_r(input_string, "=", &input_ptr);
  while (option_name != NULL) {
    // parse option value
    option_value = strtok_r(NULL, " ", &input_ptr);
    if (option_value == NULL) {
      svc_log(svc_ctx, SVC_LOG_ERROR, "option missing value: %s\n",
              option_name);
      res = VPX_CODEC_INVALID_PARAM;
      break;
    }
    if (strcmp("spatial-layers", option_name) == 0) {
      svc_ctx->spatial_layers = atoi(option_value);
    } else if (strcmp("temporal-layers", option_name) == 0) {
      svc_ctx->temporal_layers = atoi(option_value);
    } else if (strcmp("scale-factors", option_name) == 0) {
      res = parse_layer_options_from_string(svc_ctx, SCALE_FACTOR, option_value,
                                            si->svc_params.scaling_factor_num,
                                            si->svc_params.scaling_factor_den);
      if (res != VPX_CODEC_OK) break;
    } else if (strcmp("max-quantizers", option_name) == 0) {
      res =
          parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value,
                                          si->svc_params.max_quantizers, NULL);
      if (res != VPX_CODEC_OK) break;
    } else if (strcmp("min-quantizers", option_name) == 0) {
      res =
          parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value,
                                          si->svc_params.min_quantizers, NULL);
      if (res != VPX_CODEC_OK) break;
    } else if (strcmp("auto-alt-refs", option_name) == 0) {
      res = parse_layer_options_from_string(svc_ctx, AUTO_ALT_REF, option_value,
                                            si->enable_auto_alt_ref, NULL);
      if (res != VPX_CODEC_OK) break;
    } else if (strcmp("bitrates", option_name) == 0) {
      res = parse_layer_options_from_string(svc_ctx, BITRATE, option_value,
                                            si->bitrates, NULL);
      if (res != VPX_CODEC_OK) break;
    } else if (strcmp("multi-frame-contexts", option_name) == 0) {
      si->use_multiple_frame_contexts = atoi(option_value);
    } else {
      svc_log(svc_ctx, SVC_LOG_ERROR, "invalid option: %s\n", option_name);
      res = VPX_CODEC_INVALID_PARAM;
      break;
    }
    option_name = strtok_r(NULL, "=", &input_ptr);
  }
  free(input_string);

  for (i = 0; i < svc_ctx->spatial_layers; ++i) {
    if (si->svc_params.max_quantizers[i] > MAX_QUANTIZER ||
        si->svc_params.max_quantizers[i] < 0 ||
        si->svc_params.min_quantizers[i] > si->svc_params.max_quantizers[i] ||
        si->svc_params.min_quantizers[i] < 0)
      res = VPX_CODEC_INVALID_PARAM;
  }

  if (si->use_multiple_frame_contexts &&
      (svc_ctx->spatial_layers > 3 ||
       svc_ctx->spatial_layers * svc_ctx->temporal_layers > 4))
    res = VPX_CODEC_INVALID_PARAM;

  for (i = 0; i < svc_ctx->spatial_layers; ++i)
    alt_ref_enabled += si->enable_auto_alt_ref[i];
  if (alt_ref_enabled > REF_FRAMES - svc_ctx->spatial_layers) {
    svc_log(svc_ctx, SVC_LOG_ERROR,
            "svc: auto alt ref: Maxinum %d(REF_FRAMES - layers) layers could"
            "enabled auto alt reference frame, but % layers are enabled\n",
            REF_FRAMES - svc_ctx->spatial_layers, alt_ref_enabled);
    res = VPX_CODEC_INVALID_PARAM;
  }

  return res;
}
vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
                             vpx_codec_iface_t *iface,
                             vpx_codec_enc_cfg_t *enc_cfg) {
  vpx_codec_err_t res;
  int i;
  SvcInternal *const si = get_svc_internal(svc_ctx);
  if (svc_ctx == NULL || codec_ctx == NULL || iface == NULL ||
      enc_cfg == NULL) {
    return VPX_CODEC_INVALID_PARAM;
  }
  if (si == NULL) return VPX_CODEC_MEM_ERROR;

  si->codec_ctx = codec_ctx;

  si->width = enc_cfg->g_w;
  si->height = enc_cfg->g_h;

  if (enc_cfg->kf_max_dist < 2) {
    svc_log(svc_ctx, SVC_LOG_ERROR, "key frame distance too small: %d\n",
            enc_cfg->kf_max_dist);
    return VPX_CODEC_INVALID_PARAM;
  }
  si->kf_dist = enc_cfg->kf_max_dist;

  if (svc_ctx->spatial_layers == 0)
    svc_ctx->spatial_layers = VPX_SS_DEFAULT_LAYERS;
  if (svc_ctx->spatial_layers < 1 ||
      svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS) {
    svc_log(svc_ctx, SVC_LOG_ERROR, "spatial layers: invalid value: %d\n",
            svc_ctx->spatial_layers);
    return VPX_CODEC_INVALID_PARAM;
  }

  for (i = 0; i < VPX_SS_MAX_LAYERS; ++i) {
    si->quantizer[i] = DEFAULT_QUANTIZER_VALUES[i];
    si->scaling_factor_num[i] = DEFAULT_SCALE_FACTORS_NUM[i];
    si->scaling_factor_den[i] = DEFAULT_SCALE_FACTORS_DEN[i];
  }

  if (strlen(si->quantizers) > 0) {
    res = parse_layer_options_from_string(svc_ctx, QUANTIZER, si->quantizers,
                                          si->quantizer, NULL);
    if (res != VPX_CODEC_OK)
      return res;
  }

  if (strlen(si->scale_factors) > 0) {
    res = parse_layer_options_from_string(svc_ctx, SCALE_FACTOR,
                                          si->scale_factors,
                                          si->scaling_factor_num,
                                          si->scaling_factor_den);
    if (res != VPX_CODEC_OK)
      return res;
  }

  // Parse aggregate command line options. Options must start with
  // "layers=xx" then followed by other options
  res = parse_options(svc_ctx, si->options);
  if (res != VPX_CODEC_OK) return res;

  if (svc_ctx->spatial_layers < 1)
    svc_ctx->spatial_layers = 1;
  if (svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS)
    svc_ctx->spatial_layers = VPX_SS_MAX_LAYERS;

  if (svc_ctx->temporal_layers < 1)
    svc_ctx->temporal_layers = 1;
  if (svc_ctx->temporal_layers > VPX_TS_MAX_LAYERS)
    svc_ctx->temporal_layers = VPX_TS_MAX_LAYERS;

  assign_layer_bitrates(svc_ctx, enc_cfg);

#if CONFIG_SPATIAL_SVC
  for (i = 0; i < svc_ctx->spatial_layers; ++i)
    enc_cfg->ss_enable_auto_alt_ref[i] = si->enable_auto_alt_ref[i];
#endif

  if (svc_ctx->temporal_layers > 1) {
    int i;
    for (i = 0; i < svc_ctx->temporal_layers; ++i) {
      enc_cfg->ts_target_bitrate[i] = enc_cfg->rc_target_bitrate /
                                      svc_ctx->temporal_layers;
      enc_cfg->ts_rate_decimator[i] = 1 << (svc_ctx->temporal_layers - 1 - i);
    }
  }

  // modify encoder configuration
  enc_cfg->ss_number_layers = svc_ctx->spatial_layers;
  enc_cfg->ts_number_layers = svc_ctx->temporal_layers;

  if (enc_cfg->g_error_resilient == 0 && si->use_multiple_frame_contexts == 0)
    enc_cfg->g_error_resilient = 1;

  // Initialize codec
  res = vpx_codec_enc_init(codec_ctx, iface, enc_cfg, VPX_CODEC_USE_PSNR);
  if (res != VPX_CODEC_OK) {
    svc_log(svc_ctx, SVC_LOG_ERROR, "svc_enc_init error\n");
    return res;
  }

  vpx_codec_control(codec_ctx, VP9E_SET_SVC, 1);

  return VPX_CODEC_OK;
}