/*
 * Copyright (c) 2023, 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 www.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
 * www.aomedia.org/license/patent.
 */
#include "iamf/obu/audio_frame.h"

#include <cstdint>
#include <vector>

#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/types/span.h"
#include "iamf/common/read_bit_buffer.h"
#include "iamf/common/utils/macros.h"
#include "iamf/common/write_bit_buffer.h"
#include "iamf/obu/obu_base.h"
#include "iamf/obu/obu_header.h"
#include "iamf/obu/types.h"

namespace iamf_tools {

namespace {

ObuType GetObuType(uint32_t substream_id) {
  constexpr int kMaxImplicitAudioFrameID =
      kObuIaAudioFrameId17 - kObuIaAudioFrameId0;
  if (substream_id > kMaxImplicitAudioFrameID) {
    return kObuIaAudioFrame;
  }

  return static_cast<ObuType>(static_cast<uint32_t>(kObuIaAudioFrameId0) +
                              substream_id);
}

}  // namespace

AudioFrameObu::AudioFrameObu(const ObuHeader& header,
                             DecodedUleb128 substream_id,
                             absl::Span<const uint8_t> audio_frame)
    : ObuBase(header, GetObuType(substream_id)),
      audio_frame_(audio_frame.begin(), audio_frame.end()),
      audio_substream_id_(substream_id) {}

absl::StatusOr<AudioFrameObu> AudioFrameObu::CreateFromBuffer(
    const ObuHeader& header, int64_t payload_size, ReadBitBuffer& rb) {
  AudioFrameObu audio_frame_obu(header);
  RETURN_IF_NOT_OK(audio_frame_obu.ReadAndValidatePayload(payload_size, rb));
  return audio_frame_obu;
}

absl::Status AudioFrameObu::ValidateAndWritePayload(WriteBitBuffer& wb) const {
  if (header_.obu_type == kObuIaAudioFrame) {
    // The ID is explicitly in the bitstream when `kObuIaAudioFrame`. Otherwise
    // it is implied by `obu_type`.
    RETURN_IF_NOT_OK(wb.WriteUleb128(audio_substream_id_));
  }
  RETURN_IF_NOT_OK(wb.WriteUint8Span(absl::MakeConstSpan(audio_frame_)));

  return absl::OkStatus();
}

absl::Status AudioFrameObu::ReadAndValidatePayloadDerived(int64_t payload_size,
                                                          ReadBitBuffer& rb) {
  int8_t encoded_uleb128_size = 0;
  if (header_.obu_type == kObuIaAudioFrame) {
    // The ID is explicitly in the bitstream when `kObuIaAudioFrame`. Otherwise
    // it is implied by `obu_type`.
    RETURN_IF_NOT_OK(rb.ReadULeb128(audio_substream_id_, encoded_uleb128_size));
  } else {
    audio_substream_id_ = header_.obu_type - kObuIaAudioFrameId0;
  }
  if (payload_size < 0 || payload_size < encoded_uleb128_size) {
    return absl::InvalidArgumentError(absl::StrCat(
        "Less than zero bytes remaining in payload. payload_size=",
        payload_size, " encoded_uleb128_size=", encoded_uleb128_size));
  }
  audio_frame_.resize(payload_size - encoded_uleb128_size);
  return rb.ReadUint8Span(absl::MakeSpan(audio_frame_));
}

void AudioFrameObu::PrintObu() const {
  LOG(INFO) << "  audio_substream_id= " << GetSubstreamId();
  LOG(INFO) << "  // obu_trimming_status_flag= "
            << header_.obu_trimming_status_flag;
  LOG(INFO) << "  // samples_to_trim_at_end= "
            << header_.num_samples_to_trim_at_end;
  LOG(INFO) << "  // samples_to_trim_at_start= "
            << header_.num_samples_to_trim_at_start;
  LOG(INFO) << "  // size_of(audio_frame)= " << audio_frame_.size();
}

}  // namespace iamf_tools
