#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/once.h>
#include <pulse/sample.h>
#include <pulse/xmalloc.h>

#include <arpa/inet.h>

#include <fdk-aac/aacenc_lib.h>
#include <fdk-aac/aacdecoder_lib.h>

#include "a2dp-codec-util.h"
#include "a2dp-codecs.h"
#include "a2dp-codec-api.h"
#include "rtp.h"

#define AAC_DEFAULT_BITRATE 128000u

typedef struct aac_info {
    bool is_a2dp_sink;

    pa_sample_spec sample_spec;
    size_t mtu;

    HANDLE_AACDECODER aacdecoder_handle;
    bool aacdecoder_handle_opened;

    HANDLE_AACENCODER aacenc_handle;
    bool aacenc_handle_opened;
    AACENC_InfoStruct aacenc_info;

    uint32_t bitrate;

    uint16_t seq_num;

    size_t read_block_size;
    size_t write_block_size;


    int aac_enc_bitrate_mode;
    uint32_t aac_afterburner;
    pa_sample_format_t force_pa_fmt;
    int vbr;
} aac_info_t;

static bool can_be_supported(bool for_encoding)
{
    pa_log_info("can_be_supported");
    return true;
}


static bool can_accept_capabilities(const uint8_t *capabilities_buffer, uint8_t capabilities_size, bool for_encoding)
{
    pa_log_info("can_accept_capabilities");
    const a2dp_aac_t *capabilities = (const a2dp_aac_t *) capabilities_buffer;

    if (capabilities_size != sizeof(*capabilities))
        return false;

    if (!(capabilities->object_type & (AAC_OBJECT_TYPE_MPEG2_AAC_LC | AAC_OBJECT_TYPE_MPEG4_AAC_LC )))
        return false;

    if (!(capabilities->channels & (AAC_CHANNELS_1 | AAC_CHANNELS_2 )))
        return false;

    return true;
}

static const char *choose_remote_endpoint(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding)
{
    pa_log_info("choose_remote_endpoint");
    const pa_a2dp_codec_capabilities *a2dp_capabilities;
    const char *key;
    void *state;

    /* There is no preference, just choose random valid entry */
    PA_HASHMAP_FOREACH_KV(key, a2dp_capabilities, capabilities_hashmap, state) {
        if (can_accept_capabilities(a2dp_capabilities->buffer, a2dp_capabilities->size, for_encoding))
            return key;
    }

    return NULL;
}

static uint8_t fill_capabilities(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE])
{
    pa_log_info("fill_capabilities");
    a2dp_aac_t *capabilities = (a2dp_aac_t *) capabilities_buffer;
    pa_zero(*capabilities);

    capabilities->object_type = AAC_OBJECT_TYPE_MPEG2_AAC_LC | AAC_OBJECT_TYPE_MPEG4_AAC_LC;
    capabilities->channels = AAC_CHANNELS_1 | AAC_CHANNELS_2;
    AAC_SET_BITRATE(*capabilities, AAC_DEFAULT_BITRATE);
    AAC_SET_FREQUENCY(*capabilities, (AAC_SAMPLING_FREQ_8000 | AAC_SAMPLING_FREQ_11025 | AAC_SAMPLING_FREQ_12000 |
                                      AAC_SAMPLING_FREQ_16000 | AAC_SAMPLING_FREQ_22050 | AAC_SAMPLING_FREQ_24000 |
                                      AAC_SAMPLING_FREQ_32000 | AAC_SAMPLING_FREQ_44100 | AAC_SAMPLING_FREQ_48000 |
                                      AAC_SAMPLING_FREQ_64000 | AAC_SAMPLING_FREQ_88200 | AAC_SAMPLING_FREQ_96000));
    capabilities->vbr = 1;

    return sizeof(*capabilities);
}


static bool is_configuration_valid(const uint8_t *config_buffer, uint8_t config_size)
{
    pa_log_info("is_configuration_valid");
    const a2dp_aac_t *config = (const a2dp_aac_t *) config_buffer;

    if (config_size != sizeof(*config)) {
        pa_log_error("Invalid size of config buffer");
        return false;
    }

    if(!(config->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC) && !(config->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC))
    {
        pa_log_error("Invalid object_type in configuration, config->object_type : %x", config->object_type);
        return false;
    }

    if(config->channels != AAC_CHANNELS_1 &&  config->channels != AAC_CHANNELS_2)
    {
        pa_log_error("Invalid channels in configuration");
        return false;
    }

    return true;
}


static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE])
{
    pa_log_info("fill_preferred_configuration");

    a2dp_aac_t *config = (a2dp_aac_t *) config_buffer;
    const a2dp_aac_t *capabilities = (const a2dp_aac_t *) capabilities_buffer;
    pa_a2dp_freq_cap_t aac_freq_cap, aac_freq_table[] = {
            {8000U,  AAC_SAMPLING_FREQ_8000},
            {11025U, AAC_SAMPLING_FREQ_11025},
            {12000U, AAC_SAMPLING_FREQ_12000},
            {16000U, AAC_SAMPLING_FREQ_16000},
            {22050U, AAC_SAMPLING_FREQ_22050},
            {24000U, AAC_SAMPLING_FREQ_24000},
            {32000U, AAC_SAMPLING_FREQ_32000},
            {44100U, AAC_SAMPLING_FREQ_44100},
            {48000U, AAC_SAMPLING_FREQ_48000},
            {64000U, AAC_SAMPLING_FREQ_64000},
            {88200U, AAC_SAMPLING_FREQ_88200},
            {96000U, AAC_SAMPLING_FREQ_96000}
    };

    if (capabilities_size != sizeof(*capabilities)) {
        pa_log_error("Invalid size of capabilities buffer");
        return 0;
    }

    pa_zero(*config);

    if (!pa_a2dp_select_cap_frequency(AAC_GET_FREQUENCY(*capabilities), *default_sample_spec, aac_freq_table,
                                      PA_ELEMENTSOF(aac_freq_table), &aac_freq_cap))
        return 0;

    AAC_SET_FREQUENCY(*config, aac_freq_cap.cap);

    AAC_SET_BITRATE(*config, AAC_GET_BITRATE(*capabilities));

    if (default_sample_spec->channels <= 1) {
        if (capabilities->channels & AAC_CHANNELS_1)
            config->channels = AAC_CHANNELS_1;
        else if (capabilities->channels & AAC_CHANNELS_2)
            config->channels = AAC_CHANNELS_2;
        else {
            pa_log_error("No supported channel modes");
            return 0;
        }
    }

    if (default_sample_spec->channels >= 2) {
        if (capabilities->channels & AAC_CHANNELS_2)
            config->channels = AAC_CHANNELS_2;
        else if (capabilities->channels & AAC_CHANNELS_1)
            config->channels = AAC_CHANNELS_1;
        else {
            pa_log_error("No supported channel modes");
            return 0;
        }
    }

    if (capabilities->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC)
        config->object_type = AAC_OBJECT_TYPE_MPEG4_AAC_LC;
    else if (capabilities->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC)
        config->object_type = AAC_OBJECT_TYPE_MPEG2_AAC_LC;
    else {
        pa_log_error("No supported aac object type");
        return 0;
    }

    config->vbr = capabilities->vbr;

    return sizeof(*config);
}


static void *init(bool for_encoding, bool for_backchannel, const uint8_t *config_buffer, uint8_t config_size, pa_sample_spec *sample_spec, pa_core *core)
{
    pa_log_info("init");
    aac_info_t * info = pa_xnew0(aac_info_t, 1);
    info->is_a2dp_sink = !for_encoding;
    const a2dp_aac_t *config = (const a2dp_aac_t *) config_buffer;

    info->aac_enc_bitrate_mode = 3;
    info->aac_afterburner = 1;
    info->force_pa_fmt = PA_SAMPLE_S16LE;
    info->vbr = config->vbr;
    int aac_err;
    UINT aot, sample_rate, channels;

    {
        switch (config->object_type)
        {
        case AAC_OBJECT_TYPE_MPEG2_AAC_LC:
            aot = AOT_AAC_LC;
            break;
        case AAC_OBJECT_TYPE_MPEG4_AAC_LC:
            aot = AOT_AAC_LC;
            break;
        case AAC_OBJECT_TYPE_MPEG4_AAC_LTP:
            aot = AOT_AAC_LTP;
            break;
        case AAC_OBJECT_TYPE_MPEG4_AAC_SCA:
            aot = AOT_AAC_SCAL;
            break;
        default:
            {
                if(config->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC || config->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC)
                {
                    aot = AOT_AAC_LC;
                }
                else if(config->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LTP)
                {
                    aot = AOT_AAC_LTP;
                }
                else if(config->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_SCA)
                {
                    aot = AOT_AAC_SCAL;
                }
                else
                {
                    pa_log_error("Invalid AAC object type");
                    pa_assert_not_reached();
                }
            }
        }

        switch (AAC_GET_FREQUENCY(*config))
        {
        case AAC_SAMPLING_FREQ_8000:
            sample_rate = 8000;
            sample_spec->rate = 8000;
            break;
        case AAC_SAMPLING_FREQ_11025:
            sample_rate = 11025;
            sample_spec->rate = 11025;
            break;
        case AAC_SAMPLING_FREQ_12000:
            sample_rate = 12000;
            sample_spec->rate = 12000;
            break;
        case AAC_SAMPLING_FREQ_16000:
            sample_rate = 16000;
            sample_spec->rate = 16000;
            break;
        case AAC_SAMPLING_FREQ_22050:
            sample_rate = 22050;
            sample_spec->rate = 22050;
            break;
        case AAC_SAMPLING_FREQ_24000:
            sample_rate = 24000;
            sample_spec->rate = 24000;
            break;
        case AAC_SAMPLING_FREQ_32000:
            sample_rate = 32000;
            sample_spec->rate = 32000;
            break;
        case AAC_SAMPLING_FREQ_44100:
            sample_rate = 44100;
            sample_spec->rate = 44100;
            break;
        case AAC_SAMPLING_FREQ_48000:
            sample_rate = 48000;
            sample_spec->rate = 48000;
            break;
        case AAC_SAMPLING_FREQ_64000:
            sample_rate = 64000;
            sample_spec->rate = 64000;
            break;
        case AAC_SAMPLING_FREQ_88200:
            sample_rate = 88200;
            sample_spec->rate = 88200;
            break;
        case AAC_SAMPLING_FREQ_96000:
            sample_rate = 96000;
            sample_spec->rate = 96000;
            break;
        default:
            pa_log_error("Invalid AAC frequency");
            pa_assert_not_reached();
        }

        switch (config->channels)
        {
            case AAC_CHANNELS_1:
                channels = MODE_1;
                sample_spec->channels = 1;
                break;
            case AAC_CHANNELS_2:
                channels = MODE_2;
                sample_spec->channels = 2;
                break;
            default:
                pa_log_error("Invalid AAC channel mode");
                pa_assert_not_reached();
        }
    }

    sample_spec->channels =  channels;
    sample_spec->rate = sample_rate;
    sample_spec->format = info->force_pa_fmt;
    info->sample_spec = * sample_spec;

    //sink
    if(info->is_a2dp_sink)
    {
        if (!info->aacdecoder_handle_opened)
        {
            info->aacdecoder_handle = aacDecoder_Open(TT_MP4_LATM_MCP1, 1);
            info->aacdecoder_handle_opened = true;
        }
        pa_assert_se(AAC_DEC_OK == aacDecoder_SetParam(info->aacdecoder_handle, AAC_PCM_MIN_OUTPUT_CHANNELS,
                                                       sample_spec->channels));
        pa_assert_se(AAC_DEC_OK == aacDecoder_SetParam(info->aacdecoder_handle, AAC_PCM_MAX_OUTPUT_CHANNELS,
                                                       sample_spec->channels));

    }
    else
    {
        if (!info->aacenc_handle_opened)
        {
            aac_err = aacEncOpen(&info->aacenc_handle, 0x07, 2);

            if (aac_err != AACENC_OK)
            {
                pa_log_error("Cannot open AAC encoder handle: AAC error 0x%x", aac_err);
                pa_assert_not_reached();
            }
            info->aacenc_handle_opened = true;
        }

        info->bitrate =  AAC_DEFAULT_BITRATE;

        aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_AOT, aot);
        if (aac_err != AACENC_OK)
            pa_assert_not_reached();

        aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_SAMPLERATE, sample_rate);
        if (aac_err != AACENC_OK)
            pa_assert_not_reached();

        aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_CHANNELMODE, channels);
        if (aac_err != AACENC_OK)
            pa_assert_not_reached();

        if (config->vbr) {
            aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_BITRATEMODE,
                                        info->aac_enc_bitrate_mode);
            if (aac_err != AACENC_OK)
                pa_assert_not_reached();
        }

        aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_AUDIOMUXVER, 2);
        if (aac_err != AACENC_OK)
            pa_assert_not_reached();

        aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_SIGNALING_MODE, 1);
        if (aac_err != AACENC_OK)
            pa_assert_not_reached();

        aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_BITRATE, info->bitrate);
        if (aac_err != AACENC_OK)
            pa_assert_not_reached();

        aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_SAMPLERATE, info->sample_spec.rate);
        if (aac_err != AACENC_OK)
            pa_assert_not_reached();

        aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_TRANSMUX, TT_MP4_LATM_MCP1);
        if (aac_err != AACENC_OK)
            pa_assert_not_reached();

        aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_HEADER_PERIOD, 1);
        if (aac_err != AACENC_OK)
            pa_assert_not_reached();

        aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_AFTERBURNER, info->aac_afterburner);
        if (aac_err != AACENC_OK)
            pa_assert_not_reached();

        aac_err = aacEncEncode(info->aacenc_handle, NULL, NULL, NULL, NULL);
        if (aac_err != AACENC_OK)
            pa_assert_not_reached();

        pa_assert_se(AACENC_OK == aacEncInfo(info->aacenc_handle, &info->aacenc_info));

        pa_assert(info->aacenc_info.inputChannels == info->sample_spec.channels);

    }

    return info;
}

static void deinit(void *codec_info)
{
    pa_log_info("deinit");
    aac_info_t *info = (aac_info_t *) codec_info;
    if(NULL != info->aacdecoder_handle)
    {
        aacDecoder_Close(info->aacdecoder_handle);
        info->aacdecoder_handle = NULL;
        info->aacdecoder_handle_opened = false;
    }

    if(NULL != info->aacenc_handle)
    {
        aacEncClose(&info->aacenc_handle);
        info->aacenc_handle = NULL;
        info->aacenc_handle_opened = false;
    }


    pa_xfree(info);
}


static int reset(void *codec_info)
{
    pa_log_info("reset");
    //aac_info_t *info = (aac_info_t *) codec_info;
    return 0;
}


static size_t get_block_size(void *codec_info, size_t link_mtu)
{
    //pa_log("get_block_size");
    aac_info_t *info = (aac_info_t *) codec_info;
    info->mtu = link_mtu;
    info->write_block_size = info->read_block_size = 1024 * pa_frame_size(&info->sample_spec);
    return info->write_block_size;
}

static size_t get_encoded_block_size(void *codec_info, size_t input_size)
{
    //pa_log("get_encoded_block_size");
    aac_info_t *info = (aac_info_t *) codec_info;
    info->write_block_size = 1024 * pa_frame_size(&info->sample_spec);
    return info->write_block_size;
}



static size_t reduce_encoder_bitrate(void *codec_info, size_t write_link_mtu)
{
    //pa_log("reduce_encoder_bitrate");
    aac_info_t *info = (aac_info_t *) codec_info;
    if (info->vbr)
    {
        if(info->aac_enc_bitrate_mode > 1)
        {
            info->aac_enc_bitrate_mode--;
            AACENC_ERROR aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_BITRATEMODE,
                                        info->aac_enc_bitrate_mode);
            if (aac_err != AACENC_OK)
                pa_assert_not_reached();

            aac_err = aacEncEncode(info->aacenc_handle, NULL, NULL, NULL, NULL);
            if (aac_err != AACENC_OK)
                pa_assert_not_reached();

            // so make sure every encoded AAC frame fits in (mtu - header)
            if (write_link_mtu >= 500 && write_link_mtu <= 1200) {
                int maxbitrate = ((write_link_mtu - sizeof(struct rtp_header)) * 8 * info->sample_spec.rate) / 1024;
                pa_log("bitrate: %d", maxbitrate);
                aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_PEAK_BITRATE, maxbitrate);
                if (aac_err != AACENC_OK)
                    pa_assert_not_reached();
            }

            pa_log("aac_enc_bitrate_mode: set %d", info->aac_enc_bitrate_mode);
        }
        else
        {
            //pa_log("min aac_enc_bitrate_mode: set %d", info->aac_enc_bitrate_mode);
        }

    }

    return 0;
}

static size_t increase_encoder_bitrate(void *codec_info, size_t write_link_mtu)
{
    //pa_log("increase_encoder_bitrate");
    aac_info_t *info = (aac_info_t *) codec_info;
    if (info->vbr)
    {
        if(info->aac_enc_bitrate_mode < 5)
        {
            info->aac_enc_bitrate_mode++;
            AACENC_ERROR aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_BITRATEMODE,
                                        info->aac_enc_bitrate_mode);
            if (aac_err != AACENC_OK)
                pa_assert_not_reached();

            aac_err = aacEncEncode(info->aacenc_handle, NULL, NULL, NULL, NULL);
            if (aac_err != AACENC_OK)
                pa_assert_not_reached();

            // so make sure every encoded AAC frame fits in (mtu - header)
            if (write_link_mtu >= 500 && write_link_mtu <= 1200) {
                int maxbitrate = ((write_link_mtu - sizeof(struct rtp_header)) * 8 * info->sample_spec.rate) / 1024;
                pa_log("bitrate: %d", maxbitrate);
                aac_err = aacEncoder_SetParam(info->aacenc_handle, AACENC_PEAK_BITRATE, maxbitrate);
                if (aac_err != AACENC_OK)
                    pa_assert_not_reached();
            }


            pa_log("aac_enc_bitrate_mode: set %d", info->aac_enc_bitrate_mode);
        }
        else
        {
            //pa_log("max aac_enc_bitrate_mode: %d", info->aac_enc_bitrate_mode);
        }

    }
    return 0;
}

static size_t encode_buffer(void *codec_info, uint32_t timestamp, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed)
{
    //pa_log("encode_buffer");
    aac_info_t *info = (aac_info_t *) codec_info;

    struct rtp_header *header;
    size_t to_write = 0 , to_encode = 0;
    uint8_t *d = NULL;

    header = (struct rtp_header*) output_buffer;

    d =  output_buffer + sizeof(*header) ;
    to_write = output_size - sizeof(*header);

    void *in_ptr = input_buffer;
    void *out_ptr = d;
    while(true)
    {
        AACENC_BufDesc in_buf = { 0 }, out_buf = { 0 };
        AACENC_InArgs in_args = { 0 };
        AACENC_OutArgs out_args = { 0 };
        int in_identifier = IN_AUDIO_DATA;
        int in_size, in_elem_size;
        int out_identifier = OUT_BITSTREAM_DATA;
        int out_size, out_elem_size;
        int read, i;

        AACENC_ERROR err;

        in_size = input_size - to_encode;
        in_elem_size = pa_sample_size(&info->sample_spec);

        in_args.numInSamples = info->aacenc_info.frameLength * info->aacenc_info.inputChannels;
        in_buf.numBufs = 1;
		in_buf.bufs = &in_ptr;
		in_buf.bufferIdentifiers = &in_identifier;
		in_buf.bufSizes = &in_size;
		in_buf.bufElSizes = &in_elem_size;

		out_size = to_write;
		out_elem_size = 1;
		out_buf.numBufs = 1;
		out_buf.bufs = &out_ptr;
		out_buf.bufferIdentifiers = &out_identifier;
		out_buf.bufSizes = &out_size;
		out_buf.bufElSizes = &out_elem_size;

        if ((err = aacEncEncode(info->aacenc_handle, &in_buf, &out_buf, &in_args, &out_args)) != AACENC_OK)
        {
			break;
		}
        d += out_args.numOutBytes;
        to_encode += out_args.numInSamples * in_elem_size;
        out_ptr += out_args.numOutBytes;
        to_write -= out_args.numOutBytes;
        in_ptr += to_encode;

        if(to_encode >=  input_size)
        {
            break;
        }
    }

    /* write it to the fifo */
    pa_memzero(output_buffer, sizeof(*header));

    header->v = 2;
    header->pt = 96;
    header->sequence_number = htons(info->seq_num++);
    header->timestamp = htonl(timestamp);
    header->ssrc = htonl(1);

    //pa_log("seq num : %d, pcm : %d, encode : %d", info->seq_num, to_encode, d - output_buffer);

    *processed = to_encode;

    return d - output_buffer;
}

static size_t decode_buffer(void *codec_info, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed)
{
    aac_info_t *info = (aac_info_t *) codec_info;

    struct rtp_header *header;
    const UCHAR *p;
    INT_PCM *d;
    UINT to_decode, pkt_size;
    size_t total_written = 0;

    header = input_buffer;
    p = (UCHAR *) input_buffer + sizeof(*header);
    pkt_size = to_decode = (UINT) (input_size - sizeof(*header));
    d = output_buffer;

    {
        CStreamInfo* cinfo;
        AAC_DECODER_ERROR aac_err = aacDecoder_Fill(info->aacdecoder_handle,
                                                    (UCHAR **) &p, &pkt_size, &to_decode);

        if (PA_UNLIKELY(aac_err != AAC_DEC_OK)) {
            pa_log_error("aacDecoder_Fill() error 0x%x", aac_err);
            return 0;
        }

        while (true) {
            INT written;
            aac_err = aacDecoder_DecodeFrame(info->aacdecoder_handle, d, (INT) output_size, 0);
            if (PA_UNLIKELY(aac_err == AAC_DEC_NOT_ENOUGH_BITS))
                break;
            if (PA_UNLIKELY(aac_err != AAC_DEC_OK)){
                pa_log_error("aacDecoder_DecodeFrame() error 0x%x", aac_err);
                break;
            }

            cinfo = aacDecoder_GetStreamInfo(info->aacdecoder_handle);
            if(PA_UNLIKELY(!cinfo || cinfo->sampleRate <= 0)) {
                pa_log_error("Invalid stream info");
                break;
            }

            written = cinfo->frameSize * cinfo->numChannels * 2;
            d += written;
            total_written += written;
        }
    }
    //pa_log("input size : %u, total_written : %u", input_size, total_written);

    *processed = input_size;
    return total_written;
}

const pa_a2dp_endpoint_conf pa_a2dp_endpoint_conf_aac = {
    .id = { A2DP_CODEC_MPEG24, 0, 0 },
    .support_backchannel = false,
    .can_be_supported = can_be_supported,
    .can_accept_capabilities = can_accept_capabilities,
    .choose_remote_endpoint = choose_remote_endpoint,
    .fill_capabilities = fill_capabilities,
    .is_configuration_valid = is_configuration_valid,
    .fill_preferred_configuration = fill_preferred_configuration,
    .bt_codec = {
        .name = "aac0",
        .description = "aac coder",
        .init = init,
        .deinit = deinit,
        .reset = reset,
        .get_read_block_size = get_block_size,
        .get_write_block_size = get_block_size,
        .get_encoded_block_size = get_encoded_block_size,
        .reduce_encoder_bitrate = reduce_encoder_bitrate,
        .increase_encoder_bitrate = increase_encoder_bitrate,
        .encode_buffer = encode_buffer,
        .decode_buffer = decode_buffer,
    },
};

const pa_a2dp_endpoint_conf pa_a2dp_endpoint_conf_aac1 = {
    .id = { A2DP_CODEC_MPEG24, 0, 0 },
    .support_backchannel = false,
    .can_be_supported = can_be_supported,
    .can_accept_capabilities = can_accept_capabilities,
    .choose_remote_endpoint = choose_remote_endpoint,
    .fill_capabilities = fill_capabilities,
    .is_configuration_valid = is_configuration_valid,
    .fill_preferred_configuration = fill_preferred_configuration,
    .bt_codec = {
        .name = "aac1",
        .description = "aac coder",
        .init = init,
        .deinit = deinit,
        .reset = reset,
        .get_read_block_size = get_block_size,
        .get_write_block_size = get_block_size,
        .get_encoded_block_size = get_encoded_block_size,
        .reduce_encoder_bitrate = reduce_encoder_bitrate,
        .increase_encoder_bitrate = increase_encoder_bitrate,
        .encode_buffer = encode_buffer,
        .decode_buffer = decode_buffer,
    },
};

const pa_a2dp_endpoint_conf pa_a2dp_endpoint_conf_aac2 = {
    .id = { A2DP_CODEC_MPEG24, 0, 0 },
    .support_backchannel = false,
    .can_be_supported = can_be_supported,
    .can_accept_capabilities = can_accept_capabilities,
    .choose_remote_endpoint = choose_remote_endpoint,
    .fill_capabilities = fill_capabilities,
    .is_configuration_valid = is_configuration_valid,
    .fill_preferred_configuration = fill_preferred_configuration,
    .bt_codec = {
        .name = "aac2",
        .description = "aac coder",
        .init = init,
        .deinit = deinit,
        .reset = reset,
        .get_read_block_size = get_block_size,
        .get_write_block_size = get_block_size,
        .get_encoded_block_size = get_encoded_block_size,
        .reduce_encoder_bitrate = reduce_encoder_bitrate,
        .increase_encoder_bitrate = increase_encoder_bitrate,
        .encode_buffer = encode_buffer,
        .decode_buffer = decode_buffer,
    },
};

const pa_a2dp_endpoint_conf pa_a2dp_endpoint_conf_aac3 = {
    .id = { A2DP_CODEC_MPEG24, 0, 0 },
    .support_backchannel = false,
    .can_be_supported = can_be_supported,
    .can_accept_capabilities = can_accept_capabilities,
    .choose_remote_endpoint = choose_remote_endpoint,
    .fill_capabilities = fill_capabilities,
    .is_configuration_valid = is_configuration_valid,
    .fill_preferred_configuration = fill_preferred_configuration,
    .bt_codec = {
        .name = "aac3",
        .description = "aac coder",
        .init = init,
        .deinit = deinit,
        .reset = reset,
        .get_read_block_size = get_block_size,
        .get_write_block_size = get_block_size,
        .get_encoded_block_size = get_encoded_block_size,
        .reduce_encoder_bitrate = reduce_encoder_bitrate,
        .increase_encoder_bitrate = increase_encoder_bitrate,
        .encode_buffer = encode_buffer,
        .decode_buffer = decode_buffer,
    },
};

const pa_a2dp_endpoint_conf pa_a2dp_endpoint_conf_aac4 = {
    .id = { A2DP_CODEC_MPEG24, 0, 0 },
    .support_backchannel = false,
    .can_be_supported = can_be_supported,
    .can_accept_capabilities = can_accept_capabilities,
    .choose_remote_endpoint = choose_remote_endpoint,
    .fill_capabilities = fill_capabilities,
    .is_configuration_valid = is_configuration_valid,
    .fill_preferred_configuration = fill_preferred_configuration,
    .bt_codec = {
        .name = "aac4",
        .description = "aac coder",
        .init = init,
        .deinit = deinit,
        .reset = reset,
        .get_read_block_size = get_block_size,
        .get_write_block_size = get_block_size,
        .get_encoded_block_size = get_encoded_block_size,
        .reduce_encoder_bitrate = reduce_encoder_bitrate,
        .increase_encoder_bitrate = increase_encoder_bitrate,
        .encode_buffer = encode_buffer,
        .decode_buffer = decode_buffer,
    },
};

const pa_a2dp_endpoint_conf pa_a2dp_endpoint_conf_aac5 = {
    .id = { A2DP_CODEC_MPEG24, 0, 0 },
    .support_backchannel = false,
    .can_be_supported = can_be_supported,
    .can_accept_capabilities = can_accept_capabilities,
    .choose_remote_endpoint = choose_remote_endpoint,
    .fill_capabilities = fill_capabilities,
    .is_configuration_valid = is_configuration_valid,
    .fill_preferred_configuration = fill_preferred_configuration,
    .bt_codec = {
        .name = "aac5",
        .description = "aac coder",
        .init = init,
        .deinit = deinit,
        .reset = reset,
        .get_read_block_size = get_block_size,
        .get_write_block_size = get_block_size,
        .get_encoded_block_size = get_encoded_block_size,
        .reduce_encoder_bitrate = reduce_encoder_bitrate,
        .increase_encoder_bitrate = increase_encoder_bitrate,
        .encode_buffer = encode_buffer,
        .decode_buffer = decode_buffer,
    },
};
