#pragma once

#ifdef SMARTCUT_HAS_FFMPEG
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavcodec/bsf.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
}

#include <filesystem>
#include <memory>

namespace smartcut::media::ffmpeg {

struct FormatInputDeleter { void operator()(AVFormatContext* context) const { avformat_close_input(&context); } };
struct CodecContextDeleter { void operator()(AVCodecContext* context) const { avcodec_free_context(&context); } };
struct PacketDeleter { void operator()(AVPacket* packet) const { av_packet_free(&packet); } };
struct FrameDeleter { void operator()(AVFrame* frame) const { av_frame_free(&frame); } };
struct BsfDeleter { void operator()(AVBSFContext* context) const { av_bsf_free(&context); } };
struct SwrDeleter { void operator()(SwrContext* context) const { swr_free(&context); } };

using FormatInput = std::unique_ptr<AVFormatContext, FormatInputDeleter>;
using CodecContext = std::unique_ptr<AVCodecContext, CodecContextDeleter>;
using Packet = std::unique_ptr<AVPacket, PacketDeleter>;
using Frame = std::unique_ptr<AVFrame, FrameDeleter>;
using Bsf = std::unique_ptr<AVBSFContext, BsfDeleter>;
using Swr = std::unique_ptr<SwrContext, SwrDeleter>;

inline FormatInput open_input(const std::filesystem::path& path) {
  AVFormatContext* context = nullptr;
  if (avformat_open_input(&context, path.string().c_str(), nullptr, nullptr) < 0) return {};
  return FormatInput(context);
}
inline Packet make_packet() { return Packet(av_packet_alloc()); }
inline Frame make_frame() { return Frame(av_frame_alloc()); }
inline CodecContext make_codec_context(const AVCodec* codec) { return CodecContext(avcodec_alloc_context3(codec)); }

inline const AVCodec* preferred_video_decoder(AVCodecID codec_id) {
  const AVCodec* codec = nullptr;
  if (codec_id == AV_CODEC_ID_AV1) codec = avcodec_find_decoder_by_name("libaom-av1");
  else if (codec_id == AV_CODEC_ID_VP9) codec = avcodec_find_decoder_by_name("libvpx-vp9");
  return codec ? codec : avcodec_find_decoder(codec_id);
}

inline const AVCodec* matching_boundary_encoder(AVCodecID codec_id) {
  if (codec_id == AV_CODEC_ID_HEVC) return avcodec_find_encoder_by_name("libx265");
  if (codec_id == AV_CODEC_ID_H264) return avcodec_find_encoder_by_name("libx264");
  if (codec_id == AV_CODEC_ID_AV1) return avcodec_find_encoder_by_name("libaom-av1");
  if (codec_id == AV_CODEC_ID_VP9) return avcodec_find_encoder_by_name("libvpx-vp9");
  return avcodec_find_encoder(codec_id);
}

}  // namespace smartcut::media::ffmpeg
#endif
