/*
 * Copyright (c) 2026, 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_DECODER_ANNEX_F_H_
#define AVM_AV2_DECODER_ANNEX_F_H_

#include "av2/common/enums.h"
#include "av2/common/obu_util.h"

#ifdef __cplusplus
extern "C" {
#endif

#define ANNEX_F_INVALID (-1)

// Sub-bitstream extraction state, implementing Annex F of the AV2 spec.
// This state is maintained as OBUs are parsed,
// and used to filter OBUs during the decoding process.
typedef struct SubBitstreamExtractionState {
  // ---- Phase 1 outputs (Annex F Section: F.3.1) ----

  // retentionMap[xlayer_id][mlayer_id][tlayer_id] -> 0 (discard) or 1 (retain)
  int retention_map[MAX_NUM_XLAYERS][MAX_NUM_MLAYERS][MAX_NUM_TLAYERS];

  // xLayerIsSelected[xlayer_id] -> 0 or 1
  int xlayer_is_selected[MAX_NUM_XLAYERS];

  // Per-xlayer operational parameters from selected operating point
  int profile_idc[MAX_NUM_XLAYERS];
  int level_idc[MAX_NUM_XLAYERS];
  int tier_idc[MAX_NUM_XLAYERS];
  int mlayer_cnt[MAX_NUM_XLAYERS];

  // ---- Construction state ----

  // Whether sub-bitstream extraction is enabled (user selected an OP)
  int extraction_enabled;

  // Bitstream type determination (Step 2)
  int bitstream_type_determined;
  int is_multistream;  // 0 = singlestream, 1 = multistream

  // Whether the retention map has been fully constructed for current CVS
  int retention_map_ready;

  // Global operating point selection results (Step 3)
  int global_ops_selected;
  int global_ops_id;
  int global_op_idx;

  // Local operating point selection per xlayer (Step 4)
  int local_ops_selected[MAX_NUM_XLAYERS];
  int local_ops_id[MAX_NUM_XLAYERS];
  int local_op_idx[MAX_NUM_XLAYERS];

  // Extended layers present in the bitstream (from MSDO or global LCR)
  int xlayer_present[MAX_NUM_XLAYERS];
  int num_xlayers_present;
  int xlayer_ids_present[MAX_NUM_XLAYERS];

  // Track OBus
  int msdo_seen;
  int global_lcr_seen;
  int global_ops_seen;
  int local_ops_seen[MAX_NUM_XLAYERS];

  // ---- Statistics ----
  int64_t obus_removed;
  int64_t obus_retained;
} SubBitstreamExtractionState;

struct AV2Decoder;

// clang-format off
// Operating point selection and analysis process (Annex F, Section F.3.1):
//
// Spec Step | Description                             | Function
// ----------|-----------------------------------------|------------------------------------
//     1     | Initialize outputs                      | av2_sbe_init()
//     2     | Set bitstream type to multistream (MSDO) | av2_sbe_process_msdo()
//     2     | Set bitstream type to multistream (global LCR)  | av2_sbe_process_global_lcr()
//     3     | Global operating point selection        | av2_sbe_process_global_ops()
//     4     | Local OPS selection & retention map     | av2_sbe_process_local_ops()
//     4     | Retention map construction              | av2_sbe_build_retention_map()
//     5     | Extract profile/level/tier/mlayer count | av2_sbe_extract_seq_header_params()
//     6     | Return outputs                         | (implicit)
// clang-format on

// Initialize or reset the sub-bitstream extraction state.
// Called at decoder init and at each new coded video sequence.
// Does NOT reset extraction_enabled (preserved from CLI).
void av2_sbe_init(SubBitstreamExtractionState *sbe);

// Step 2: Process MSDO OBU to set bitstream type to multistream
// and record present extended layers.
void av2_sbe_process_msdo(SubBitstreamExtractionState *sbe, int num_streams,
                          const int *stream_ids);

// Step 2: Process global LCR OBU (obu_xlayer_id == 31) to determine
// multistream bitstream type and record present extended layers.
void av2_sbe_process_global_lcr(SubBitstreamExtractionState *sbe,
                                int num_xlayers, const int *xlayer_ids);

// Step 3: Process global OPS OBU (obu_xlayer_id == 31).
// If ops_id matches the user-selected OPS, populates xlayer_is_selected[]
// from ops_xlayer_map.
void av2_sbe_process_global_ops(SubBitstreamExtractionState *sbe, int ops_id,
                                int ops_cnt, int selected_ops_id,
                                int selected_op_index, int ops_xlayer_map,
                                int ops_mlayer_info_idc);

// Step 4: Process local OPS OBU (obu_xlayer_id != 31).
// Records that a local OPS was seen for this xlayer. If the retention map
// was already built, rebuilds this xlayer's entries from the now-available
// OPS data (fixes late-arriving local OPS in multistream bitstreams).
void av2_sbe_process_local_ops(SubBitstreamExtractionState *sbe,
                               struct AV2Decoder *pbi, int xlayer_id,
                               int ops_id, int ops_cnt);

// Build the retention map from collected OPS/LCR data (Steps 3-5).
// Called once before the first non-structural OBU in a temporal unit.
// pbi is needed to access ops_list for mlayer/tlayer map data.
// triggering_xlayer_id is the obu_xlayer_id of the OBU that triggered
// retention map construction (used for singlestream xlayer detection).
// Returns 1 if map was built, 0 if already ready.
int av2_sbe_build_retention_map(SubBitstreamExtractionState *sbe,
                                struct AV2Decoder *pbi,
                                int triggering_xlayer_id);

// Phase 2 core filter: determine whether an OBU should be retained
// in the sub-bitstream (Annex F F.3.2).
// Returns 1 = retain, 0 = remove.
int av2_sbe_should_retain_obu(const SubBitstreamExtractionState *sbe,
                              OBU_TYPE obu_type, int obu_xlayer_id,
                              int obu_mlayer_id, int obu_tlayer_id);

// Step 5 fallback: extract profile/level/tier from sequence header
// when no OPS or LCR provides this information.
void av2_sbe_extract_seq_header_params(SubBitstreamExtractionState *sbe,
                                       int xlayer_id, int seq_profile_idc,
                                       int seq_max_level_idx, int seq_tier,
                                       int seq_max_mlayer_cnt);

// Returns 1 if the OBU type is a structural OBU that should always
// be parsed before filtering can begin.
static INLINE int is_sbe_structural_obu(OBU_TYPE obu_type) {
  return obu_type == OBU_TEMPORAL_DELIMITER ||
         obu_type == OBU_MULTI_STREAM_DECODER_OPERATION ||
         obu_type == OBU_LAYER_CONFIGURATION_RECORD ||
         obu_type == OBU_ATLAS_SEGMENT || obu_type == OBU_OPERATING_POINT_SET ||
         obu_type == OBU_BUFFER_REMOVAL_TIMING ||
         obu_type == OBU_METADATA_SHORT || obu_type == OBU_METADATA_GROUP ||
         obu_type == OBU_PADDING;
}

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

#endif  // AVM_AV2_DECODER_ANNEX_F_H_
