#ifndef __CODEC_H__
#define __CODEC_H__

#include <glib.h>
#include <sys/time.h>
#include <stdbool.h>

#include "str.h"
#include "codeclib.h"
#include "helpers.h"
#include "rtplib.h"
#include "timerthread.h"
#include "types.h"
#include "ssrc.h"

struct call_media;
struct codec_handler;
struct media_packet;
struct codec_ssrc_handler;
struct rtp_header;
struct stream_params;
struct supp_codec_tracker;
struct rtcp_timer;
struct mqtt_timer;
struct codec_store;
struct call_monologue;
struct delay_buffer;
struct sink_handler;


typedef int codec_handler_func(struct codec_handler *, struct media_packet *);


struct codec_handler_index {
	int payload_type;
	struct call_media *sink;
};

struct codec_pipeline_index {
	rtp_payload_type src;
	rtp_payload_type dst;
};

struct codec_handler {
	union {
		struct codec_pipeline_index pi;
		struct {
			rtp_payload_type source_pt;
			rtp_payload_type dest_pt;
		};
	};
	struct codec_handler_index i;
	int dtmf_payload_type;
	int real_dtmf_payload_type;
	int cn_payload_type;
	codec_handler_func *handler_func;
	bool passthrough:1;
	bool kernelize:1;
	bool transcoder:1;
	bool pcm_dtmf_detect:1;
	bool blackhole:1;

	size_t payload_len; // for short-packet blocking

	struct ssrc_hash ssrc_hash;
	struct codec_handler *input_handler; // == main handler for supp codecs
	struct codec_handler *output_handler; // == self, or other PT handler
	struct call_media *media;
#ifdef WITH_TRANSCODING
	int (*packet_encoded)(encoder_t *enc, void *u1, void *u2);
	int (*packet_decoded)(decoder_t *, AVFrame *, void *, void *);
#endif

	// for media playback
	struct codec_ssrc_handler *ssrc_handler;
	// for DTMF injection
	struct codec_handler *dtmf_injector;
	struct delay_buffer *delay_buffer;
	// for remote transcoding
	struct transcode_config *tcc;
	struct transform_handler *transform;

	// stats entry
	const char *stats_chain_suffix;
	const char *stats_chain_suffix_brief;
	char *stats_chain;
	struct codec_stats *stats_entry;
};

struct codec_packet {
	struct timerthread_queue_entry ttq_entry;
	codec_packet_list link;
	str s;
	str plain;
	struct rtp_header *rtp;
	unsigned long ts;
	unsigned int clockrate;
	struct ssrc_entry_call *ssrc_out;
	void (*free_func)(void *);
	void (*plain_free_func)(void *);
};

struct codec_scheduler {
	unsigned long first_ts; // for output TS scaling
	unsigned long last_ts; // to detect input lag and handle lost packets
	int64_t first_send;
	unsigned long first_send_ts;
	long output_skew;
	bool is_media_playback; // used to track media playback so we can skip skew calculation
};

struct transcode_config {
	char *src; // unparsed
	char *dst; // unparsed

	char *name; // of the config section

	struct codec_pipeline_index i; // parsed

	int preference; // default: 0

	// transform verdict
	endpoint_t transform;
	str local_interface;
	str remote_interface;

	// codec-chain verdict
	bool codec_chain:1;
};

typedef union {
	struct call_monologue *ml;
} codec_timer_callback_arg_t __attribute__ ((__transparent_union__));


void codecs_init(void);
void codecs_cleanup(void);
void codec_timers_launch(void);
void rtcp_timer_stop(struct rtcp_timer **);
void codec_timer_callback(call_t *, void (*)(call_t *, codec_timer_callback_arg_t),
		codec_timer_callback_arg_t, uint64_t delay);

void mqtt_timer_stop(struct mqtt_timer **);
void mqtt_timer_start(struct mqtt_timer **mqtp, call_t *call, struct call_media *media);

struct codec_handler *codec_handler_get(struct call_media *, int payload_type, struct call_media *sink,
		struct sink_handler *);
void codec_handlers_free(struct call_media *);
struct codec_handler *codec_handler_make_playback(const rtp_payload_type *src_pt,
		const rtp_payload_type *dst_pt, unsigned long ts, struct call_media *, uint32_t ssrc,
		str_case_value_ht codec_set);
struct codec_handler *codec_handler_make_media_player(const rtp_payload_type *src_pt,
		const rtp_payload_type *dst_pt, unsigned long ts, struct call_media *, uint32_t ssrc,
		str_case_value_ht codec_set);
struct codec_handler *codec_handler_make_dummy(const rtp_payload_type *dst_pt, struct call_media *media,
		str_case_value_ht codec_set);
void codec_calc_jitter(struct ssrc_entry_call *, unsigned long ts, unsigned int clockrate, int64_t);
void codec_update_all_handlers(struct call_monologue *ml);
void codec_update_all_source_handlers(struct call_monologue *ml, const sdp_ng_flags *flags, bool clear_delay_buffer);

struct codec_store_args {
	str_case_value_ht codec_set;
	const struct codec_store *answer_cs;
	struct codec_store *merge_cs;
	bool allow_asymmetric;
};

__attribute__((nonnull(1)))
void codec_store_cleanup(struct codec_store *cs);
__attribute__((nonnull(1)))
void codec_store_init(struct codec_store *cs, struct call_media *);
__attribute__((nonnull(1, 2)))
void __codec_store_populate(struct codec_store *dst, struct codec_store *src, struct codec_store_args);
#define codec_store_populate(dst, src, ...) \
	__codec_store_populate(dst, src, (struct codec_store_args) {__VA_ARGS__})
__attribute__((nonnull(1, 2)))
void __codec_store_populate_reuse(struct codec_store *, struct codec_store *, struct codec_store_args);
#define codec_store_populate_reuse(dst, src, ...) \
	__codec_store_populate_reuse(dst, src, (struct codec_store_args) {__VA_ARGS__})
__attribute__((nonnull(1, 2)))
void codec_store_copy(struct codec_store *, struct codec_store *);
void codec_store_add_raw(struct codec_store *cs, rtp_payload_type *pt);
__attribute__((nonnull(1, 2)))
void codec_store_strip(struct codec_store *, str_q *strip, str_case_ht except);
__attribute__((nonnull(1, 2, 3)))
void codec_store_offer(struct codec_store *, str_q *, struct codec_store *);
__attribute__((nonnull(1, 2, 3)))
void codec_store_check_empty(struct codec_store *, struct codec_store *, sdp_ng_flags *);
__attribute__((nonnull(1, 2)))
void codec_store_accept(struct codec_store *, str_q *, struct codec_store *);
__attribute__((nonnull(1, 2)))
int codec_store_accept_one(struct codec_store *, str_q *, bool accept_any);
__attribute__((nonnull(1, 2)))
void codec_store_track(struct codec_store *, str_q *);
__attribute__((nonnull(1, 2, 3)))
void codec_store_transcode(struct codec_store *, str_q *, struct codec_store *);
__attribute__((nonnull(1, 2, 3)))
void __codec_store_answer(struct codec_store *dst, struct codec_store *src, sdp_ng_flags *flags,
		struct codec_store_args);
#define codec_store_answer(dst, src, flags, ...) \
	__codec_store_answer(dst, src, flags, (struct codec_store_args) {__VA_ARGS__})
__attribute__((nonnull(1, 2)))
void codec_store_synthesise(struct codec_store *dst, struct codec_store *opposite);
__attribute__((nonnull(1, 2)))
bool codec_store_is_full_answer(const struct codec_store *src, const struct codec_store *dst);

void codec_add_raw_packet(struct media_packet *mp, unsigned int clockrate);
void codec_packet_free(struct codec_packet *);
struct codec_packet *codec_packet_dup(struct codec_packet *p);
bool codec_packet_copy(struct codec_packet *p);

void payload_type_free(rtp_payload_type *p);
rtp_payload_type *rtp_payload_type_dup(const rtp_payload_type *pt);

G_DEFINE_AUTOPTR_CLEANUP_FUNC(rtp_payload_type, payload_type_free)


rtp_payload_type *codec_make_payload_type(const str *codec_str, enum media_type);
bool codec_parse_payload_type(rtp_payload_type *pt, const str *codec_str);

// handle string allocation
void codec_init_payload_type(rtp_payload_type *, enum media_type);
void payload_type_clear(rtp_payload_type *p);


struct chu_args {
	const sdp_ng_flags *flags;
	const struct stream_params *sp;
	bool allow_asymmetric;
	bool reset_transcoding;
	// when false (default if not passed) the delay buffer is flushed as normal
	// used by play-media to ensure media packets aren't sent alongside delayed packets
	bool clear_delay_buffer;
};
#define codec_handlers_update(r, s, ...) \
	__codec_handlers_update(r, s, (struct chu_args) {__VA_ARGS__})

bool codec_handler_transform(struct call_media *r, ng_codecs_q *);

#ifdef WITH_TRANSCODING

void ensure_codec_def(rtp_payload_type *pt, struct call_media *media);
void codec_handler_free(struct codec_handler **handler);
__attribute__((nonnull(1, 2)))
void __codec_handlers_update(struct call_media *receiver, struct call_media *sink, struct chu_args);
void codec_add_dtmf_event(struct codec_ssrc_handler *ch, int code, int level, uint64_t ts, bool injected);
uint64_t codec_last_dtmf_event(struct codec_ssrc_handler *ch);
uint64_t codec_encoder_pts(struct codec_ssrc_handler *ch, struct ssrc_entry_call *);
void codec_decoder_skip_pts(struct codec_ssrc_handler *ch, uint64_t);
uint64_t codec_decoder_unskip_pts(struct codec_ssrc_handler *ch);
void codec_tracker_update(struct codec_store *, struct codec_store *);
void codec_handlers_stop(codec_handlers_q *, struct call_media *sink, bool clear_delay_buffer);


void packet_encoded_packetize(AVPacket *pkt, struct codec_ssrc_handler *ch, struct media_packet *mp,
		packetizer_f pkt_f, void *pkt_f_data, const struct fraction *cr_fact,
		void (*tx_f)(struct codec_ssrc_handler *, struct media_packet *, str *,
			char *, unsigned int, int64_t pts, int64_t duration,
			const struct fraction *cr_fact));
void codec_output_rtp(struct media_packet *mp, struct codec_scheduler *,
		struct codec_handler *handler, // normally == ch->handler except for DTMF
		char *buf, // malloc'd, room for rtp_header + filled-in payload
		unsigned int payload_len,
		unsigned long payload_ts,
		int marker, int seq, int seq_inc, int payload_type,
		unsigned long ts_delay);


INLINE struct codec_handler_index __codec_handler_lookup_struct(int pt, struct call_media *sink) {
	struct codec_handler_index lookup = {
		.payload_type = pt,
		.sink = sink,
	};
	return lookup;
}
INLINE struct codec_handler *codec_handler_lookup(codec_handlers_ht ht, int pt, struct call_media *sink) {
	struct codec_handler_index lookup = __codec_handler_lookup_struct(pt, sink);
	return t_hash_table_lookup(ht, &lookup);
}


#else

INLINE void __codec_handlers_update(struct call_media *receiver, struct call_media *sink, struct chu_args a)
{
}
INLINE void codec_handler_free(struct codec_handler **handler) { }
INLINE void codec_tracker_update(struct codec_store *cs, struct codec_store *ocs) { }
INLINE void codec_handlers_stop(codec_handlers_q *q, struct call_media *sink, bool clear_delay_buffer) { }
INLINE void ensure_codec_def(rtp_payload_type *pt, struct call_media *media) { }

#endif



#endif
