#ifndef __CODECLIB_H__
#define __CODECLIB_H__


struct codec_def_s;
struct packet_sequencer_s;
typedef const struct codec_def_s codec_def_t;
typedef struct packet_sequencer_s packet_sequencer_t;
struct rtp_payload_type;

enum media_type {
	MT_UNKNOWN = 0,
	MT_AUDIO,
	MT_VIDEO,
	MT_IMAGE,
	MT_MESSAGE,
	MT_TEXT,
	MT_APPLICATION,
	MT_OTHER,

	__MT_MAX
};


#include "str.h"
#include "rtplib.h"

INLINE enum media_type codec_get_type(const str *type) {
	if (!type || !type->len)
		return MT_UNKNOWN;
	if (!str_cmp(type, "audio"))
		return MT_AUDIO;
	if (!str_cmp(type, "video"))
		return MT_VIDEO;
	if (!str_cmp(type, "image"))
		return MT_IMAGE;
	if (!str_cmp(type, "message"))
		return MT_MESSAGE;
	if (!str_cmp(type, "text"))
		return MT_TEXT;
	if (!str_cmp(type, "application"))
		return MT_APPLICATION;
	return MT_OTHER;
}


// 0:  exact match
// 1:  a is compatible with b (not necessarily the other way around)
// -1: a is not compatible with b
typedef int format_cmp_f(const struct rtp_payload_type *a, const struct rtp_payload_type *b);
typedef GString *format_print_f(const struct rtp_payload_type *);


#ifndef WITHOUT_CODECLIB



#include <stdbool.h>
#include <libswresample/swresample.h>
#include <libavcodec/avcodec.h>
#include <libavutil/audio_fifo.h>
#ifdef HAVE_BCG729
#include <bcg729/encoder.h>
#include <bcg729/decoder.h>
#endif
#include <opus.h>

#define AMR_FT_TYPES 14



enum rtpe_cpu_flag {
	RTPE_CPU_FLAG_AVX2 = 0,
	RTPE_CPU_FLAG_AVX512BW,
	RTPE_CPU_FLAG_AVX512F,
	RTPE_CPU_FLAG_SSE2,

	__NUM_RTPE_CPU_FLAGS
};



struct codec_type_s;
struct decoder_s;
struct encoder_s;
struct format_s;
struct resample_s;
struct seq_packet_s;
struct rtp_payload_type;
union codec_options_u;
struct encoder_callback_s;
struct dtx_method_s;
struct fraction;

typedef struct codec_type_s codec_type_t;
typedef struct decoder_s decoder_t;
typedef struct encoder_s encoder_t;
typedef struct format_s format_t;
typedef struct resample_s resample_t;
typedef struct seq_packet_s seq_packet_t;
typedef union codec_options_u codec_options_t;
typedef struct encoder_callback_s encoder_callback_t;
typedef struct dtx_method_s dtx_method_t;
typedef struct codec_cc_s codec_cc_t;

typedef int packetizer_f(AVPacket *, GString *, str *, size_t, encoder_t *,
		int64_t *__restrict pts, int64_t *__restrict dur);
typedef void format_init_f(struct rtp_payload_type *);
typedef void set_enc_options_f(encoder_t *, const str *);
typedef void set_dec_options_f(decoder_t *, const str *);
typedef void select_encoder_format_f(encoder_t *, format_t *requested_format, const format_t *input_format,
		const struct rtp_codec_format *fmtp);
typedef void select_decoder_format_f(decoder_t *, const struct rtp_codec_format *fmtp);

typedef bool format_parse_f(struct rtp_codec_format *, const str *fmtp);
typedef void format_answer_f(struct rtp_payload_type *, const struct rtp_payload_type *);



struct codec_type_s {
	void (*def_init)(struct codec_def_s *);

	const char *(*decoder_init)(decoder_t *, const str *);
	int (*decoder_input)(decoder_t *, const str *data, GQueue *);
	void (*decoder_close)(decoder_t *);

	const char *(*encoder_init)(encoder_t *, const str *);
	int (*encoder_input)(encoder_t *, AVFrame **);
	void (*encoder_got_packet)(encoder_t *);
	void (*encoder_close)(encoder_t *);
};

struct encoder_callback_s {
	struct {
		int64_t cmr_in_ts;
		unsigned int cmr_in;

		int64_t cmr_out_ts;
		unsigned int cmr_out;
	} amr;

	struct {
		int64_t cmr_in_ts;
		unsigned int cmr_in;
	} evs;
};
union codec_options_u {
	struct {
		const unsigned int *bits_per_frame;
		const unsigned int *bitrates;

		int64_t mode_change_interval_us;
		int64_t cmr_interval_us;
	} amr;

	struct {
		enum evs_bw max_bw;
	} evs;
};

enum dtx_method {
	DTX_NATIVE = 0,
	DTX_SILENCE,
	DTX_CN,

	NUM_DTX_METHODS
};

struct fraction {
	int mult;
	int div;
};
INLINE int fraction_mult(int a, const struct fraction *f) {
	return a * f->mult / f->div;
}
INLINE int fraction_div(int a, const struct fraction *f) {
	return a * f->div / f->mult;
}
INLINE long fraction_multl(long a, const struct fraction *f) {
	return a * f->mult / f->div;
}
INLINE long fraction_divl(long a, const struct fraction *f) {
	return a * f->div / f->mult;
}

struct codec_def_s {
	const char *rtpname;
	struct fraction default_clockrate_fact;
	enum AVCodecID avcodec_id;
	const char *avcodec_name_enc;
	const char *avcodec_name_dec;
	int default_clockrate;
	int default_channels;
	int default_bitrate;
	int default_ptime;
	int minimum_ptime;
	const char *default_fmtp;
	format_parse_f *format_parse;
	format_cmp_f *format_cmp;
	format_print_f *format_print;
	format_answer_f *format_answer;
	packetizer_f *packetizer;
	select_encoder_format_f *select_encoder_format;
	select_decoder_format_f *select_decoder_format;
	int bits_per_sample;
	enum media_type media_type;
	str silence_pattern;
	enum {
		MOS_NB = 0, // default
		MOS_FB,
		MOS_LEGACY,

		__MOS_TYPES
	} mos_type;

	// codec-specific callbacks
	format_init_f *init;
	set_enc_options_f *set_enc_options;
	set_dec_options_f *set_dec_options;
	const dtx_method_t *dtx_methods[NUM_DTX_METHODS];

	// filled in by codeclib_init()
	str rtpname_str;
	int rfc_payload_type;
	unsigned int support_encoding:1,
	             support_decoding:1;

	// flags
	unsigned int supplemental:1,
	             dtmf:1, // special case
		     fixed_sizes:1, // hint for `block-short` feature
		     evs:1,
		     amr:1;

	const codec_type_t *codec_type;

	// libavcodec
	const AVCodec *encoder;
	const AVCodec *decoder;
};

codec_def_t *codec_def_make_generic_av(enum AVCodecID);


struct format_s {
	int clockrate;
	int channels;
	int format; // enum AVSampleFormat
};

struct resample_s {
	SwrContext *swresample;
	bool no_filter;
};

enum codec_event {
	CE_AMR_CMR_RECV,
	CE_AMR_SEND_CMR,
	CE_EVS_CMR_RECV,
};

struct dtx_method_s {
	enum dtx_method method_id;

	int (*init)(decoder_t *);
	void (*cleanup)(decoder_t *);
	int (*do_dtx)(decoder_t *, GQueue *, int);

	union {
		struct {
			decoder_t *cn_dec;
			const str *cn_payload;
		} cn;
	};
};

struct decoder_s {
	codec_def_t *def;
	struct fraction clockrate_fact;
	codec_options_t codec_options;
	union codec_format_options format_options;
	dtx_method_t dtx;

	format_t in_format,
		 dec_out_format,
		 dest_format;

	resample_t resampler;

	union {
		struct {
			AVCodecContext *avcctx;
			AVPacket *avpkt;
			const enum AVSampleFormat *sample_fmts;

			union {
				struct {
					uint16_t bitrate_tracker[AMR_FT_TYPES];
					int64_t tracker_end;
					int64_t last_cmr;
				} amr;
			};
		} avc;
#ifdef HAVE_BCG729
		bcg729DecoderChannelContextStruct *bcg729;
#endif
		struct {
			unsigned long start_ts;
			unsigned int event;
			unsigned long duration;
		} dtmf;
		void *evs;
		OpusDecoder *opus;
	};

	unsigned long rtp_ts;
	uint64_t pts;
	int ptime;

	int (*event_func)(enum codec_event event, void *ptr, void *event_data);
	void *event_data;
};

struct encoder_s {
	format_t requested_format,
		 input_format,
		 actual_format;

	codec_def_t *def;
	struct fraction clockrate_fact;
	codec_options_t codec_options;
	encoder_callback_t callback;
	union codec_format_options format_options;

	resample_t resampler;

	union {
		struct {
			const AVCodec *codec;
			AVCodecContext *avcctx;
			const enum AVSampleFormat *sample_fmts;

			union {
				struct {
					int64_t cmr_in_ts;
					int64_t cmr_out_ts;
					unsigned int cmr_out_seq;
					uint64_t pkt_seq;
				} amr;
			};
		} avc;
#ifdef HAVE_BCG729
		bcg729EncoderChannelContextStruct *bcg729;
#endif
		struct {
			void *ctx;
			void *ind_list;
			int64_t cmr_in_ts;
		} evs;
		OpusEncoder *opus;
	};
	AVPacket *avpkt;
	AVAudioFifo *fifo;
	int64_t fifo_pts; // pts of first data in fifo
	int64_t packet_pts; // first pts of data in packetizer buffer
	int64_t next_pts; // next pts expected from the encoder
	int ptime;
	int bitrate;
	int samples_per_frame; // for encoding
	int samples_per_packet; // for frame packetizer
	AVFrame *frame; // to pull samples from the fifo
	int64_t mux_dts; // last dts passed to muxer
};

struct seq_packet_s {
	int seq;
};
struct packet_sequencer_s {
	GTree *packets;
	unsigned int lost_count;
	int seq; // next expected
	unsigned int ext_seq; // last received
	int roc; // rollover counter XXX duplicate with SRTP encryption context
};


extern const GQueue * const codec_supplemental_codecs;


// must be set before calling codeclib_init
extern void (*codeclib_thread_init)(void);
extern void (*codeclib_thread_cleanup)(void);
extern void (*codeclib_thread_loop)(void);

void codeclib_init(int);
void codeclib_free(void);

bool rtpe_has_cpu_flag(enum rtpe_cpu_flag flag);

codec_def_t *codec_find(const str *name, enum media_type);
codec_def_t *codec_get_pcm16(void);

bool codec_parse_fmtp(codec_def_t *def, struct rtp_codec_format *fmtp, const str *fmtp_string,
		union codec_format_options *copy);

decoder_t *decoder_new_fmt(codec_def_t *def, int clockrate, int channels, int ptime,
		const format_t *resample_fmt);
decoder_t *decoder_new_fmtp(codec_def_t *def, int clockrate, int channels, int ptime,
		const format_t *resample_fmt,
		struct rtp_codec_format *fmtp, const str *fmtp_string, const str *codec_opts);
void decoder_close(decoder_t *dec);
int decoder_input_data(decoder_t *dec, const str *data, unsigned long ts,
		int (*callback)(decoder_t *, AVFrame *, void *u1, void *u2), void *u1, void *u2);
int decoder_input_data_ptime(decoder_t *dec, const str *data, unsigned long ts, int *ptime,
		int (*callback)(decoder_t *, AVFrame *, void *u1, void *u2), void *u1, void *u2);
gboolean decoder_has_dtx(decoder_t *);
int decoder_switch_dtx(decoder_t *dec, enum dtx_method);
int decoder_set_cn_dtx(decoder_t *dec, const str *);
int decoder_dtx(decoder_t *dec, unsigned long ts, int ptime,
		int (*callback)(decoder_t *, AVFrame *, void *u1, void *u2), void *u1, void *u2);


encoder_t *encoder_new(void);
int encoder_config(encoder_t *enc, codec_def_t *def, int bitrate, int ptime,
		const format_t *requested_format, format_t *actual_format);
int encoder_config_fmtp(encoder_t *enc, codec_def_t *def, int bitrate, int ptime,
		const format_t *input_format,
		const format_t *requested_format, format_t *actual_format,
		struct rtp_codec_format *fmtp, const str *fmtp_string, const str *codec_opts);
void encoder_close(encoder_t *);
void encoder_free(encoder_t *);
int encoder_input_data(encoder_t *enc, AVFrame *frame,
		int (*callback)(encoder_t *, void *u1, void *u2), void *u1, void *u2);
int encoder_input_fifo(encoder_t *enc, AVFrame *frame,
		int (*callback)(encoder_t *, void *u1, void *u2), void *u1, void *u2);


void __packet_sequencer_init(packet_sequencer_t *ps, GDestroyNotify);
INLINE void packet_sequencer_init(packet_sequencer_t *ps, GDestroyNotify);
void packet_sequencer_destroy(packet_sequencer_t *ps);
void *packet_sequencer_next_packet(packet_sequencer_t *ps);
int packet_sequencer_next_ok(packet_sequencer_t *ps);
void *packet_sequencer_force_next_packet(packet_sequencer_t *ps);
int packet_sequencer_insert(packet_sequencer_t *ps, seq_packet_t *);

packetizer_f packetizer_passthrough; // pass frames as they arrive in AVPackets


void frame_fill_tone_samples(enum AVSampleFormat fmt, void *samples, unsigned int offset, unsigned int num,
		unsigned int freq, unsigned int volume, unsigned int sample_rate, unsigned int channels);
void frame_fill_dtmf_samples(enum AVSampleFormat fmt, void *samples, unsigned int offset, unsigned int num,
		unsigned int event, unsigned int volume, unsigned int sample_rate, unsigned int channels);


#ifdef HAVE_CODEC_CHAIN

typedef struct {
	unsigned int ctx_idx;

	unsigned int runs;
	unsigned int slots;
	uint64_t run_wait;
	uint64_t writers_wait;
	uint64_t compute_wait;
	uint64_t readers_wait;

	unsigned int run_busy;
	unsigned int write_busy;
	unsigned int slots_full;
	unsigned int buf_full;

	uint64_t ready_wait;
	uint64_t callbacks_preempt;
	uint64_t callbacks_fetch;
	uint64_t callbacks_run;
	uint64_t loop_barrier;
} codec_cc_context_stats;

TYPED_GQUEUE(codec_cc_context, codec_cc_context_stats);

typedef struct {
	char name[32];

	unsigned int async_busy;
	unsigned int async_blocked;
	unsigned int async_retry;

	codec_cc_context_q contexts;
} codec_cc_stats_entry;

TYPED_GQUEUE(codec_cc_stats, codec_cc_stats_entry);


extern codec_cc_t *(*codec_cc_new)(codec_def_t *src, format_t *src_format, codec_def_t *dst,
		format_t *dst_format, int bitrate, int ptime,
		void *(*init_async)(void *, void *, void *),
		void (*async_callback)(AVPacket *, void *));
void cc_init_chain(codec_def_t *src, format_t *src_format, codec_def_t *dst,
		format_t *dst_format);
void codec_cc_stop(codec_cc_t *);
void codec_cc_free(codec_cc_t **);

codec_cc_stats_q codec_cc_stats(void);

#else

INLINE codec_cc_t *codec_cc_new(codec_def_t *src, format_t *src_format, codec_def_t *dst,
		format_t *dst_format, int bitrate, int ptime,
		void *(*init_async)(void *, void *, void *),
		void (*async_callback)(AVPacket *, void *)) { return NULL; }
INLINE void cc_init_chain(codec_def_t *src, format_t *src_format, codec_def_t *dst,
		format_t *dst_format) { }
INLINE void codec_cc_stop(codec_cc_t *c) { }
INLINE void codec_cc_free(codec_cc_t **c) { }

#endif

AVPacket *codec_cc_input_data(codec_cc_t *c, const str *data, unsigned long ts, void *, void *, void *);


#include "auxlib.h"


// `ps` must be zero allocated
INLINE void packet_sequencer_init(packet_sequencer_t *ps, GDestroyNotify n) {
	if (ps->packets)
		return;
	__packet_sequencer_init(ps, n);
}
INLINE int format_eq(const format_t *a, const format_t *b) {
	if (G_UNLIKELY(a->clockrate != b->clockrate))
		return 0;
	if (G_UNLIKELY(a->channels != b->channels))
		return 0;
	if (G_UNLIKELY(a->format != b->format))
		return 0;
	return 1;
}
INLINE void format_init(format_t *f) {
	f->clockrate = -1;
	f->channels = -1;
	f->format = -1;
}
INLINE char *av_error(int no) {
	char *buf = get_thread_buf();
	av_strerror(no, buf, THREAD_BUF_SIZE);
	return buf;
}
INLINE int decoder_event(decoder_t *dec, enum codec_event event, void *ptr) {
	if (!dec)
		return 0;
	if (!dec->event_func)
		return 0;
	return dec->event_func(event, ptr, dec->event_data);
}
INLINE bool codec_def_supported(codec_def_t *def) {
	if (!def)
		return false;
	return def->support_encoding && def->support_decoding;
}


#else


// stubs
struct codec_def_s {
	int dtmf;
	int supplemental;
	int fixed_sizes;
	format_cmp_f * const format_cmp;
	format_print_f * const format_print;
	const str silence_pattern;
};
struct packet_sequencer_s {
};

INLINE void codeclib_init(int print) {
	if (print)
		printf("No codecs supported.\n");
}
INLINE void codeclib_free(void) {
	;
}

INLINE codec_def_t *codec_find(const str *name, enum media_type type) {
	return NULL;
}
INLINE void packet_sequencer_destroy(packet_sequencer_t *p) {
	return;
}
INLINE bool codec_def_supported(codec_def_t *def) {
	return false;
}


#endif
#endif
