#include <stdio.h>
#include "all.h"

#include <libavdevice/avdevice.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <speex/speex.h>

static AVFormatContext *fctx = NULL;
static AVCodecContext *dctx = NULL;

static void *estate;
static SpeexBits ebits;
static int speex_frame_samples;

SpeexEchoState *echo_state;

void error2(const char *str, int i) {
    fprintf(stderr, "%s: %i\n", str, i);
    abort();
}

void new_for_speex(const short *data, int samples) {
    static short storage[1000];
    static int nread = 0;
    static int in_packet = 0;

    memcpy(&storage[nread], data, samples*sizeof(data[0]));
    nread += samples;

    while(nread >= speex_frame_samples)
    {
        static short dest[1000];
        char buffer[2000];
        int nb;
        
        recv_output();

        emit_frame();

        speex_echo_cancellation(echo_state, storage, obuffer, dest);

        printf("Encoding one frame\n");
        speex_encode_int(estate, dest, &ebits);
        in_packet++;

        if (in_packet >= 1) {
            nb = speex_bits_nbytes(&ebits);

            /* Sending */
            nb = speex_bits_write(&ebits, buffer, sizeof buffer);

            printf("Sending %d bytes\n", nb);
            send_frame(buffer, nb);
            in_packet = 0;

            /* Reset encoding bits */
            speex_bits_reset(&ebits);
        }

        nread -= speex_frame_samples;
        memmove(storage, &storage[speex_frame_samples], nread*sizeof(storage[0]));
    }
}

void new_input_packet(const AVPacket *p) {
    AVFrame *f = avcodec_alloc_frame();
    int decoded;
    int err;

    f->nb_samples = speex_frame_samples;
    
    err = avcodec_decode_audio4(dctx, f, &decoded, p);
    if (err < 0)
        error2("Cannot decode", err);

    printf("Read packet of %d samples, %s format, %i rate\n", f->nb_samples,
            av_get_sample_fmt_name(f->format), f->sample_rate);
    new_for_speex((const short *) f->data[0], f->nb_samples);

    avcodec_free_frame(&f);
}

void init_input() {
    AVInputFormat *f = av_find_input_format("ALSA");

    if (!f)
        error("failed find ALSA");

    estate = speex_encoder_init(speex_lib_get_mode(SPEEX_MODEID_NB));
    speex_bits_init(&ebits);

    /* Prepare sample rate request */
    AVDictionary *options = NULL;
    av_dict_set(&options, "channels", "1", 0);
    char *rate = (char *) av_malloc(100);
    int val;
    speex_encoder_ctl(estate, SPEEX_GET_SAMPLING_RATE, &val);
    snprintf(rate, 100, "%d", val);
    av_dict_set(&options, "sample_rate", rate, AV_DICT_DONT_STRDUP_VAL);

    printf("Sample rate: %d Hz\n", val);

    speex_encoder_ctl(estate, SPEEX_GET_FRAME_SIZE, &speex_frame_samples);
    printf("Samples wanted by speex: %d\n", speex_frame_samples);

    echo_state = speex_echo_state_init(speex_frame_samples, 1024);

    int err;
    err = avformat_open_input(&fctx, "default", f, &options);
    if (err != 0)
        error("failed open");

    av_dict_free(&options);

    AVCodec *c = NULL;
    int nstream = av_find_best_stream(fctx, AVMEDIA_TYPE_AUDIO,
            -1, -1, &c, 0);
    if (nstream < 0)
        error("failed best stream");

    dctx = fctx->streams[nstream]->codec;

    err = avcodec_open2(dctx, c, NULL);
    if (err != 0)
        error("failed avcodec_open2");
}

void process_input()
{
    int err;
    do {
        AVPacket p;
        av_init_packet(&p);
        err = av_read_frame(fctx, &p);
        if (err == 0) {
            new_input_packet(&p);
            av_free_packet(&p);
        }
    } while(err == 0);

    speex_encoder_destroy(estate);

    avcodec_close(dctx);
    av_free(dctx);
    avformat_close_input(&fctx);
}
