/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006-2013 Belledonne Communications, Grenoble

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.

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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "mediastreamer2/mediastream.h"
#include "mediastreamer2_tester.h"
#include "mediastreamer2_tester_private.h"
#include <math.h>

#ifdef _MSC_VER
#define unlink _unlink
#endif

static RtpProfile rtp_profile;

#define T140_PAYLOAD_TYPE 98
#define T140_RED_PAYLOAD_TYPE 99

static int tester_init(void) {
	ms_init();
	ortp_init();
	rtp_profile_set_payload(&rtp_profile, T140_PAYLOAD_TYPE, &payload_type_t140);
	rtp_profile_set_payload(&rtp_profile, T140_RED_PAYLOAD_TYPE, &payload_type_t140_red);
	return 0;
}

static int tester_cleanup(void) {
	ms_exit();
	rtp_profile_clear_all(&rtp_profile);
	return 0;
}

typedef struct _text_stream_tester_stats_t {
	OrtpEvQueue *q;
	rtp_stats_t rtp;
	int number_of_received_char;
	char received_chars[128];
} text_stream_tester_stats_t;

typedef struct _text_stream_tester_t {
	TextStream *ts;
	text_stream_tester_stats_t stats;
	char* local_ip;
	int local_rtp;
	int local_rtcp;
	int payload_type;
} text_stream_tester_t;


static void reset_stats(text_stream_tester_stats_t *s) {
	memset(s, 0, sizeof(text_stream_tester_stats_t));
}

void text_stream_tester_set_local_ip(text_stream_tester_t* obj, const char* ip) {
	char* new_ip = ip ? ms_strdup(ip) : NULL;
	if (obj->local_ip) ms_free(obj->local_ip);
	obj->local_ip = new_ip;
}

text_stream_tester_t* text_stream_tester_new() {
	text_stream_tester_t* tst = ms_new0(text_stream_tester_t, 1);
	text_stream_tester_set_local_ip(tst, "127.0.0.1");
	tst->local_rtp = -1; /*random*/
	tst->local_rtcp = -1; /*random*/
	return  tst;
}

text_stream_tester_t* text_stream_tester_create(const char* local_ip, int local_rtp, int local_rtcp) {
	text_stream_tester_t *tst = text_stream_tester_new();
	if (local_ip)
		text_stream_tester_set_local_ip(tst, local_ip);
	tst->local_rtp = local_rtp;
	tst->local_rtcp = local_rtcp;
	return tst;
}

void text_stream_tester_destroy(text_stream_tester_t* obj) {
	if(obj->local_ip) ms_free(obj->local_ip);
	ms_free(obj);
}

static void create_text_stream(text_stream_tester_t *tst, int payload_type) {
	tst->ts = text_stream_new2(tst->local_ip, tst->local_rtp, tst->local_rtcp);
	tst->local_rtp = rtp_session_get_local_port(tst->ts->ms.sessions.rtp_session);
	tst->local_rtcp = rtp_session_get_local_rtcp_port(tst->ts->ms.sessions.rtp_session);
	reset_stats(&tst->stats);
	rtp_session_set_multicast_loopback(tst->ts->ms.sessions.rtp_session, TRUE);
	tst->stats.q = ortp_ev_queue_new();
	rtp_session_register_event_queue(tst->ts->ms.sessions.rtp_session, tst->stats.q);
	tst->payload_type = payload_type;
}

static void destroy_text_stream(text_stream_tester_t *tst) {
	text_stream_stop(tst->ts);
}

static void init_text_streams(text_stream_tester_t *tst1, text_stream_tester_t *tst2, bool_t avpf, bool_t one_way, OrtpNetworkSimulatorParams *params, int payload_type) {
	create_text_stream(tst1, payload_type);
	create_text_stream(tst2, payload_type);

	/* Configure network simulator. */
	if ((params != NULL) && (params->enabled == TRUE)) {
		rtp_session_enable_network_simulation(tst1->ts->ms.sessions.rtp_session, params);
		rtp_session_enable_network_simulation(tst2->ts->ms.sessions.rtp_session, params);
	}
	
	text_stream_start(tst1->ts, &rtp_profile, tst2->local_ip, tst2->local_rtp, tst2->local_ip, tst2->local_rtcp, payload_type);
	text_stream_start(tst2->ts, &rtp_profile, tst1->local_ip, tst1->local_rtp, tst1->local_ip, tst1->local_rtcp, payload_type);
}

static void uninit_text_streams(text_stream_tester_t *tst1, text_stream_tester_t *tst2) {
	destroy_text_stream(tst1);
	destroy_text_stream(tst2);
}

static void event_queue_cb(MediaStream *ms, void *user_pointer) {
	text_stream_tester_t *tst = (text_stream_tester_t *)user_pointer;
	if (tst->stats.q != NULL) {
		OrtpEvent *ev = NULL;
		while ((ev = ortp_ev_queue_get(tst->stats.q)) != NULL) {
			OrtpEventType evt = ortp_event_get_type(ev);
			OrtpEventData *evd = ortp_event_get_data(ev);
			if (evt == ORTP_EVENT_RTT_CHARACTER_RECEIVED) {
				ms_message("Received RTT char: %lu, %c", (unsigned long)evd->info.received_rtt_character, (char)evd->info.received_rtt_character);
				tst->stats.received_chars[tst->stats.number_of_received_char++] = (char)evd->info.received_rtt_character;
			}
			ortp_event_destroy(ev);
		}
	}
}

static void basic_text_stream(void) {
	text_stream_tester_t* marielle = text_stream_tester_new();
	text_stream_tester_t* margaux = text_stream_tester_new();
	const char* helloworld = "Hello World !";
	int i = 0, strcmpresult = -2;
	
	init_text_streams(marielle, margaux, FALSE, FALSE, NULL, T140_PAYLOAD_TYPE /* ignored */);
	
	for (; i < strlen(helloworld); i++) {
		char c = helloworld[i];
		text_stream_putchar32(margaux->ts, (uint32_t)c);
	}
	
	BC_ASSERT_TRUE(wait_for_until_with_parse_events(&marielle->ts->ms, &margaux->ts->ms, &marielle->stats.number_of_received_char, strlen(helloworld), 5000, event_queue_cb, marielle, NULL, NULL));
	ms_message("Received message is: %s", marielle->stats.received_chars);
	strcmpresult = strcmp(marielle->stats.received_chars, helloworld);
	BC_ASSERT_EQUAL(strcmpresult, 0, int, "%d");

	uninit_text_streams(marielle, margaux);
	text_stream_tester_destroy(marielle);
	text_stream_tester_destroy(margaux);
}

static void basic_text_stream2(void) {
	text_stream_tester_t* marielle = text_stream_tester_new();
	text_stream_tester_t* margaux = text_stream_tester_new();
	const char* helloworld = "Hello World !";
	int i = 0, strcmpresult = -2;
	int dummy = 0;
	
	init_text_streams(marielle, margaux, FALSE, FALSE, NULL, T140_PAYLOAD_TYPE /* ignored */);
	
	for (; i < strlen(helloworld); i++) {
		char c = helloworld[i];
		text_stream_putchar32(margaux->ts, (uint32_t)c);
		wait_for_until_with_parse_events(&marielle->ts->ms, &margaux->ts->ms, &dummy, 1, 500, event_queue_cb, marielle, NULL, NULL);
	}
	
	BC_ASSERT_TRUE(wait_for_until(&marielle->ts->ms, &margaux->ts->ms, &marielle->stats.number_of_received_char, strlen(helloworld), 1000));
	ms_message("Received message is: %s", marielle->stats.received_chars);
	strcmpresult = strcmp(marielle->stats.received_chars, helloworld);
	BC_ASSERT_EQUAL(strcmpresult, 0, int, "%d");

	uninit_text_streams(marielle, margaux);
	text_stream_tester_destroy(marielle);
	text_stream_tester_destroy(margaux);
}

static void copy_paste_text_longer_than_rtt_buffer(void) {
	text_stream_tester_t* marielle = text_stream_tester_new();
	text_stream_tester_t* margaux = text_stream_tester_new();
	const char* helloworld = "Lorem ipsum belledonnum communicatum mediastrimum";
	int i = 0, strcmpresult = -2;
	
	init_text_streams(marielle, margaux, FALSE, FALSE, NULL, T140_PAYLOAD_TYPE /* ignored */);
	
	for (; i < strlen(helloworld); i++) {
		char c = helloworld[i];
		text_stream_putchar32(margaux->ts, (uint32_t)c);
	}
	
	BC_ASSERT_FALSE(wait_for_until_with_parse_events(&marielle->ts->ms, &margaux->ts->ms, &marielle->stats.number_of_received_char, strlen(helloworld), 5000, event_queue_cb, marielle, NULL, NULL));
	ms_message("Received message is: %s", marielle->stats.received_chars);
	strcmpresult = strcmp(marielle->stats.received_chars, helloworld);
	BC_ASSERT_TRUE(strcmpresult < 0);

	uninit_text_streams(marielle, margaux);
	text_stream_tester_destroy(marielle);
	text_stream_tester_destroy(margaux);
}

static void srtp_protected_text_stream(void) {
	text_stream_tester_t* marielle = text_stream_tester_new();
	text_stream_tester_t* margaux = text_stream_tester_new();
	const char* helloworld = "Hello World !";
	int i = 0, strcmpresult = -2;
	int dummy = 0;
	
	init_text_streams(marielle, margaux, FALSE, FALSE, NULL, T140_PAYLOAD_TYPE /* ignored */);
	BC_ASSERT_EQUAL(ms_media_stream_sessions_set_encryption_mandatory(&marielle->ts->ms.sessions, TRUE), 0, int, "%d");
	
	BC_ASSERT_TRUE(ms_srtp_supported());
	
	BC_ASSERT_TRUE(ms_media_stream_sessions_set_srtp_send_key_b64(&(marielle->ts->ms.sessions), MS_AES_128_SHA1_32, "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj") == 0);
	BC_ASSERT_TRUE(ms_media_stream_sessions_set_srtp_send_key_b64(&(margaux->ts->ms.sessions), MS_AES_128_SHA1_32, "6jCLmtRkVW9E/BUuJtYj/R2z6+4iEe06/DWohQ9F") == 0);
	BC_ASSERT_TRUE(ms_media_stream_sessions_set_srtp_recv_key_b64(&(margaux->ts->ms.sessions), MS_AES_128_SHA1_32, "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj") == 0);
	BC_ASSERT_TRUE(ms_media_stream_sessions_set_srtp_recv_key_b64(&(marielle->ts->ms.sessions), MS_AES_128_SHA1_32, "6jCLmtRkVW9E/BUuJtYj/R2z6+4iEe06/DWohQ9F") == 0);
	
	BC_ASSERT_TRUE(media_stream_secured(&marielle->ts->ms));
	BC_ASSERT_TRUE(media_stream_secured(&margaux->ts->ms));
	
	for (; i < strlen(helloworld); i++) {
		char c = helloworld[i];
		text_stream_putchar32(margaux->ts, (uint32_t)c);
		wait_for_until_with_parse_events(&marielle->ts->ms, &margaux->ts->ms, &dummy, 1, 500, event_queue_cb, marielle, NULL, NULL);
	}
	
	BC_ASSERT_TRUE(wait_for_until(&marielle->ts->ms, &margaux->ts->ms, &marielle->stats.number_of_received_char, strlen(helloworld), 1000));
	ms_message("Received message is: %s", marielle->stats.received_chars);
	strcmpresult = strcmp(marielle->stats.received_chars, helloworld);
	BC_ASSERT_EQUAL(strcmpresult, 0, int, "%d");

	uninit_text_streams(marielle, margaux);
	text_stream_tester_destroy(marielle);
	text_stream_tester_destroy(margaux);
}

static test_t tests[] = {
	{ "Basic text stream: copy paste short text", basic_text_stream },
	{ "Basic text stream: slow typing", basic_text_stream2 },
	{ "copy paste text longer than buffer size", copy_paste_text_longer_than_rtt_buffer },
	{ "slow typing with SRTP", srtp_protected_text_stream }
};

test_suite_t text_stream_test_suite = {
	"TextStream",
	tester_init,
	tester_cleanup,
	NULL,
	NULL,
	sizeof(tests) / sizeof(tests[0]),
	tests
};
