/*
* Copyright (C) 2020, University of the Basque Country (UPV/EHU)
* Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com>
*
* The original file was part of Open Source Doubango Framework
* Copyright (C) 2010-2011 Mamadou Diop.
* Copyright (C) 2012 Doubango Telecom <http://doubango.org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO 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.
*
* DOUBANGO 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 DOUBANGO.
*
*/

#ifndef TNET_TEST_ICE_H
#define TNET_TEST_ICE_H

#undef kStunUsrName
#undef kStunPwd

#define kStunUsrName			"bossiel@yahoo.fr"
#define kStunPwd				"tinynet"
#define kStunServerIP			"ns313841.ovh.net" /*"numb.viagenie.ca"*/ /*stun.ekiga.net*/

#define kSkipHosts				1
#define kSkipReflexives			1 // server reflexive: STUN
#define kSkipPeers				1 // peer reflexive
#define kSkipRelays				0 // relays: TURN

#define kTurnTrue				1
#define kStunTrue				1
#define kTurnFalse				0
#define kStunFalse				0

static const tsk_bool_t use_rtcp = tsk_false;

static struct tnet_ice_ctx_s *p_ice_ctx1 = tsk_null;
static struct tnet_ice_ctx_s *p_ice_ctx2 = tsk_null;

static void test_ice_print_local_candidates(const struct tnet_ice_ctx_s *pc_ctx)
{
	tsk_size_t index = 0;
	const tnet_ice_candidate_t* candidate;
	char* p_str = tsk_null;

	while ((candidate = tnet_ice_ctx_get_local_candidate_at(pc_ctx, index++))) {
		tsk_strcat_2(&p_str, "%s\r\n", tnet_ice_candidate_tostring((tnet_ice_candidate_t*)candidate));
	}
	TSK_DEBUG_INFO("ICE LOCAL CANDIDATES:\n%s", p_str);
	TSK_FREE(p_str);
}

static char* test_ice_get_local_candidates(const struct tnet_ice_ctx_s *pc_ctx)
{
	tsk_size_t index = 0;
	const tnet_ice_candidate_t* candidate;
	char* p_str = tsk_null;

	while ((candidate = tnet_ice_ctx_get_local_candidate_at(pc_ctx, index++))) {
		if (kSkipHosts && candidate->type_e == tnet_ice_cand_type_host) {
			continue;
		}
		if (kSkipReflexives && candidate->type_e == tnet_ice_cand_type_srflx) {
			continue;
		}
		if (kSkipRelays && candidate->type_e == tnet_ice_cand_type_relay) {
			continue;
		}
		if (kSkipPeers && candidate->type_e == tnet_ice_cand_type_prflx) {
			continue;
		}
		tsk_strcat_2(&p_str, "%s\r\n", tnet_ice_candidate_tostring((tnet_ice_candidate_t*)candidate));
	}
	return p_str;
}

static int test_ice_rtp_callback(const void* callback_data, const uint8_t* data_ptr, tsk_size_t data_size, tnet_fd_t local_fd, const struct sockaddr_storage* remote_addr)
{
	struct tnet_ice_ctx_s *p_ice_ctx = (struct tnet_ice_ctx_s *)callback_data;

	TSK_DEBUG_INFO("\n\nICE rtp callback (incoming data): %.*s\n\n", data_size, data_ptr);

#if 0
	tnet_ice_ctx_send_turn_rtp(p_ice_ctx, data_ptr, data_size);
#endif

	return 0;
}

static int test_ice_state_callback(const tnet_ice_event_t *e)
{
	struct tnet_ice_ctx_s *p_ice_ctx = (struct tnet_ice_ctx_s *)e->ctx;
	int ret = 0;

	TSK_DEBUG_INFO("ICE state callback: %s", e->phrase);

	switch(e->type)
	{
		case tnet_ice_event_type_gathering_completed:
			{
				test_ice_print_local_candidates(p_ice_ctx);
				if (p_ice_ctx == p_ice_ctx1) {
					if ((ret = tnet_ice_ctx_start(p_ice_ctx2))) {
						goto bail;
					}
				}
				else {
					const tnet_ice_candidate_t* candidate;
					char* p_cand;

					p_cand = test_ice_get_local_candidates(p_ice_ctx2);
					candidate = tnet_ice_ctx_get_local_candidate_first(p_ice_ctx2);
					ret = tnet_ice_ctx_set_remote_candidates(p_ice_ctx1, p_cand, candidate->ufrag, candidate->pwd, tsk_true, tsk_false);
					if (ret == 0) {
						TSK_FREE(p_cand);
						p_cand = test_ice_get_local_candidates(p_ice_ctx1);
						candidate = tnet_ice_ctx_get_local_candidate_first(p_ice_ctx1);
						ret = tnet_ice_ctx_set_remote_candidates(p_ice_ctx2, p_cand, candidate->ufrag, candidate->pwd, tsk_false, tsk_false);
						TSK_FREE(p_cand);
					}
				}
				break;
			}

		case tnet_ice_event_type_conncheck_succeed:
			{
				const char kTurnData[] = "TURN data to send for testing";
				const tnet_ice_candidate_t *candidate_offer, *candidate_answer_src, *candidate_answer_dest;

				// === RTP === //
				ret = tnet_ice_ctx_get_nominated_symetric_candidates(p_ice_ctx, TNET_ICE_CANDIDATE_COMPID_RTP, &candidate_offer, &candidate_answer_src, &candidate_answer_dest);
				if (ret == 0) {
					TSK_DEBUG_INFO("Nominated candidate(RTP): Offer=[[%s]], AnswerSrc=[[%s]], AnswerDest=[[%s]]",
						tnet_ice_candidate_tostring((tnet_ice_candidate_t*)candidate_offer),
						tnet_ice_candidate_tostring((tnet_ice_candidate_t*)candidate_answer_src),
						tnet_ice_candidate_tostring((tnet_ice_candidate_t*)candidate_answer_dest));

					if (tnet_ice_ctx_is_turn_rtp_active(p_ice_ctx)) {
						tnet_ice_ctx_send_turn_rtp(p_ice_ctx, kTurnData, sizeof(kTurnData));
					}
				}
				// === RTCP === //
				if (use_rtcp) {
					ret = tnet_ice_ctx_get_nominated_symetric_candidates(p_ice_ctx, TNET_ICE_CANDIDATE_COMPID_RTCP, &candidate_offer, &candidate_answer_src, &candidate_answer_dest);
					if (ret == 0) {
						TSK_DEBUG_INFO("Nominated candidate(RTCP): Offer=[[%s]], AnswerSrc=[[%s]], AnswerDest=[[%s]]",
							tnet_ice_candidate_tostring((tnet_ice_candidate_t*)candidate_offer),
							tnet_ice_candidate_tostring((tnet_ice_candidate_t*)candidate_answer_src),
							tnet_ice_candidate_tostring((tnet_ice_candidate_t*)candidate_answer_dest));
						if (tnet_ice_ctx_is_turn_rtcp_active(p_ice_ctx)) {
							tnet_ice_ctx_send_turn_rtcp(p_ice_ctx, kTurnData, sizeof(kTurnData));
						}
					}
				}
				break;
			}
	}

bail:
	return ret;
}

void test_ice()
{
	int ret;
	static const tsk_bool_t use_ipv6 = tsk_false;
	static const tsk_bool_t use_ice_jingle = tsk_false;
	static const tsk_bool_t use_video = tsk_false;
	
	if (!(p_ice_ctx1 = tnet_ice_ctx_create(use_ice_jingle, use_ipv6, use_rtcp, use_video, test_ice_state_callback, tsk_null))) {
		goto bail;
	}
	if (!(p_ice_ctx2 = tnet_ice_ctx_create(use_ice_jingle, use_ipv6, use_rtcp, use_video, test_ice_state_callback, tsk_null))) {
		goto bail;
	}
	if ((ret = tnet_ice_ctx_set_turn_enabled(p_ice_ctx1, 1))) {
		goto bail;
	}
	if ((ret = tnet_ice_ctx_set_turn_enabled(p_ice_ctx2, 1))) {
		goto bail;
	}
	if ((ret = tnet_ice_ctx_set_stun_enabled(p_ice_ctx1, 1))) {
		goto bail;
	}
	if ((ret = tnet_ice_ctx_set_stun_enabled(p_ice_ctx2, 1))) {
		goto bail;
	}
	if ((ret = tnet_ice_ctx_set_userdata(p_ice_ctx1, p_ice_ctx1))) {
		goto bail;
	}
	if ((ret = tnet_ice_ctx_set_userdata(p_ice_ctx2, p_ice_ctx2))) {
		goto bail;
	}
	if ((ret = tnet_ice_ctx_rtp_callback(p_ice_ctx1, test_ice_rtp_callback, p_ice_ctx1))) {
		goto bail;
	}
	if ((ret = tnet_ice_ctx_rtp_callback(p_ice_ctx2, test_ice_rtp_callback, p_ice_ctx2))) {
		goto bail;
	}
#if 0 //@deprecated
	if ((ret = tnet_ice_ctx_set_stun(p_ice_ctx1, kStunServerIP, 3478, kStunSoftware, kStunUsrName, kStunPwd))) {
		goto bail;
	}
	if ((ret = tnet_ice_ctx_set_stun(p_ice_ctx2, kStunServerIP, 3478, kStunSoftware, kStunUsrName, kStunPwd))) {
		goto bail;
	}
#else
	tnet_ice_ctx_add_server(p_ice_ctx1, "udp", kStunServerIP, 3478, kTurnFalse, kStunTrue, kStunUsrName, kStunPwd); // STUN-UDP
	tnet_ice_ctx_add_server(p_ice_ctx1, "tcp", kStunServerIP, 3478, kTurnTrue, kStunFalse, kStunUsrName, kStunPwd); // TURN-TCP
	tnet_ice_ctx_add_server(p_ice_ctx2, "udp", kStunServerIP, 3478, kTurnFalse, kStunTrue, kStunUsrName, kStunPwd); // STUN-UDP
	tnet_ice_ctx_add_server(p_ice_ctx2, "tcp", kStunServerIP, 3478, kTurnTrue, kStunFalse, kStunUsrName, kStunPwd); // TURN-TCP
#endif
	if ((ret = tnet_ice_ctx_start(p_ice_ctx1))) {
		goto bail;
	}
	// start ctx2 when we finish gathering ctx1's candidates
	//if ((ret = tnet_ice_ctx_start(p_ice_ctx2))) {
	//	goto bail;
	//}

	getchar();
	
	// ret = tnet_ice_ctx_stop(p_ice_ctx1);
	// ret = tnet_ice_ctx_stop(p_ice_ctx2);

bail:
	TSK_OBJECT_SAFE_FREE(p_ice_ctx1);
	TSK_OBJECT_SAFE_FREE(p_ice_ctx2);
}


#endif /* TNET_TEST_ICE_H */