#include "sip-uac.h"
#include "sip-dialog.h"
#include "sip-message.h"
#include "sip-uac-transaction.h"
#include <stdio.h>

// 17.1.1.3 Construction of the ACK Request(Section 13.) (p129)
int sip_uac_ack_3456xx(struct sip_uac_transaction_t* t, const struct sip_message_t* reply, struct sip_dialog_t* dialog)
{
	int r;
//	char ptr[1024];
	char contact[1024];
	struct sip_via_t via;
	struct sip_message_t* ack;

	r = 0;
	ack = sip_message_create(SIP_MESSAGE_REQUEST);
	r = dialog && cstrvalid(&dialog->remote.target.host) ? sip_message_init2(ack, SIP_METHOD_ACK, dialog) : sip_message_initack(ack, t->req);
	if (0 != r)
	{
		sip_message_destroy(ack);
		return -1;
	}

#if defined(SIP_KEEP_DIALOG_REQUET_URI)
	ack->ptr.ptr = sip_uri_clone(ack->ptr.ptr, ack->ptr.end, &ack->u.c.uri, &t->req->u.c.uri);
#endif
	assert(ack->u.c.uri.scheme.n == 3 && 0 == strncmp("sip", ack->u.c.uri.scheme.p, 3));
	if (cstrcmp(&ack->u.c.method, SIP_METHOD_ACK))
	{
		// overwrite method
		ack->u.c.method.p = SIP_METHOD_ACK;
		ack->u.c.method.n = strlen(SIP_METHOD_ACK);
		memcpy(&ack->cseq.method, &ack->u.c.method, sizeof(ack->cseq.method));
	}

	// override to tag
	if (!cstrvalid(&ack->to.tag))
	{
		r = sip_contact_write(&reply->to, contact, contact + sizeof(contact));
		if (r < 0 || r >= sizeof(contact) - 1)
		{
			sip_message_destroy(ack);
			return -1;
		}
		r = sip_message_add_header(ack, "To", contact);
	}
	
	// 8.1.1.7 Via (p39)
	// The branch parameter value MUST be unique across space and time for
	// all requests sent by the UA.The exceptions to this rule are CANCEL
	// and ACK for non-2xx responses.As discussed below, a CANCEL request
	// will have the same value of the branch parameter as the request it
	// cancels.As discussed in Section 17.1.1.3, an ACK for a non-2xx
	// response will also have the same branch ID as the INVITE whose
	// response it acknowledges.

	// 13.2.2.4 2xx Responses (p82)
	// The UAC core MUST generate an ACK request for each 2xx received from
	// the transaction layer.The header fields of the ACK are constructed
	// in the same way as for any request sent within a dialog(see Section
	// 12) with the exception of the CSeq and the header fields related to
	// authentication.The sequence number of the CSeq header field MUST be
	// the same as the INVITE being acknowledged, but the CSeq method MUST
	// be ACK.

	// 17 Transactions (p122)
	// In the case of a transaction where the request was an INVITE(known as an 
	// INVITE transaction), the transaction also includes the ACK only if the 
	// final response was not a 2xx response.If the response was a 2xx, 
	// the ACK is not considered part of the transaction.

	// 6 Definitions: SIP Transaction (p24)
	// 1. If the request is INVITE and the final response is a non-2xx, the transaction also
	//	  includes an ACK to the response. 
	// 2. The ACK for a 2xx response to an INVITE request is a separate transaction(new branch value).

	// fix 408 Busy Here + 200 OK(Dahua IPC)
	//if (200 <= reply->u.s.code && reply->u.s.code < 300)
	//{
	//	assert(0 == sip_vias_count(&ack->vias));
	//	// https://www.ietf.org/mail-archive/web/sip/current/msg06460.html
	//	// [Sip] Branch in INVITE ,ACK,BYE
	//	r = sip_uac_transaction_via(t, ptr, sizeof(ptr), contact, sizeof(contact));
	//	if(0 == r)
	//		r = sip_message_add_header(ack, "Via", ptr);
	//}
	//else
	{
		// 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.

		// The ACK MUST contain a single Via header field, and this MUST 
		// be equal to the top Via header field of the original request.
		if (0 == sip_vias_count(&ack->vias) && sip_vias_count(&t->req->vias) > 0)
		{
			ack->ptr.ptr = sip_via_clone(ack->ptr.ptr, ack->ptr.end, &via, sip_vias_get(&t->req->vias, 0));
			r = sip_vias_push(&ack->vias, &via);
		}
	}

	// message
	assert(ack->u.c.uri.scheme.n == 3 && 0 == strncmp("sip", ack->u.c.uri.scheme.p, 3));
	t->size = sip_message_write(ack, t->data, sizeof(t->data));
	// destroy sip message
	sip_message_destroy(ack);

	if (0 != r || t->size <= 0 || t->size >= sizeof(t->data))
		return 0 == r ? -1 : r; // E2BIG
	return t->transport.send(t->transportptr, t->data, t->size);
}

// 17.1.1.3 Construction of the ACK Request(Section 13.) (p129)
int sip_uac_ack(struct sip_uac_transaction_t* invite, const void* data, int bytes, const char* content_type)
{
	int r;
	char ptr[1024];
	char contact[1024];
	struct sip_message_t* ack;
	const struct cstring_t* h;

	if (!invite->dialog || !cstrvalid(&invite->dialog->remote.target.host))
		return -1;

	r = 0;
	ack = sip_message_create(SIP_MESSAGE_REQUEST);
	r = sip_message_init2(ack, SIP_METHOD_ACK, invite->dialog);
	if (0 != r)
	{
		sip_message_destroy(ack);
		return r;
	}

#if defined(SIP_KEEP_DIALOG_REQUET_URI)
	ack->ptr.ptr = sip_uri_clone(ack->ptr.ptr, ack->ptr.end, &ack->u.c.uri, &invite->req->u.c.uri);
#endif
	assert(ack->u.c.uri.scheme.n >= 3 && 0 == strncmp("sip", ack->u.c.uri.scheme.p, 3));

	sip_message_add_header(ack, "Content-Type", content_type);

	// 8.1.1.7 Via (p39)
	// The branch parameter value MUST be unique across space and time for
	// all requests sent by the UA.The exceptions to this rule are CANCEL
	// and ACK for non-2xx responses.As discussed below, a CANCEL request
	// will have the same value of the branch parameter as the request it
	// cancels.As discussed in Section 17.1.1.3, an ACK for a non-2xx
	// response will also have the same branch ID as the INVITE whose
	// response it acknowledges.

	// 13.2.2.4 2xx Responses (p82)
	// The UAC core MUST generate an ACK request for each 2xx received from
	// the transaction layer.The header fields of the ACK are constructed
	// in the same way as for any request sent within a dialog(see Section
	// 12) with the exception of the CSeq and the header fields related to
	// authentication.The sequence number of the CSeq header field MUST be
	// the same as the INVITE being acknowledged, but the CSeq method MUST
	// be ACK.

	// 17 Transactions (p122)
	// In the case of a transaction where the request was an INVITE(known as an 
	// INVITE transaction), the transaction also includes the ACK only if the 
	// final response was not a 2xx response.If the response was a 2xx, 
	// the ACK is not considered part of the transaction.

	// 6 Definitions: SIP Transaction (p24)
	// 1. If the request is INVITE and the final response is a non-2xx, the transaction also
	//	  includes an ACK to the response. 
	// 2. The ACK for a 2xx response to an INVITE request is a separate transaction(new branch value).
	assert(0 == sip_vias_count(&ack->vias));
	// https://www.ietf.org/mail-archive/web/sip/current/msg06460.html
	// [Sip] Branch in INVITE ,ACK,BYE
	r = sip_uac_transaction_via(invite, ptr, sizeof(ptr), contact, sizeof(contact));
	if (0 == r)
		r = sip_message_add_header(ack, "Via", ptr);
	
	locker_lock(&invite->locker);
	if (invite->status != SIP_UAC_TRANSACTION_ACCEPTED_UNACK)
	{
		locker_unlock(&invite->locker);
		sip_message_destroy(ack);
		assert(0);
		return -1;
	}

	// message
	ack->payload = data;
	ack->size = bytes;
	invite->size = sip_message_write(ack, invite->data, sizeof(invite->data));
	// destroy sip message
	sip_message_destroy(ack);

	if (0 != r || invite->size <= 0 || invite->size >= sizeof(invite->data))
	{
		locker_unlock(&invite->locker);
		return 0 == r ? -1 : r; // E2BIG
	}

	invite->status = SIP_UAC_TRANSACTION_ACCEPTED_ACKED;
	locker_unlock(&invite->locker);

	return invite->transport.send(invite->transportptr, invite->data, invite->size);
}

struct sip_uac_transaction_t* sip_uac_prack(struct sip_agent_t* sip, const struct sip_message_t* req100rel, struct sip_dialog_t* dialog, sip_uac_onreply onreply, void* param)
{
	char rack[64];
	struct sip_message_t* req;
	struct sip_uac_transaction_t* t;
	const struct cstring_t* rseq;

	rseq = sip_message_get_header_by_name(req100rel, SIP_HEADER_RSEQ);
	if (!sip || !dialog || !rseq)
		return NULL;

	snprintf(rack, sizeof(rack), "%u %u %.*s", (unsigned int)req100rel->rseq, (unsigned int)req100rel->cseq.id, (int)req100rel->cseq.method.n, req100rel->cseq.method.p);

	++dialog->local.id;
	req = sip_message_create(SIP_MESSAGE_REQUEST);
	if (0 != sip_message_init2(req, SIP_METHOD_INFO, dialog)
		|| 0 != sip_message_add_header(req, SIP_HEADER_RACK, rack))
	{
		--dialog->local.id;
		sip_message_destroy(req);
		return NULL;
	}

	t = sip_uac_transaction_create(sip, req);
	t->onreply = onreply;
	t->param = param;
	return t;
}

struct sip_uac_transaction_t* sip_uac_update(struct sip_agent_t* sip, struct sip_dialog_t* dialog, sip_uac_onreply onreply, void* param)
{
	struct sip_message_t* req;
	struct sip_uac_transaction_t* t;

	if (!sip || !dialog)
		return NULL;

	++dialog->local.id;
	req = sip_message_create(SIP_MESSAGE_REQUEST);
	if (0 != sip_message_init2(req, SIP_METHOD_UPDATE, dialog))
	{
		--dialog->local.id;
		sip_message_destroy(req);
		return NULL;
	}

	t = sip_uac_transaction_create(sip, req);
	t->onreply = onreply;
	t->param = param;
	return t;
}
