#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "avcodec.h"
#include "ac3.h"

typedef struct CombineContext{
    AVClass *av_class;                      ///< AVClass used for AVOption
    AC3EncOptions options;                  ///< encoding options
    void *ctx;
    AVCodec *codec;
}CombineContext;

#define OFFSET(param) offsetof(CombineContext, options.param)
#define AC3ENC_PARAM (AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)

#define AC3ENC_TYPE_AC3_FIXED   0
#define AC3ENC_TYPE_AC3         1
#define AC3ENC_TYPE_EAC3        2

#define AC3ENC_TYPE 12354
#include "ac3enc_opts_template.c"

static AVClass ac3enc_class = { "AC-3 Encoder", av_default_item_name,
                                eac3_options, LIBAVUTIL_VERSION_INT };

static av_cold AVCodec *get_codec(enum AVSampleFormat s){
#if CONFIG_AC3_FIXED_ENCODER
    if(s==AV_SAMPLE_FMT_S16) return &ff_ac3_fixed_encoder;
#endif
#if CONFIG_AC3_FLOAT_ENCODER
    if(s==AV_SAMPLE_FMT_FLT) return &ff_ac3_float_encoder;
#endif
    return NULL;
}


static av_cold int encode_init(AVCodecContext *avctx)
{
    CombineContext *c= avctx->priv_data;
    int ret;
    int offset= (uint8_t*)&c->options - (uint8_t*)c;

    c->codec= get_codec(avctx->sample_fmt);
    if(!c->codec){
        av_log(avctx, AV_LOG_ERROR, "Unsupported sample format\n");
        return -1;
    }
    c->ctx= av_mallocz(c->codec->priv_data_size);
    memcpy((uint8_t*)c->ctx + offset, &c->options, (uint8_t*)&c->ctx - (uint8_t*)&c->options);
    FFSWAP(void *,avctx->priv_data, c->ctx);
    ret= c->codec->init(avctx);
    FFSWAP(void *,avctx->priv_data, c->ctx);
    return ret;
}

static int encode_frame(AVCodecContext *avctx, unsigned char *frame,
                        int buf_size, void *data)
{
    CombineContext *c= avctx->priv_data;
    int ret;

    FFSWAP(void *,avctx->priv_data, c->ctx);
    ret= c->codec->encode(avctx, frame, buf_size, data);
    FFSWAP(void *,avctx->priv_data, c->ctx);
    return ret;
}

static av_cold int encode_close(AVCodecContext *avctx)
{
    CombineContext *c= avctx->priv_data;
    int ret;

    FFSWAP(void *,avctx->priv_data, c->ctx);
    ret= c->codec->close(avctx);
    FFSWAP(void *,avctx->priv_data, c->ctx);
    return ret;
}

AVCodec ff_ac3_encoder = {
    "ac3",
    AVMEDIA_TYPE_AUDIO,
    CODEC_ID_AC3,
    sizeof(CombineContext),
    encode_init,
    encode_frame,
    encode_close,
    NULL,
    .sample_fmts = (const enum AVSampleFormat[]){
#if CONFIG_AC3_FLOAT_ENCODER
        AV_SAMPLE_FMT_FLT,
#endif
#if CONFIG_AC3_FIXED_ENCODER
        AV_SAMPLE_FMT_S16,
#endif
        AV_SAMPLE_FMT_NONE},
    .long_name = NULL_IF_CONFIG_SMALL("ATSC A/52A (AC-3)"),
    .priv_class = &ac3enc_class,
    .channel_layouts = ff_ac3_channel_layouts,
};