#ifndef _sip_header_h_
#define _sip_header_h_

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "cstring.h"
#include "darray.h"

#if defined(__cplusplus)
extern "C" {
#endif

// 10.2.6 Discovering a Registrar (p62)
#define SIP_MULTICAST_HOST "sip.mcast.net"
#define SIP_MULTICAST_ADDRESS "224.0.1.75"

#define DARRAY_DECLARE(name)				\
	struct name##s_t						\
	{										\
		struct darray_t arr;				\
	};										\
											\
	void name##s_init(struct name##s_t* p);	\
	void name##s_free(struct name##s_t* p);	\
	int name##s_count(const struct name##s_t* p);	\
	int name##s_push(struct name##s_t* p, struct name##_t* item); \
	struct name##_t* name##s_get(const struct name##s_t* p, int index);

#define DARRAY_IMPLEMENT(name, N)			\
	static inline void name##s_arrfree(struct darray_t *arr)	\
	{										\
		int i;								\
		struct name##s_t* p;				\
		p = (struct name##s_t*)(((char*)arr) - offsetof(struct name##s_t, arr)); \
		for(i = 0; i < darray_count(arr); i++) \
			name##_free(name##s_get(p, i)); \
		if(arr && arr->elements)			\
			free(arr->elements);			\
	}										\
											\
	static inline void* name##s_arralloc(struct darray_t *arr, size_t size) \
	{										\
		return realloc(arr->elements, size);\
	}										\
											\
	void name##s_init(struct name##s_t* p)	\
	{										\
		p->arr.free = name##s_arrfree;		\
		p->arr.alloc = name##s_arralloc;	\
		darray_init(&p->arr, sizeof(struct name##_t), N); \
	}										\
											\
	void name##s_free(struct name##s_t* p)	\
	{										\
		darray_free(&p->arr);				\
	}										\
											\
	int name##s_push(struct name##s_t* p, struct name##_t* item)	\
	{										\
		return darray_insert(&p->arr, -1, item);	\
	}										\
											\
	struct name##_t* name##s_get(const struct name##s_t* p, int index)	\
	{										\
		return (struct name##_t*)darray_get(&((struct name##s_t*)p)->arr, index);	\
	}										\
											\
	int name##s_count(const struct name##s_t* p)	\
	{										\
		return darray_count(&p->arr);		\
	}


struct sip_param_t
{
	struct cstring_t name;
	struct cstring_t value;
};
DARRAY_DECLARE(sip_param);

struct sip_uri_t
{
	struct cstring_t scheme;
	struct cstring_t host; // userinfo@host:port
	
	struct sip_params_t parameters;
	struct cstring_t transport; // udp/tcp/sctp/tls/other
	struct cstring_t method;
	struct cstring_t maddr; // the server address to be contacted for this user, overriding any address derived from the host field
	struct cstring_t user; // phone/ip
	int ttl;
	int lr;
	int rport; // 0-not found, -1-no-value, other-value

	struct sip_params_t headers;
};
DARRAY_DECLARE(sip_uri);

struct sip_requestline_t
{
	struct cstring_t method;
	struct sip_uri_t uri;
};

struct sip_statusline_t
{
	int code;
	int verminor, vermajor;
	char protocol[64];
	struct cstring_t reason;
};

struct sip_contact_t
{
	struct sip_uri_t uri;
	struct cstring_t nickname;

	// parameters
	struct cstring_t tag; // TO/FROM
	double q; // c-p-q
	int64_t expires; // delta-seconds, default 3600
	struct sip_params_t params; // include tag/q/expires
};
DARRAY_DECLARE(sip_contact);

struct sip_via_t
{
	struct cstring_t protocol;
	struct cstring_t version;
	struct cstring_t transport;
	struct cstring_t host; // sent-by host:port

	// parameters
	struct cstring_t branch; // token
	struct cstring_t maddr; // host
	struct cstring_t received; // IPv4address / IPv6address
	int ttl; // 0-255
	int rport; // 0-not found, -1-no-value, other-value
	struct sip_params_t params; // include branch/maddr/received/ttl/rport
};
DARRAY_DECLARE(sip_via);

struct sip_cseq_t
{
	uint32_t id;
	struct cstring_t method;
};

struct sip_substate_t
{
	struct cstring_t state;

	// parameters
	struct cstring_t reason;
	uint32_t expires; // expires
	uint32_t retry; // retry-after
	struct sip_params_t params; // include reason/expires/retry
};

struct sip_event_t
{
	struct cstring_t event;

	// parameters
	struct cstring_t id;
	struct sip_params_t params; // include id
};

int sip_header_param(const char* s, const char* end, struct sip_param_t* param);
int sip_header_params(char sep, const char* s, const char* end, struct sip_params_t* params);
int sip_param_write(const struct sip_param_t* param, char* data, const char* end);
int sip_params_write(const struct sip_params_t* params, char* data, const char* end, char sep);
const struct sip_param_t* sip_params_find(const struct sip_params_t* params, const char* name, int bytes);
const struct cstring_t* sip_params_find_string(const struct sip_params_t* params, const char* name, int bytes);
int sip_params_find_int(const struct sip_params_t* params, const char* name, int bytes, int* value);
int sip_params_find_int64(const struct sip_params_t* params, const char* name, int bytes, int64_t* value);
int sip_params_find_double(const struct sip_params_t* params, const char* name, int bytes, double* value);
int sip_params_add_or_update(struct sip_params_t* params, const char* name, int bytes, const struct cstring_t* value);

/// @return 0-ok, other-error
int sip_header_cseq(const char* s, const char* end, struct sip_cseq_t* cseq);
/// @return write length, >0-ok, <0-error
int sip_cseq_write(const struct sip_cseq_t* cseq, char* data, const char* end);

int sip_header_uri(const char* s, const char* end, struct sip_uri_t* uri);
int sip_uri_write(const struct sip_uri_t* uri, char* data, const char* end);
int sip_uri_equal(const struct sip_uri_t* l, const struct sip_uri_t* r);
int sip_uri_username(const struct sip_uri_t* uri, struct cstring_t* user);
int sip_request_uri_write(const struct sip_uri_t* uri, char* data, const char* end);

int sip_header_via(const char* s, const char* end, struct sip_via_t* via);
int sip_header_vias(const char* s, const char* end, struct sip_vias_t* vias);
int sip_via_write(const struct sip_via_t* via, char* data, const char* end);
const struct cstring_t* sip_vias_top_branch(const struct sip_vias_t* vias);

int sip_header_route(const char* s, const char* end, struct sip_uri_t* route);
int sip_header_routes(const char* s, const char* end, struct sip_uris_t* route);
int sip_route_write(const struct sip_uri_t* route, char* data, const char* end);

int sip_header_contact(const char* s, const char* end, struct sip_contact_t* contact);
int sip_header_contacts(const char* s, const char* end, struct sip_contacts_t* contacts);
int sip_contact_write(const struct sip_contact_t* contact, char* data, const char* end);
int sip_contacts_match_any(const struct sip_contacts_t* contacts);

char* cstring_clone(char* ptr, const char* end, struct cstring_t* clone, const char* s, size_t n);
char* sip_uri_clone(char* ptr, const char* end, struct sip_uri_t* clone, const struct sip_uri_t* uri);
char* sip_via_clone(char* ptr, const char* end, struct sip_via_t* clone, const struct sip_via_t* via);
char* sip_contact_clone(char* ptr, const char* end, struct sip_contact_t* clone, const struct sip_contact_t* contact);

void sip_uri_free(struct sip_uri_t* uri);
void sip_via_free(struct sip_via_t* via);
void sip_contact_free(struct sip_contact_t* contact);
void sip_substate_free(struct sip_substate_t* substate);
void sip_event_free(struct sip_event_t* event);

/// @return 0-ok, other-error
int sip_header_substate(const char* s, const char* end, struct sip_substate_t* substate);
/// @return write length, >0-ok, <0-error
int sip_substate_write(const struct sip_substate_t* substate, char* data, const char* end);

/// @return 0-ok, other-error
int sip_header_event(const char* s, const char* end, struct sip_event_t* evt);
/// @return write length, >0-ok, <0-error
int sip_event_write(const struct sip_event_t* evt, char* data, const char* end);
/// @return 1-true, 0-false
int sip_event_equal(const struct sip_event_t* l, const struct sip_event_t* r);

#if defined(__cplusplus)
}
#endif
#endif /* !_sip_header_h_ */
