/*
 * This file is part of GAPK (GSM Audio Pocket Knife).
 *
 * (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
 *
 * GAPK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * GAPK 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GAPK.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <talloc.h>
#include <string.h>
#include <assert.h>

#include <osmocom/core/utils.h>
#include <osmocom/codec/codec.h>

#include <osmocom/gapk/procqueue.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/common.h>

/* A good FR frame */
static const char *sample_frame_hex = \
	"d9ec9be212901f802335598c501f805bad3d4ba01f809b69df5a501f809cd1b4da";

static void talloc_ctx_walk_cb(const void *chunk, int depth,
	int max_depth, int is_ref, void *data)
{
	const char *chunk_name = talloc_get_name(chunk);
	int spaces_cnt;

	/* Hierarchical spacing */
	for (spaces_cnt = 0; spaces_cnt < depth; spaces_cnt++)
		printf("  ");

	/* Chunk info */
	printf("chunk %s: depth=%d\n", chunk_name, depth);
}

void pq_execute(struct osmo_gapk_pq *pq, uint8_t *frame, size_t len)
{
	struct osmo_gapk_pq_item *pq_item;
	unsigned int len_prev = len;
	uint8_t *buf_prev = frame;
	int rv;

	/* Iterate over all items in the chain */
	llist_for_each_entry(pq_item, &pq->items, list) {
		printf("Block '%s/%s/%s' in (len=%d): %s\n", pq->name,
			pq_item->cat_name, pq_item->sub_name, len_prev,
			osmo_hexdump(buf_prev, len_prev));

		/* Call item's processing handler */
		rv = pq_item->proc(pq_item->state, pq_item->buf, buf_prev, len_prev);
		assert(rv > 0);

		printf("Block '%s/%s/%s' out (len=%d): %s\n", pq->name,
			pq_item->cat_name, pq_item->sub_name, rv,
			osmo_hexdump(pq_item->buf, rv));

		buf_prev = pq_item->buf;
		len_prev = rv;
	}
}

void test_fr_concealment(struct osmo_gapk_pq *pq)
{
	uint8_t fb[GSM_FR_BYTES];
	int i;

	/* Init frame buffer with BFI */
	memset(fb, 0x00, sizeof(fb));
	fb[0] = 0xd0;

	/* Process a BFI frame */
	printf("[i] Process a BFI frame: %s\n", osmo_hexdump(fb, sizeof(fb)));
	pq_execute(pq, fb, sizeof(fb));
	printf("\n");

	/* Parse frame from string to hex */
	osmo_hexparse(sample_frame_hex, fb, GSM_FR_BYTES);

	/* Process a good frame (reset ECU) */
	printf("[i] Process a good frame: %s\n", osmo_hexdump(fb, sizeof(fb)));
	pq_execute(pq, fb, sizeof(fb));
	printf("\n");

	/* Now pretend that we do not receive any good frames anymore */
	memset(fb, 0x00, sizeof(fb));
	fb[0] = 0xd0;

	printf("[i] Pretend that we do not receive any good frames anymore\n");
	for (i = 0; i < 20; i++)
		pq_execute(pq, fb, sizeof(fb));

	printf("\n");
}

int main(int argc, char **argv)
{
	const struct osmo_gapk_codec_desc *codec;
	struct osmo_gapk_pq *pq;
	int rc;

	/* Enable tracking the use of NULL memory contexts */
	talloc_enable_null_tracking();

	/* Allocate a single processing chain */
	pq = osmo_gapk_pq_create("pq_ecu_test");
	assert(pq != NULL);

	/* Obtain FR codec description */
	codec = osmo_gapk_codec_get_from_type(CODEC_FR);
	assert(codec->ecu_proc);

	/* Put a FR ECU */
	rc = osmo_gapk_pq_queue_ecu(pq, codec);
	assert(rc == 0);

	/* Put a FR decoder */
	rc = osmo_gapk_pq_queue_codec(pq, codec, 0);
	assert(rc == 0);

	/* Prepare the chain */
	rc = osmo_gapk_pq_prepare(pq);
	assert(rc == 0);

	test_fr_concealment(pq);

	/* Release memory */
	osmo_gapk_pq_destroy(pq);

	/* Make sure we have no memleaks */
	talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL);

	/* Make both Valgrind and LeakSanitizer happy */
	talloc_disable_null_tracking();

	return 0;
}
