/*
 * (C) 2012 by Pablo Neira Ayuso <pablo@gnumonks.org>
 * (C) 2012 by On Waves ehf <http://www.on-waves.com>
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 */

#include <stdio.h>
#include <pcap.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <dlfcn.h>
#include <unistd.h>

#include <osmocom/core/msgb.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/select.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>

#include <osmocom/netif/rtp.h>
#include <osmocom/netif/osmux.h>

#include "osmo_pcap.h"

#define DOSMUXTEST 0

/*
 * This is the output handle for osmux, it stores last RTP sequence and
 * timestamp that has been used. There should be one per circuit ID.
 */
static struct osmux_out_handle *h_output;

static void tx_cb(struct msgb *msg, void *data)
{
	printf("now sending message scheduled [emulated], msg=%p\n", msg);
	/*
	 * Here we should call the real function that sends the message
	 * instead of releasing it.
	 */
	msgb_free(msg);
}

static void deliver(struct msgb *batch_msg)
{
	struct osmux_hdr *osmuxh;

	printf("sending batch (len=%d) [emulated]\n", batch_msg->len);

	/* This code below belongs to the osmux receiver */
	while((osmuxh = osmux_xfrm_output_pull(batch_msg)) != NULL)
		osmux_xfrm_output_sched(h_output, osmuxh);
	msgb_free(batch_msg);
}

/*
 * This is the input handle for osmux. It stores the last osmux sequence that
 * has been used and the deliver function that sends the osmux batch.
 */
struct osmux_in_handle *h_input;

#define MAX_CONCURRENT_CALLS	8

static int ccid[MAX_CONCURRENT_CALLS] = { -1, -1, -1, -1, -1, -1, -1, -1 };

static void register_ccid(uint32_t ssrc)
{
       int i, found = 0;

       for (i=0; i<MAX_CONCURRENT_CALLS; i++) {
               if (ccid[i] == ssrc)
                       continue;
               if (ccid[i] < 0) {
                       found = 1;
                       break;
               }
       }

       if (found) {
               ccid[i] = ssrc;
               LOGP(DOSMUXTEST, LOGL_DEBUG, "mapping ssrc=%u to ccid=%d\n",
                       ntohl(ssrc), i);
       } else {
               LOGP(DOSMUXTEST, LOGL_ERROR, "cannot map ssrc to ccid!\n");
       }
}

static int get_ccid(uint32_t ssrc)
{
       int i, found = 0;

       for (i=0; i<MAX_CONCURRENT_CALLS; i++) {
               if (ccid[i] == ssrc) {
                       found = 1;
                       break;
               }
       }

       return found ? i : -1;
}

static int pcap_test_run(struct msgb *msg)
{
	int ret, ccid;
	struct rtp_hdr *rtph;

	rtph = osmo_rtp_get_hdr(msg);
	if (rtph == NULL)
		return 0;

	ccid = get_ccid(rtph->ssrc);
	if (ccid < 0)
		register_ccid(rtph->ssrc);

	while ((ret = osmux_xfrm_input(h_input, msg, ccid)) > 0) {
		/* batch full, deliver it */
		osmux_xfrm_input_deliver(h_input);
	}
	if (ret == -1)
		printf("something is wrong\n");

	return 0;
}

static struct osmo_pcap osmo_pcap;

static void osmo_pcap_pkt_timer_cb(void *data)
{
	if (osmo_pcap_test_run(&osmo_pcap, IPPROTO_UDP, pcap_test_run) < 0) {
		osmo_pcap_stats_printf();
		printf("\e[1;34mDone.\e[0m\n");
		osmo_pcap_test_close(osmo_pcap.h);
		exit(EXIT_SUCCESS);
	}
}

struct log_info_cat osmux_test_cat[] = {
	[DOSMUXTEST] = {
		.name = "DOSMUXTEST",
		.description = "osmux test",
		.color = "\033[1;35m",
		.enabled = 1, .loglevel = LOGL_NOTICE,
	},
};

const struct log_info osmux_log_info = {
	.filter_fn = NULL,
	.cat = osmux_test_cat,
	.num_cat = ARRAY_SIZE(osmux_test_cat),
};

static void *tall_test;

int main(int argc, char *argv[])
{
	int ret;

	if (argc != 2) {
		fprintf(stderr, "Wrong usage:\n");
		fprintf(stderr, "%s <pcap_file>\n", argv[0]);
		fprintf(stderr, "example: %s file.pcap\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	tall_test = talloc_named_const(NULL, 1, "osmux_pcap_test");
	osmo_init_logging(&osmux_log_info);
	log_set_log_level(osmo_stderr_target, LOGL_DEBUG);
	log_set_use_color(osmo_stderr_target, 0);

	osmo_pcap_init();

	printf("\e[1;34mStarting test...\e[0m\n");

	osmo_pcap.h = osmo_pcap_test_open(argv[1]);
	if (osmo_pcap.h == NULL)
		exit(EXIT_FAILURE);

	osmo_pcap.timer.cb = osmo_pcap_pkt_timer_cb;

	h_input = osmux_xfrm_input_alloc(tall_test);
	osmux_xfrm_input_set_initial_seqnum(h_input, 0);
	osmux_xfrm_input_set_batch_factor(h_input, 4);
	osmux_xfrm_input_set_deliver_cb(h_input, deliver, NULL);

	h_output = osmux_xfrm_output_alloc(tall_test);
	osmux_xfrm_output_set_rtp_ssrc(h_output, 0);
	osmux_xfrm_output_set_rtp_pl_type(h_output, 98);
	osmux_xfrm_output_set_tx_cb(h_output, tx_cb, NULL);

	/* first run */
	osmo_pcap_pkt_timer_cb(NULL);

	while(1) {
		osmo_select_main(0);
	}

	talloc_free(h_output);

	return ret;
}