/*
 * Copyright (c) 2024, 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/cli/codec/flac_decoder.h"

#include <cstdint>
#include <memory>

#include "absl/log/check.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/types/span.h"
#include "iamf/cli/codec/decoder_base.h"
#include "iamf/cli/codec/flac_decoder_stream_callbacks.h"
#include "include/FLAC/stream_decoder.h"

namespace iamf_tools {

absl::StatusOr<std::unique_ptr<DecoderBase>> FlacDecoder::Create(
    int num_channels, uint32_t num_samples_per_frame) {
  FLAC__StreamDecoder* decoder = FLAC__stream_decoder_new();
  if (decoder == nullptr) {
    return absl::InternalError("Failed to create FLAC stream decoder.");
  }

  auto flac_decoder = absl::WrapUnique(
      new FlacDecoder(num_channels, num_samples_per_frame, decoder));

  FLAC__stream_decoder_init_stream(
      decoder, flac_callbacks::LibFlacReadCallback, /*seek_callback=*/nullptr,
      /*tell_callback=*/nullptr, /*length_callback=*/nullptr,
      /*eof_callback=*/nullptr, flac_callbacks::LibFlacWriteCallback,
      /*metadata_callback=*/nullptr, flac_callbacks::LibFlacErrorCallback,
      static_cast<void*>(flac_decoder->callback_data_.get()));

  return flac_decoder;
}

FlacDecoder::~FlacDecoder() {
  // The factory function prevents `decoder_` from ever being null.
  CHECK_NE(decoder_, nullptr);
  FLAC__stream_decoder_delete(decoder_);
}

absl::Status FlacDecoder::Finalize() {
  // Signal to `libflac` the decoder is finished.
  if (!FLAC__stream_decoder_finish(decoder_)) {
    return absl::InternalError("Failed to finalize Flac stream decoder.");
  }
  return absl::OkStatus();
}

absl::Status FlacDecoder::DecodeAudioFrame(
    absl::Span<const uint8_t> encoded_frame) {
  // Set the encoded frame to be decoded; the libflac decoder will copy the
  // data using LibFlacReadCallback.
  callback_data_->SetEncodedFrame(encoded_frame);
  if (!FLAC__stream_decoder_process_single(decoder_)) {
    // More specific error information is logged in LibFlacErrorCallback.
    return absl::InternalError("Failed to decode FLAC frame.");
  }

  return absl::OkStatus();
}

}  // namespace iamf_tools
