#include <memory>
#include "sockutil.h"
#include "aio-timeout.h"
#include "sip-uac.h"
#include "sip-message.h"
#include "sip-transport.h"
#include "sip-timer.h"
#include "port/ip-route.h"
#include "http-parser.h"
#include "http-header-auth.h"
#include "sys/thread.h"
#include "sys/system.h"
#include "uri-parse.h"
#include "cstringext.h"
#include "base64.h"
#include "md5.h"
#include <stdint.h>

struct sip_uac_test_t
{
	socket_t udp;
	socklen_t addrlen;
	struct sockaddr_storage addr;

	http_parser_t* parser;
	struct sip_agent_t* sip;
	struct sip_transport_t transport;
};

static int sip_uac_transport_via(void* transport, const char* destination, char protocol[16], char local[128], char dns[128])
{
	int r;
	char ip[65];
	u_short port;
	struct uri_t* uri;
	
	struct sip_uac_test_t *test = (struct sip_uac_test_t *)transport;

	// rfc3263 4.1 Selecting a Transport Protocol
	// 1. If the URI specifies a transport protocol in the transport parameter,
	//    that transport protocol SHOULD be used. Otherwise, if no transport 
	//    protocol is specified, but the TARGET(maddr) is a numeric IP address, 
	//    the client SHOULD use UDP for a SIP URI, and TCP for a SIPS URI.
	// 2. if no transport protocol is specified, and the TARGET is not numeric, 
	//    but an explicit port is provided, the client SHOULD use UDP for a SIP URI, 
	//    and TCP for a SIPS URI
	// 3. Otherwise, if no transport protocol or port is specified, and the target 
	//    is not a numeric IP address, the client SHOULD perform a NAPTR query for 
	//    the domain in the URI.

	// The client SHOULD try the first record. If an attempt should fail, based on 
	// the definition of failure in Section 4.3, the next SHOULD be tried, and if 
	// that should fail, the next SHOULD be tried, and so on.

	test->addrlen = sizeof(test->addr);
	memset(&test->addr, 0, sizeof(test->addr));
	snprintf(protocol, 16, "%s", "UDP");

	uri = uri_parse(destination, strlen(destination));
	if (!uri)
		return -1; // invalid uri

	// rfc3263 4-Client Usage (p5)
	// once a SIP server has successfully been contacted (success is defined below), 
	// all retransmissions of the SIP request and the ACK for non-2xx SIP responses 
	// to INVITE MUST be sent to the same host.
	// Furthermore, a CANCEL for a particular SIP request MUST be sent to the same 
	// SIP server that the SIP request was delivered to.

	// TODO: sips port
	r = socket_addr_from(&test->addr, &test->addrlen, uri->host, uri->port ? uri->port : SIP_PORT);
	if (0 == r)
	{
		socket_addr_to((struct sockaddr*)&test->addr, test->addrlen, ip, &port);
		socket_getname(test->udp, local, &port);
		r = ip_route_get(ip, local);
		if (0 == r)
		{
			dns[0] = 0;
			struct sockaddr_storage ss;
			socklen_t len = sizeof(ss);
			if (0 == socket_addr_from(&ss, &len, local, port))
				socket_addr_name((struct sockaddr*)&ss, len, dns, 128);

			if (SIP_PORT != port)
				snprintf(local + strlen(local), 128 - strlen(local), ":%hu", port);

			if (NULL == strchr(dns, '.'))
				snprintf(dns, 128, "%s", local); // don't have valid dns
		}
	}

	uri_free(uri);
	return r;
}

static int sip_uac_transport_send(void* transport, const void* data, size_t bytes)
{
	struct sip_uac_test_t *test = (struct sip_uac_test_t *)transport;

	//char p1[1024];
	//char p2[1024];
	((char*)data)[bytes] = 0;
	printf("%s\n", (const char*)data);
	int r = socket_sendto(test->udp, data, bytes, 0, (struct sockaddr*)&test->addr, test->addrlen);
	return r == bytes ? 0 : -1;
}

static void sip_uac_recv_reply(struct sip_uac_test_t *test)
{
	uint8_t buffer[2 * 1024];
	struct sockaddr_storage addr;
	socklen_t addrlen = sizeof(addr);

	do
	{
		int r = socket_recvfrom(test->udp, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, &addrlen);

		size_t n = r;
		if (0 == http_parser_input(test->parser, buffer, &n))
		{
			struct sip_message_t* reply = sip_message_create(SIP_MESSAGE_REPLY);
			r = sip_message_load(reply, test->parser);
			assert(0 == sip_agent_input(test->sip, reply, NULL));
			sip_message_destroy(reply);

			http_parser_clear(test->parser);
			break;
		}
	} while (1);
}

static int sip_uac_onregister(void* param, const struct sip_message_t* reply, struct sip_uac_transaction_t* t, int code)
{
	char buffer[1024];
	struct sip_uac_test_t *test = (struct sip_uac_test_t *)param;

	if (200 <= code && code < 300)
	{
	}
	else if (401 == code)
	{
		// https://blog.csdn.net/yunlianglinfeng/article/details/81109380
		// http://www.voidcn.com/article/p-oqqbqgvd-bgn.html
		const char* h;
		std::shared_ptr<sip_uac_transaction_t> t(sip_uac_register(test->sip, "sip:34020000001320000001@192.168.154.1", "sip:192.168.154.128", 3600, sip_uac_onregister, param), sip_uac_transaction_release);
		h = http_get_header_by_name(test->parser, "Call-ID");
		sip_uac_add_header(t.get(), "Call-ID", h); // All registrations from a UAC SHOULD use the same Call-ID
		h = http_get_header_by_name(test->parser, "CSeq");
		snprintf(buffer, sizeof(buffer), "%u REGISTER", atoi(h) + 1);
		sip_uac_add_header(t.get(), "CSeq", buffer); // A UA MUST increment the CSeq value by one for each REGISTER request with the same Call-ID

		// Unauthorized
		struct http_header_www_authenticate_t auth;
		memset(&auth, 0, sizeof(auth));
		h = http_get_header_by_name(test->parser, "WWW-Authenticate");
		assert(0 == http_header_www_authenticate(h, &auth));
		switch (auth.scheme)
		{
		case HTTP_AUTHENTICATION_DIGEST:
			//HA1=md5(username:realm:password)
			//HA2=md5(Method:Uri)
			//RESPONSE=md5(HA1:nonce:HA2)
            ++auth.nc;
			if(!auth.algorithm[0])
				snprintf(auth.algorithm, sizeof(auth.algorithm), "MD5");
			if (!auth.qop[0])
				snprintf(auth.qop, sizeof(auth.qop), "auth");
			else if (strchr(auth.qop, ','))
				*strchr(auth.qop, ',') = '\0'; // replace auth,auth-int -> auth
            snprintf(auth.uri, sizeof(auth.uri), "sip:%s", "192.168.154.128");
            snprintf(auth.username, sizeof(auth.username), "%s", "34020000001320000001");
            http_header_auth(&auth, "12345678", "REGISTER", NULL, 0, buffer, sizeof(buffer));
            sip_uac_add_header(t.get(), "Authorization", buffer);
			assert(0 == sip_uac_send(t.get(), NULL, 0, &test->transport, test));
			sip_uac_recv_reply(test);
			break;

		case HTTP_AUTHENTICATION_BASIC:
			break;
		default:
			assert(0);
		}
	}
	else
	{
	}
	return 0;
}

static void sip_uac_register_test(struct sip_uac_test_t *test)
{
	//t = sip_uac_register(uac, "Bob <sip:bob@biloxi.com>", "sip:registrar.biloxi.com", 7200, sip_uac_message_onregister, test);
	std::shared_ptr<sip_uac_transaction_t> t(sip_uac_register(test->sip, "sip:34020000001320000001@192.168.154.1", "sip:192.168.154.128", 3600, sip_uac_onregister, test), sip_uac_transaction_release);
	assert(0 == sip_uac_send(t.get(), NULL, 0, &test->transport, test));
	sip_uac_recv_reply(test);
}

static int sip_uac_onmessage(void* param, const struct sip_message_t* reply, struct sip_uac_transaction_t* t, int code)
{
	return 0;
}

static void sip_uac_message_test(struct sip_uac_test_t *test)
{
	const char* msg = "<?xml version=\"1.0\"?>"
						"<Notify>"
						"<CmdType>Keepalive</CmdType>"
						"<SN>478</SN>"
						"<DeviceID>34020000001320000001</DeviceID>"
						"<Status>OK</Status>"
						"</Notify>";

	std::shared_ptr<sip_uac_transaction_t> t(sip_uac_message(test->sip, "sip:34020000001320000001@192.168.154.1", "sip:34020000002000000001@192.168.154.128", sip_uac_onmessage, test), sip_uac_transaction_release);
	sip_uac_add_header(t.get(), "Content-Type", "Application/MANSCDP+xml");
	assert(0 == sip_uac_send(t.get(), msg, strlen(msg), &test->transport, test));
	sip_uac_recv_reply(test);
}

static int sip_uac_oninvited(void* param, const struct sip_message_t* reply, struct sip_uac_transaction_t* t, struct sip_dialog_t* dialog, const struct cstring_t* id, int code)
{
	if (200 <= code && code < 300)
	{
		sip_uac_ack(t, NULL, 0, NULL);
	}
	return NULL;
}

static void sip_uac_invite_test(struct sip_uac_test_t *test)
{
	std::shared_ptr<sip_uac_transaction_t> t(sip_uac_invite(test->sip, "sip:34020000001320000001@192.168.154.128", "sip:34020000001320000001@192.168.154.128", sip_uac_oninvited, test), sip_uac_transaction_release);
	assert(0 == sip_uac_send(t.get(), NULL, 0, &test->transport, test));
	sip_uac_recv_reply(test);
}

void sip_uac_test(void)
{
	sip_timer_init();
	struct sip_uac_test_t test;
	test.transport = {
		sip_uac_transport_via,
		sip_uac_transport_send,
	};
	struct sip_uas_handler_t handler;
	memset(&handler, 0, sizeof(handler));

	test.udp = socket_udp();
	test.sip = sip_agent_create(&handler);
	test.parser = http_parser_create(HTTP_PARSER_RESPONSE, NULL, NULL);
	socket_bind_any(test.udp, SIP_PORT);
	sip_uac_register_test(&test);
	sip_uac_message_test(&test);
	//sip_uac_invite_test(&test);

	sip_agent_destroy(test.sip);
	socket_close(test.udp);
	http_parser_destroy(test.parser);
	sip_timer_cleanup();
}
