/*
 * Copyright (c) 2010-2022 Belledonne Communications SARL.
 *
 * This file is part of mediastreamer2
 * (see https://gitlab.linphone.org/BC/public/mediastreamer2).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef BV16_USE_BUILD_INTERFACE
#include <bvcommon/typedef.h>
#include <bv16/bv16cnst.h>
#include <bvcommon/bvcommon.h>
#include <bv16/bv16strct.h>
#include <bv16/bv16.h>
#include <bv16/bitpack.h>
#else
#include <bv16-floatingpoint/bvcommon/typedef.h>
#include <bv16-floatingpoint/bv16/bv16cnst.h>
#include <bv16-floatingpoint/bvcommon/bvcommon.h>
#include <bv16-floatingpoint/bv16/bv16strct.h>
#include <bv16-floatingpoint/bv16/bv16.h>
#include <bv16-floatingpoint/bv16/bitpack.h>
#endif

#include <bctoolbox/defs.h>

#include "mediastreamer2/mscodecutils.h"
#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/msticker.h"

#ifdef _MSC_VER
#include <malloc.h>
#include <stdint.h>
#define alloca _alloca
#endif

/* signal and bitstream frame size in byte */
#define SIGNAL_FRAME_SIZE 80
#define BITSTREAM_FRAME_SIZE 10

typedef struct EncState {
	struct BV16_Encoder_State state;
	uint32_t ts; // timestamp
	int ptime;
	int max_ptime;
	int nsamples;
	int frame_size;
	int size_samples;
	MSBufferizer *bufferizer;
} EncState;

static int enc_set_ptime(MSFilter *f, void *arg) {
	EncState *s = (EncState *)f->data;
	s->ptime = *(int *)arg;
	if (s->ptime > s->max_ptime) {
		s->ptime = s->max_ptime;
	} else if (s->ptime % 5) {
		// if the ptime is not a mulptiple of 5, go to the next multiple
		s->ptime = s->ptime - s->ptime % 5 + 5;
	}
	ms_message("MSBV16Enc: got ptime=%i ", s->ptime);
	return 0;
}

static int enc_get_ptime(MSFilter *f, void *arg) {
	EncState *s = (EncState *)f->data;
	*((int *)arg) = s->ptime;
	return 0;
}

static int enc_add_fmtp(MSFilter *f, void *arg) {
	EncState *s = (EncState *)f->data;
	const char *fmtp = (const char *)arg;
	char tmp[64];
	tmp[0] = '\0';
	if (fmtp_get_value(fmtp, "maxptime", tmp, sizeof(tmp))) {
		s->max_ptime = atoi(tmp);
		if (s->max_ptime < 10 || s->max_ptime > 100) {
			ms_warning("MSBV16Enc: unknown value [%i] for maxptime, use default value (100) instead", s->max_ptime);
			s->max_ptime = 100;
		}
		ms_message("MSBV16Enc: got maxptime=%i", s->max_ptime);
	} else if (fmtp_get_value(fmtp, "ptime", tmp, sizeof(tmp))) {
		int val = atoi(tmp);
		return enc_set_ptime(f, &val);
	}
	return 0;
}

static int enc_add_attr(MSFilter *f, void *arg) {
	const char *attr = (const char *)arg;
	ms_message("MSBV16Enc: enc_add_attr %s", attr);

	if (strstr(attr, "ptime:") != NULL) {
		int ptime = atoi(attr + 6);
		return enc_set_ptime(f, &ptime);
	}
	return 0;
}

static int enc_get_sample_rate(BCTBX_UNUSED(MSFilter *f), void *arg) {
	*((int *)arg) = 8000;
	return 0;
}

static int get_channels(BCTBX_UNUSED(MSFilter *f), void *arg) {
	*((int *)arg) = 1;
	return 0;
}
static void enc_init(MSFilter *f) {
	EncState *s = (EncState *)ms_new0(EncState, 1);
	Reset_BV16_Encoder(&(s->state));
	s->nsamples = FRSZ;
	s->size_samples = FRSZ * sizeof(short);
	s->ptime = 10;
	s->max_ptime = 100;
	s->bufferizer = ms_bufferizer_new();
	f->data = s;

	ms_message("MSBV16Enc Init ");
}

static void enc_uninit(MSFilter *f) {
	EncState *s = (EncState *)f->data;
	ms_bufferizer_destroy(s->bufferizer);
	ms_free(s);
	ms_message("MSBV16Enc Uninit ");
}

/***
Encodes 8 kHz-sampled narrowband speech at a bit rate of or 16 kbit/s,
uses 5 ms frames.
The encoder receives 10 ms speech => 160 bytes.
***/
static void enc_process(MSFilter *f) {
	EncState *s = (EncState *)f->data;
	struct BV16_Bit_Stream bs;
	short *buf = NULL;
	mblk_t *inputMessage = NULL, *outputMessage = NULL;
	int frame_per_packet = s->ptime / 5;
	size_t in_rcvd_bytes = 0;

	in_rcvd_bytes = SIGNAL_FRAME_SIZE * frame_per_packet;
	buf = (short *)alloca(in_rcvd_bytes);
	memset((void *)buf, 0, in_rcvd_bytes);

	while ((inputMessage = ms_queue_get(f->inputs[0])) != NULL) {
		ms_bufferizer_put(s->bufferizer, inputMessage);
	}

	/* process ptimes ms of data : (ptime in ms)/1000->ptime is seconds * 8000(sample rate) * 2(byte per sample) */
	while (ms_bufferizer_get_avail(s->bufferizer) >= in_rcvd_bytes) {
		int bufferIndex;
		outputMessage = allocb(BITSTREAM_FRAME_SIZE * frame_per_packet,
		                       0); /* output bitStream is 80 bits long * number of samples */
		/* process buffer in 5 ms frames but read everything first*/
		ms_bufferizer_read(s->bufferizer, (uint8_t *)buf, in_rcvd_bytes);
		for (bufferIndex = 0; bufferIndex < frame_per_packet; bufferIndex++) {
			BV16_Encode(&bs, &s->state, (short *)&buf[bufferIndex * FRSZ]);
			BV16_BitPack((UWord8 *)outputMessage->b_wptr, &bs);
			outputMessage->b_wptr += BITSTREAM_FRAME_SIZE;
		}
		mblk_set_timestamp_info(outputMessage, s->ts);
		ms_bufferizer_fill_current_metas(s->bufferizer, outputMessage);
		ms_queue_put(f->outputs[0], outputMessage);
		s->ts += FRSZ * frame_per_packet;
	}
}

static MSFilterMethod enc_methods[] = {{MS_FILTER_ADD_FMTP, enc_add_fmtp},
                                       {MS_FILTER_ADD_ATTR, enc_add_attr},
                                       {MS_FILTER_GET_SAMPLE_RATE, enc_get_sample_rate},
                                       {MS_FILTER_GET_NCHANNELS, get_channels},
                                       {MS_AUDIO_ENCODER_SET_PTIME, enc_set_ptime},
                                       {MS_AUDIO_ENCODER_GET_PTIME, enc_get_ptime},
                                       {0, NULL}};

#ifdef _MSC_VER

MSFilterDesc ms_bv16_enc_desc = {MS_BV16_ENC_ID,
                                 "MSBv16Enc",
                                 N_("The BV16 full-rate codec"),
                                 MS_FILTER_ENCODER,
                                 "bv16",
                                 1,
                                 1,
                                 enc_init,
                                 NULL,
                                 enc_process,
                                 NULL,
                                 enc_uninit,
                                 enc_methods};

#else

MSFilterDesc ms_bv16_enc_desc = {.id = MS_BV16_ENC_ID,
                                 .name = "MSBv16Enc",
                                 .text = N_("The BV16 full-rate codec"),
                                 .category = MS_FILTER_ENCODER,
                                 .enc_fmt = "bv16",
                                 .ninputs = 1,
                                 .noutputs = 1,
                                 .init = enc_init,
                                 .process = enc_process,
                                 .uninit = enc_uninit,
                                 .methods = enc_methods};

#endif

typedef struct DecState {
	struct BV16_Decoder_State state;
	uint32_t ts; // timestamp
	bool_t plc;
	int packet_ms_size;
	MSConcealerContext *concealer;
} DecState;

static void dec_init(MSFilter *f) {
	DecState *s = ms_new0(DecState, 1);
	Reset_BV16_Decoder(&s->state);
	f->data = s;
	s->plc = 1;
	s->packet_ms_size = 10;
	ms_message("MSBV16Dec Init ");
}
static void dec_preprocess(MSFilter *f) {
	DecState *s = (DecState *)f->data;
	if (s->plc) {
		s->concealer = ms_concealer_context_new(UINT32_MAX);
	} else {
		s->concealer = NULL;
	}
}

static void dec_postprocess(MSFilter *f) {
	DecState *s = (DecState *)f->data;
	if (s->plc) ms_concealer_context_destroy(s->concealer);
}

static void dec_uninit(MSFilter *f) {
	DecState *s = (DecState *)f->data;
	Reset_BV16_Decoder((struct BV16_Decoder_State *)f->data);
	ms_free(s);
	ms_message("MSBV16Dec Uninit ");
}

static void dec_process(MSFilter *f) {
	DecState *s = (DecState *)f->data;
	mblk_t *inputMessage, *outputMessage;
	struct BV16_Bit_Stream bs;

	while ((inputMessage = ms_queue_get(f->inputs[0])) != NULL) {

		while (inputMessage->b_rptr < inputMessage->b_wptr) {
			outputMessage = allocb(SIGNAL_FRAME_SIZE, 0);
			mblk_meta_copy(inputMessage, outputMessage);
			BV16_BitUnPack((UWord8 *)inputMessage->b_rptr, &bs);
			BV16_Decode(&bs, &s->state, (short *)(outputMessage->b_wptr));
			outputMessage->b_wptr += SIGNAL_FRAME_SIZE;
			inputMessage->b_rptr += BITSTREAM_FRAME_SIZE;
			ms_queue_put(f->outputs[0], outputMessage);
			if (s->plc) ms_concealer_inc_sample_time(s->concealer, f->ticker->time, 5, 1);
		}
		freemsg(inputMessage);
	}
	// called every 10 ms
	if (s->plc && ms_concealer_context_is_concealement_required(s->concealer, f->ticker->time)) {
		int ms_concealed;
		// need to conceal 10 ms
		for (ms_concealed = 0; ms_concealed < s->packet_ms_size; ms_concealed += 5) {
			outputMessage = allocb(SIGNAL_FRAME_SIZE, 0);
			BV16_PLC(&s->state, (short *)outputMessage->b_wptr);
			outputMessage->b_wptr += SIGNAL_FRAME_SIZE;
			mblk_set_plc_flag(outputMessage, 1);
			ms_queue_put(f->outputs[0], outputMessage);
		}
		ms_concealer_inc_sample_time(s->concealer, f->ticker->time, 10, 0);
	}
}

static int dec_enable_plc(MSFilter *f, void *arg) {
	DecState *s = (DecState *)f->data;
	bool_t has_plc = *((bool_t *)arg);
	s->plc = has_plc;
	return 0;
}

static int dec_have_plc(BCTBX_UNUSED(MSFilter *f), void *arg) {
	*((int *)arg) = 1;
	return 0;
}

static int dec_get_sample_rate(BCTBX_UNUSED(MSFilter *f), void *arg) {
	*((int *)arg) = 8000;
	return 0;
}

static MSFilterMethod dec_methods[] = {{MS_FILTER_GET_SAMPLE_RATE, dec_get_sample_rate},
                                       {MS_FILTER_GET_NCHANNELS, get_channels},
                                       {MS_DECODER_HAVE_PLC, dec_have_plc},
                                       {MS_DECODER_ENABLE_PLC, dec_enable_plc},
                                       {0, NULL}};

#ifdef _MSC_VER

MSFilterDesc ms_bv16_dec_desc = {MS_BV16_DEC_ID,
                                 "MSBv16Dec",
                                 N_("The BV16 codec"),
                                 MS_FILTER_DECODER,
                                 "bv16",
                                 1,
                                 1,
                                 dec_init,
                                 dec_preprocess,
                                 dec_process,
                                 dec_postprocess,
                                 dec_uninit,
                                 dec_methods,
                                 MS_FILTER_IS_PUMP};

#else

MSFilterDesc ms_bv16_dec_desc = {.id = MS_BV16_DEC_ID,
                                 .name = "MSBv16Dec",
                                 .text = N_("The BV16 codec"),
                                 .category = MS_FILTER_DECODER,
                                 .enc_fmt = "bv16",
                                 .ninputs = 1,
                                 .noutputs = 1,
                                 .init = dec_init,
                                 .preprocess = dec_preprocess,
                                 .process = dec_process,
                                 .postprocess = dec_postprocess,
                                 .uninit = dec_uninit,
                                 .methods = dec_methods,
                                 .flags = MS_FILTER_IS_PUMP};

#endif

MS_FILTER_DESC_EXPORT(ms_bv16_dec_desc)
MS_FILTER_DESC_EXPORT(ms_bv16_enc_desc)
