/*
 * Copyright (c) 2012-2019 Belledonne Communications SARL.
 *
 * This file is part of belle-sip.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include "belle-sip/belle-sip.h"
#include "belle_sip_internal.h"
#include "grammars/belle_sdpLexer.h"
#include "grammars/belle_sdpParser.h"
#include "sdp/parser.hh"

struct _belle_sdp_mime_parameter {
	belle_sip_object_t base;
	int rate;
	int channel_count;
	int ptime;
	int max_ptime;
	int media_format;
	const char *type;
	const char *parameters;
};

static void belle_sip_object_freefunc(void *obj) {
	belle_sip_object_unref(BELLE_SIP_OBJECT(obj));
}
static void *belle_sip_object_copyfunc(void *obj) {
	return belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(obj));
}
static void *belle_sip_string_copyfunc(void *obj) {
	return (void *)belle_sip_strdup((const char *)obj);
}

/***************************************************************************************
 * Attribute
 *
 **************************************************************************************/
typedef belle_sdp_attribute_t *(*attribute_parse_func)(const char *);
struct attribute_name_func_pair {
	const char *name;
	attribute_parse_func func;
};
static struct attribute_name_func_pair attribute_table[] = {
    {"rtcp-fb", (attribute_parse_func)belle_sdp_rtcp_fb_attribute_parse},
    {"rtcp-xr", (attribute_parse_func)belle_sdp_rtcp_xr_attribute_parse},
    {"content", (attribute_parse_func)belle_sdp_content_attribute_parse},
    {"label", (attribute_parse_func)belle_sdp_label_attribute_parse},
    {"creq", (attribute_parse_func)belle_sdp_creq_attribute_parse},
    {"csup", (attribute_parse_func)belle_sdp_csup_attribute_parse},
    {"tcap", (attribute_parse_func)belle_sdp_tcap_attribute_parse},
    {"acap", (attribute_parse_func)belle_sdp_acap_attribute_parse},
    {"csup", (attribute_parse_func)belle_sdp_csup_attribute_parse},
    {"acfg", (attribute_parse_func)belle_sdp_acfg_attribute_parse},
    {"pcfg", (attribute_parse_func)belle_sdp_pcfg_attribute_parse}};
struct _belle_sdp_attribute {
	belle_sip_object_t base;
	const char *name;
	char *unparsed_value;
};
void belle_sdp_attribute_destroy(belle_sdp_attribute_t *attribute) {
	DESTROY_STRING(attribute, name)
	DESTROY_STRING(attribute, unparsed_value)
}
void belle_sdp_attribute_clone(belle_sdp_attribute_t *attribute, const belle_sdp_attribute_t *orig){
    CLONE_STRING(belle_sdp_attribute, name, attribute, orig)} belle_sip_error_code
    belle_sdp_attribute_marshal(belle_sdp_attribute_t *attribute, char *buff, size_t buff_size, size_t *offset) {
	return belle_sip_snprintf(buff, buff_size, offset, "a=%s", attribute->name);
}
belle_sdp_attribute_t *belle_sdp_attribute_create(const char *name, const char *value) {
	belle_sdp_attribute_t *ret;
	size_t i;
	size_t elements = sizeof(attribute_table) / sizeof(attribute_table[0]);

	if (!name || name[0] == '\0') {
		belle_sip_error("Cannot create SDP attribute without name");
		return NULL;
	}

	for (i = 0; i < elements; i++) {
		if (strcasecmp(attribute_table[i].name, name) == 0) {
			char *raw;
			if (value) raw = belle_sip_strdup_printf("a=%s:%s", name, value);
			else raw = belle_sip_strdup_printf("a=%s", name);
			ret = attribute_table[i].func(raw);
			belle_sip_free(raw);
			return ret;
		}
	}
	/* Not a specialized SDP attribute */
	return BELLE_SDP_ATTRIBUTE(belle_sdp_raw_attribute_create(name, value));
}
const char *belle_sdp_attribute_get_value(belle_sdp_attribute_t *attribute) {
	char *ret;
	char *end;

	if (attribute->unparsed_value) {
		belle_sip_free(attribute->unparsed_value);
		attribute->unparsed_value = NULL;
	}
	attribute->unparsed_value = belle_sip_object_to_string(attribute);

	ret = attribute->unparsed_value;
	ret += strlen(attribute->name) + 2; /* "a=" + name*/
	if (*ret == ':') ret++;
	for (; *ret == ' '; ret++) {
	}; /* skip eventual spaces */
	return ret;
}
unsigned int belle_sdp_attribute_has_value(belle_sdp_attribute_t *attribute) {
	return belle_sdp_attribute_get_value(attribute) != NULL;
}

BELLE_SDP_NEW(attribute, belle_sip_object)

belle_sdp_attribute_t *belle_sdp_attribute_parse(const char *line) {
	auto parser = bellesip::SDP::Parser::getInstance();
	auto holder = (belle_sdp_attribute_holder_t *)parser->parse(line, "attribute");
	if (holder == NULL) belle_sip_error("attribute parser error for [%s]", line);
	belle_sdp_attribute_t *attribute = belle_sdp_attribute_holder_get_attribute(holder);
	belle_sip_free(holder);

	return attribute;
}

GET_SET_STRING(belle_sdp_attribute, name);

/***************************************************************************************
 * Attributes Holder
 *
 **************************************************************************************/
struct _belle_sdp_attribute_holder {
	belle_sdp_attribute_t *attribute;
};

belle_sdp_attribute_holder_t *belle_sdp_attribute_holder_new() {
	return (belle_sdp_attribute_holder_t *)belle_sip_malloc0(sizeof(belle_sdp_attribute_holder_t));
}

void belle_sdp_attribute_holder_set_attribute(belle_sdp_attribute_holder_t *holder, void *attribute) {
	holder->attribute = (belle_sdp_attribute_t *)attribute;
}

belle_sdp_attribute_t *belle_sdp_attribute_holder_get_attribute(belle_sdp_attribute_holder_t *holder) {
	return holder->attribute;
}

/***************************************************************************************
 * RAW Attribute
 *
 **************************************************************************************/
struct _belle_sdp_raw_attribute {
	belle_sdp_attribute_t base;
	const char *value;
};
void belle_sdp_raw_attribute_destroy(belle_sdp_raw_attribute_t *attribute) {
	DESTROY_STRING(attribute, value)
}
void belle_sdp_raw_attribute_clone(belle_sdp_raw_attribute_t *attribute, const belle_sdp_raw_attribute_t *orig) {
	if (belle_sdp_attribute_get_value(BELLE_SDP_ATTRIBUTE(orig))) {
		belle_sdp_raw_attribute_set_value(attribute, belle_sdp_attribute_get_value(BELLE_SDP_ATTRIBUTE(orig)));
	}
}
belle_sip_error_code
belle_sdp_raw_attribute_marshal(belle_sdp_raw_attribute_t *attribute, char *buff, size_t buff_size, size_t *offset) {
	belle_sip_error_code error = belle_sdp_attribute_marshal(BELLE_SDP_ATTRIBUTE(attribute), buff, buff_size, offset);
	if (error != BELLE_SIP_OK) return error;
	if (attribute->value) {
		error = belle_sip_snprintf(buff, buff_size, offset, ":%s", attribute->value);
		if (error != BELLE_SIP_OK) return error;
	}
	return error;
}
BELLE_SDP_NEW(raw_attribute, belle_sdp_attribute)
belle_sdp_raw_attribute_t *belle_sdp_raw_attribute_create(const char *name, const char *value) {
	belle_sdp_raw_attribute_t *attribute = belle_sdp_raw_attribute_new();
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), name);
	belle_sdp_raw_attribute_set_value(attribute, value);
	return attribute;
}
void belle_sdp_raw_attribute_set_value(belle_sdp_raw_attribute_t *attribute, const char *value) {
	if (attribute->value != NULL) belle_sip_free((void *)attribute->value);
	if (value) {
		attribute->value = belle_sip_strdup(value);
	} else attribute->value = NULL;
}
void belle_sdp_raw_attribute_set_name(belle_sdp_raw_attribute_t *attribute, const char *name) {
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), name);
}

/***************************************************************************************
 * RFC4574 Attributes
 *
 **************************************************************************************/

struct _belle_sdp_label_attribute {
	belle_sdp_attribute_t base;
	const char *pointer;
};

static void belle_sdp_label_attribute_init(belle_sdp_label_attribute_t *attribute) {
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "label");
}

void belle_sdp_label_attribute_destroy(belle_sdp_label_attribute_t *attribute) {
	DESTROY_STRING(attribute, pointer)
}
void belle_sdp_label_attribute_clone(belle_sdp_label_attribute_t *attribute, const belle_sdp_label_attribute_t *orig) {
	belle_sdp_label_attribute_set_pointer(attribute, belle_sdp_label_attribute_get_pointer(orig));
}

belle_sip_error_code belle_sdp_label_attribute_marshal(belle_sdp_label_attribute_t *attribute,
                                                       char *buff,
                                                       size_t buff_size,
                                                       size_t *offset) {
	belle_sip_error_code error = belle_sip_snprintf(buff, buff_size, offset, "a=label:%s", attribute->pointer);

	return error;
}

BELLE_SDP_NEW_WITH_CTR(label_attribute, belle_sdp_attribute)
BELLE_SDP_BELR_PARSE(label_attribute)
GET_SET_STRING(belle_sdp_label_attribute, pointer)

/***************************************************************************************
 * RFC4796 Attributes
 *
 **************************************************************************************/

// content

struct _belle_sdp_content_attribute {
	belle_sdp_attribute_t base;
	belle_sip_list_t *media_tags;
};

void belle_sdp_content_attribute_destroy(belle_sdp_content_attribute_t *attribute) {
	belle_sip_list_free_with_data(attribute->media_tags, belle_sip_free);
}

void belle_sdp_content_attribute_clone(belle_sdp_content_attribute_t *attribute,
                                       const belle_sdp_content_attribute_t *orig) {
	attribute->media_tags = belle_sip_list_copy_with_data(orig->media_tags, belle_sip_string_copyfunc);
}

belle_sip_error_code belle_sdp_content_attribute_marshal(belle_sdp_content_attribute_t *attribute,
                                                         char *buff,
                                                         size_t buff_size,
                                                         size_t *offset) {
	belle_sip_list_t *media_tags = attribute->media_tags;

	belle_sip_error_code error = belle_sip_snprintf(buff, buff_size, offset, "a=content:");
	if (error != BELLE_SIP_OK) return error;

	int i = 0;
	for (; media_tags != NULL; media_tags = media_tags->next) {
		error =
		    belle_sip_snprintf(buff, buff_size, offset, "%s%s", i++ == 0 ? "" : ",", (const char *)media_tags->data);
		if (error != BELLE_SIP_OK) return error;
	}

	return error;
}

void belle_sdp_content_attribute_add_media_tag(belle_sdp_content_attribute_t *attribute, const char *media_tag) {
	attribute->media_tags = belle_sip_list_append(attribute->media_tags, belle_sip_strdup(media_tag));
}

belle_sip_list_t *belle_sdp_content_attribute_get_media_tags(belle_sdp_content_attribute_t *attribute) {
	return attribute->media_tags;
}

static void belle_sdp_content_attribute_init(belle_sdp_content_attribute_t *attribute) {
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "content");
}

BELLE_SDP_NEW_WITH_CTR(content_attribute, belle_sdp_attribute)
BELLE_SDP_BELR_PARSE(content_attribute)

/***************************************************************************************
 * RFC5939 Attributes
 *
 **************************************************************************************/

// csup

struct _belle_sdp_csup_attribute {
	belle_sdp_attribute_t base;
	belle_sip_list_t *option_tags;
};

void belle_sdp_csup_attribute_destroy(belle_sdp_csup_attribute_t *attribute) {
	belle_sip_list_free_with_data(attribute->option_tags, belle_sip_free);
}

void belle_sdp_csup_attribute_clone(belle_sdp_csup_attribute_t *attribute, const belle_sdp_csup_attribute_t *orig) {
	attribute->option_tags = belle_sip_list_copy_with_data(orig->option_tags, belle_sip_string_copyfunc);
}

belle_sip_error_code
belle_sdp_csup_attribute_marshal(belle_sdp_csup_attribute_t *attribute, char *buff, size_t buff_size, size_t *offset) {
	belle_sip_list_t *option_tags = attribute->option_tags;

	belle_sip_error_code error = belle_sip_snprintf(buff, buff_size, offset, "a=csup:");
	if (error != BELLE_SIP_OK) return error;

	int i = 0;
	for (; option_tags != NULL; option_tags = option_tags->next) {
		error =
		    belle_sip_snprintf(buff, buff_size, offset, "%s%s", i++ == 0 ? "" : ",", (const char *)option_tags->data);
		if (error != BELLE_SIP_OK) return error;
	}

	return error;
}

void belle_sdp_csup_attribute_add_option_tag(belle_sdp_csup_attribute_t *attribute, const char *option_tag) {
	attribute->option_tags = belle_sip_list_append(attribute->option_tags, belle_sip_strdup(option_tag));
}

belle_sip_list_t *belle_sdp_csup_attribute_get_option_tags(belle_sdp_csup_attribute_t *attribute) {
	return attribute->option_tags;
}

static void belle_sdp_csup_attribute_init(belle_sdp_csup_attribute_t *attribute) {
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "csup");
}

BELLE_SDP_NEW_WITH_CTR(csup_attribute, belle_sdp_attribute)
BELLE_SDP_BELR_PARSE(csup_attribute)

// creq

struct _belle_sdp_creq_attribute {
	belle_sdp_attribute_t base;
	belle_sip_list_t *option_tags;
};

void belle_sdp_creq_attribute_destroy(belle_sdp_creq_attribute_t *attribute) {
	belle_sip_list_free_with_data(attribute->option_tags, belle_sip_free);
}

void belle_sdp_creq_attribute_clone(belle_sdp_creq_attribute_t *attribute, const belle_sdp_creq_attribute_t *orig) {
	attribute->option_tags = belle_sip_list_copy_with_data(orig->option_tags, belle_sip_string_copyfunc);
}

belle_sip_error_code
belle_sdp_creq_attribute_marshal(belle_sdp_creq_attribute_t *attribute, char *buff, size_t buff_size, size_t *offset) {
	belle_sip_list_t *option_tags = attribute->option_tags;

	belle_sip_error_code error = belle_sip_snprintf(buff, buff_size, offset, "a=creq:");
	if (error != BELLE_SIP_OK) return error;

	int i = 0;
	for (; option_tags != NULL; option_tags = option_tags->next) {
		error =
		    belle_sip_snprintf(buff, buff_size, offset, "%s%s", i++ == 0 ? "" : ",", (const char *)option_tags->data);
		if (error != BELLE_SIP_OK) return error;
	}

	return error;
}

void belle_sdp_creq_attribute_add_option_tag(belle_sdp_creq_attribute_t *attribute, const char *option_tag) {
	attribute->option_tags = belle_sip_list_append(attribute->option_tags, belle_sip_strdup(option_tag));
}

belle_sip_list_t *belle_sdp_creq_attribute_get_option_tags(belle_sdp_creq_attribute_t *attribute) {
	return attribute->option_tags;
}

static void belle_sdp_creq_attribute_init(belle_sdp_creq_attribute_t *attribute) {
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "creq");
}

BELLE_SDP_NEW_WITH_CTR(creq_attribute, belle_sdp_attribute)
BELLE_SDP_BELR_PARSE(creq_attribute)

// tcap

struct _belle_sdp_tcap_attribute {
	belle_sdp_attribute_t base;
	belle_sip_list_t *protos;
	int id;
};

static void belle_sdp_tcap_attribute_init(belle_sdp_tcap_attribute_t *attribute) {
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "tcap");
}

void belle_sdp_tcap_attribute_destroy(belle_sdp_tcap_attribute_t *attribute) {
	belle_sip_list_free_with_data(attribute->protos, belle_sip_free);
}

void belle_sdp_tcap_attribute_clone(belle_sdp_tcap_attribute_t *attribute, const belle_sdp_tcap_attribute_t *orig) {
	attribute->protos = belle_sip_list_copy_with_data(orig->protos, belle_sip_string_copyfunc);
	attribute->id = orig->id;
}

belle_sip_error_code
belle_sdp_tcap_attribute_marshal(belle_sdp_tcap_attribute_t *attribute, char *buff, size_t buff_size, size_t *offset) {
	belle_sip_list_t *protos = attribute->protos;

	belle_sip_error_code error = belle_sip_snprintf(buff, buff_size, offset, "a=tcap:%d", attribute->id);
	if (error != BELLE_SIP_OK) return error;

	int i = 0;
	for (; protos != NULL; protos = protos->next) {
		error = belle_sip_snprintf(buff, buff_size, offset, " %s", (const char *)protos->data);
		if (error != BELLE_SIP_OK) return error;
	}

	return error;
}

void belle_sdp_tcap_attribute_add_proto(belle_sdp_tcap_attribute_t *attribute, const char *proto) {
	attribute->protos = belle_sip_list_append(attribute->protos, belle_sip_strdup(proto));
}

belle_sip_list_t *belle_sdp_tcap_attribute_get_protos(belle_sdp_tcap_attribute_t *attribute) {
	return attribute->protos;
}

BELLE_SDP_NEW_WITH_CTR(tcap_attribute, belle_sdp_attribute)
BELLE_SDP_BELR_PARSE(tcap_attribute)
GET_SET_INT(belle_sdp_tcap_attribute, id, int)

// acap

struct _belle_sdp_acap_attribute {
	belle_sdp_attribute_t base;
	int id;
	const char *name;
	const char *value;
};

static void belle_sdp_acap_attribute_init(belle_sdp_acap_attribute_t *attribute) {
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "acap");
}

void belle_sdp_acap_attribute_destroy(belle_sdp_acap_attribute_t *attribute) {
	DESTROY_STRING(attribute, name)
	DESTROY_STRING(attribute, value)
}
void belle_sdp_acap_attribute_clone(belle_sdp_acap_attribute_t *attribute, const belle_sdp_acap_attribute_t *orig) {
	attribute->id = orig->id;
	belle_sdp_acap_attribute_set_name(attribute, belle_sdp_acap_attribute_get_name(orig));
	belle_sdp_acap_attribute_set_value(attribute, belle_sdp_acap_attribute_get_value(orig));
}

belle_sip_error_code
belle_sdp_acap_attribute_marshal(belle_sdp_acap_attribute_t *attribute, char *buff, size_t buff_size, size_t *offset) {
	if (attribute->value != NULL && attribute->value[0] != '\0') {
		belle_sip_error_code error = belle_sip_snprintf(buff, buff_size, offset, "a=acap:%d %s:%s", attribute->id,
		                                                attribute->name, attribute->value);
		return error;
	} else {
		belle_sip_error_code error =
		    belle_sip_snprintf(buff, buff_size, offset, "a=acap:%d %s", attribute->id, attribute->name);
		return error;
	}
}

BELLE_SDP_NEW_WITH_CTR(acap_attribute, belle_sdp_attribute)
BELLE_SDP_BELR_PARSE(acap_attribute)
GET_SET_INT(belle_sdp_acap_attribute, id, int)
GET_SET_STRING(belle_sdp_acap_attribute, name)
GET_SET_STRING(belle_sdp_acap_attribute, value)

// acfg

struct _belle_sdp_acfg_attribute {
	belle_sdp_attribute_t base;
	int id;
	belle_sip_list_t *configs;
};

static void belle_sdp_acfg_attribute_init(belle_sdp_acfg_attribute_t *attribute) {
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "acfg");
}

void belle_sdp_acfg_attribute_destroy(belle_sdp_acfg_attribute_t *attribute) {
	belle_sip_list_free_with_data(attribute->configs, belle_sip_free);
}
void belle_sdp_acfg_attribute_clone(belle_sdp_acfg_attribute_t *attribute, const belle_sdp_acfg_attribute_t *orig) {
	attribute->id = orig->id;
	attribute->configs = belle_sip_list_copy_with_data(orig->configs, belle_sip_string_copyfunc);
}

belle_sip_error_code
belle_sdp_acfg_attribute_marshal(belle_sdp_acfg_attribute_t *attribute, char *buff, size_t buff_size, size_t *offset) {
	belle_sip_list_t *configs = attribute->configs;

	belle_sip_error_code error = belle_sip_snprintf(buff, buff_size, offset, "a=acfg:%d", attribute->id);
	if (error != BELLE_SIP_OK) return error;

	int i = 0;
	for (; configs != NULL; configs = configs->next) {
		error = belle_sip_snprintf(buff, buff_size, offset, " %s", (const char *)configs->data);
		if (error != BELLE_SIP_OK) return error;
	}

	return error;
}

void belle_sdp_acfg_attribute_add_config(belle_sdp_acfg_attribute_t *attribute, const char *config) {
	attribute->configs = belle_sip_list_append(attribute->configs, belle_sip_strdup(config));
}

belle_sip_list_t *belle_sdp_acfg_attribute_get_configs(const belle_sdp_acfg_attribute_t *attribute) {
	return attribute->configs;
}

BELLE_SDP_NEW_WITH_CTR(acfg_attribute, belle_sdp_attribute)
BELLE_SDP_BELR_PARSE(acfg_attribute)
GET_SET_INT(belle_sdp_acfg_attribute, id, int)

// pcfg

struct _belle_sdp_pcfg_attribute {
	belle_sdp_attribute_t base;
	int id;
	belle_sip_list_t *configs;
};

static void belle_sdp_pcfg_attribute_init(belle_sdp_pcfg_attribute_t *attribute) {
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "pcfg");
}

void belle_sdp_pcfg_attribute_destroy(belle_sdp_pcfg_attribute_t *attribute) {
	belle_sip_list_free_with_data(attribute->configs, belle_sip_free);
}
void belle_sdp_pcfg_attribute_clone(belle_sdp_pcfg_attribute_t *attribute, const belle_sdp_pcfg_attribute_t *orig) {
	attribute->id = orig->id;
	attribute->configs = belle_sip_list_copy_with_data(orig->configs, belle_sip_string_copyfunc);
}

belle_sip_error_code
belle_sdp_pcfg_attribute_marshal(belle_sdp_pcfg_attribute_t *attribute, char *buff, size_t buff_size, size_t *offset) {
	belle_sip_list_t *configs = attribute->configs;

	belle_sip_error_code error = belle_sip_snprintf(buff, buff_size, offset, "a=pcfg:%d", attribute->id);

	int i = 0;
	for (; configs != NULL; configs = configs->next) {
		error = belle_sip_snprintf(buff, buff_size, offset, " %s", (const char *)configs->data);
		if (error != BELLE_SIP_OK) return error;
	}

	return error;
}

void belle_sdp_pcfg_attribute_add_config(belle_sdp_pcfg_attribute_t *attribute, const char *config) {
	attribute->configs = belle_sip_list_append(attribute->configs, belle_sip_strdup(config));
}

belle_sip_list_t *belle_sdp_pcfg_attribute_get_configs(const belle_sdp_pcfg_attribute_t *attribute) {
	return attribute->configs;
}

BELLE_SDP_NEW_WITH_CTR(pcfg_attribute, belle_sdp_attribute)
BELLE_SDP_BELR_PARSE(pcfg_attribute)
GET_SET_INT(belle_sdp_pcfg_attribute, id, int)

/***************************************************************************************
 * RTCP-FB Attribute
 *
 **************************************************************************************/
struct _belle_sdp_rtcp_fb_attribute {
	belle_sdp_attribute_t base;
	belle_sdp_rtcp_fb_val_type_t type;
	belle_sdp_rtcp_fb_val_param_t param;
	uint32_t smaxpr;
	uint16_t trr_int;
	int8_t id;
};
BELLESIP_EXPORT unsigned int belle_sdp_rtcp_fb_attribute_has_pli(const belle_sdp_rtcp_fb_attribute_t *attribute);
BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_pli(belle_sdp_rtcp_fb_attribute_t *attribute, unsigned int enable);
BELLESIP_EXPORT unsigned int belle_sdp_rtcp_fb_attribute_has_sli(const belle_sdp_rtcp_fb_attribute_t *attribute);
BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_sli(belle_sdp_rtcp_fb_attribute_t *attribute, unsigned int enable);
BELLESIP_EXPORT unsigned int belle_sdp_rtcp_fb_attribute_has_rpsi(const belle_sdp_rtcp_fb_attribute_t *attribute);
BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_rpsi(belle_sdp_rtcp_fb_attribute_t *attribute,
                                                          unsigned int enable);
BELLESIP_EXPORT unsigned int belle_sdp_rtcp_fb_attribute_has_app(const belle_sdp_rtcp_fb_attribute_t *attribute);
BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_app(belle_sdp_rtcp_fb_attribute_t *attribute, unsigned int enable);
void belle_sdp_rtcp_fb_attribute_destroy(belle_sdp_rtcp_fb_attribute_t *attribute) {
}
void belle_sdp_rtcp_fb_attribute_clone(belle_sdp_rtcp_fb_attribute_t *attribute,
                                       const belle_sdp_rtcp_fb_attribute_t *orig) {
	attribute->type = orig->type;
	attribute->param = orig->param;
	attribute->trr_int = orig->trr_int;
	attribute->id = orig->id;
	attribute->smaxpr = orig->smaxpr;
}
belle_sip_error_code belle_sdp_rtcp_fb_attribute_marshal(belle_sdp_rtcp_fb_attribute_t *attribute,
                                                         char *buff,
                                                         size_t buff_size,
                                                         size_t *offset) {
	int8_t id = belle_sdp_rtcp_fb_attribute_get_id(attribute);
	belle_sdp_rtcp_fb_val_type_t type = belle_sdp_rtcp_fb_attribute_get_type(attribute);
	belle_sdp_rtcp_fb_val_param_t param = belle_sdp_rtcp_fb_attribute_get_param(attribute);
	belle_sip_error_code error = belle_sdp_attribute_marshal(BELLE_SDP_ATTRIBUTE(attribute), buff, buff_size, offset);
	if (error != BELLE_SIP_OK) return error;
	if (id < 0) {
		error = belle_sip_snprintf(buff, buff_size, offset, ":* ");
	} else {
		error = belle_sip_snprintf(buff, buff_size, offset, ":%u ", id);
	}
	if (error != BELLE_SIP_OK) return error;
	switch (type) {
		case BELLE_SDP_RTCP_FB_ACK:
			error = belle_sip_snprintf(buff, buff_size, offset, "ack");
			if (error != BELLE_SIP_OK) return error;
			switch (param) {
				default:
				case BELLE_SDP_RTCP_FB_NONE:
					break;
				case BELLE_SDP_RTCP_FB_RPSI:
					error = belle_sip_snprintf(buff, buff_size, offset, " rpsi");
					break;
				case BELLE_SDP_RTCP_FB_APP:
					error = belle_sip_snprintf(buff, buff_size, offset, " app");
					break;
			}
			break;
		case BELLE_SDP_RTCP_FB_NACK:
			error = belle_sip_snprintf(buff, buff_size, offset, "nack");
			if (error != BELLE_SIP_OK) return error;
			switch (param) {
				default:
				case BELLE_SDP_RTCP_FB_NONE:
					break;
				case BELLE_SDP_RTCP_FB_PLI:
					error = belle_sip_snprintf(buff, buff_size, offset, " pli");
					break;
				case BELLE_SDP_RTCP_FB_SLI:
					error = belle_sip_snprintf(buff, buff_size, offset, " sli");
					break;
				case BELLE_SDP_RTCP_FB_RPSI:
					error = belle_sip_snprintf(buff, buff_size, offset, " rpsi");
					break;
				case BELLE_SDP_RTCP_FB_APP:
					error = belle_sip_snprintf(buff, buff_size, offset, " app");
					break;
			}
			break;
		case BELLE_SDP_RTCP_FB_TRR_INT:
			error = belle_sip_snprintf(buff, buff_size, offset, "trr-int %u",
			                           belle_sdp_rtcp_fb_attribute_get_trr_int(attribute));
			break;
		case BELLE_SDP_RTCP_FB_CCM:
			error = belle_sip_snprintf(buff, buff_size, offset, "ccm");
			if (error != BELLE_SIP_OK) return error;
			switch (param) {
				case BELLE_SDP_RTCP_FB_FIR:
					error = belle_sip_snprintf(buff, buff_size, offset, " fir");
					break;
				case BELLE_SDP_RTCP_FB_TMMBR:
					error = belle_sip_snprintf(buff, buff_size, offset, " tmmbr");
					if (belle_sdp_rtcp_fb_attribute_get_smaxpr(attribute) > 0) {
						error = belle_sip_snprintf(buff, buff_size, offset, " smaxpr=%u",
						                           belle_sdp_rtcp_fb_attribute_get_smaxpr(attribute));
					}
					break;
				default:
					break;
			}
			break;
	}
	return error;
}
void belle_sdp_rtcp_fb_attribute_set_raw_id(belle_sdp_rtcp_fb_attribute_t *attribute, const char *value) {
	attribute->id = strcmp(value, "*") == 0 ? -1 : atoi(value);
}
void belle_sdp_rtcp_fb_attribute_set_raw_type(belle_sdp_rtcp_fb_attribute_t *attribute, const char *value) {
	if (strcmp(value, "ack") == 0) {
		attribute->type = BELLE_SDP_RTCP_FB_ACK;
	}
	if (strcmp(value, "nack") == 0) {
		attribute->type = BELLE_SDP_RTCP_FB_NACK;
	}
	if (strcmp(value, "ccm") == 0) {
		attribute->type = BELLE_SDP_RTCP_FB_CCM;
	}
}
void belle_sdp_rtcp_fb_attribute_set_raw_param(belle_sdp_rtcp_fb_attribute_t *attribute, const char *value) {
	// Space is required here
	if (strcmp(value, " pli") == 0) {
		attribute->param = BELLE_SDP_RTCP_FB_PLI;
	}
	if (strcmp(value, " sli") == 0) {
		attribute->param = BELLE_SDP_RTCP_FB_SLI;
	}
	if (strcmp(value, " rpsi") == 0) {
		attribute->param = BELLE_SDP_RTCP_FB_RPSI;
	}
	if (strcmp(value, "fir") == 0) {
		attribute->param = BELLE_SDP_RTCP_FB_FIR;
	}
	if (strcmp(value, "tmmbr") == 0) {
		attribute->param = BELLE_SDP_RTCP_FB_TMMBR;
	}
}
static void belle_sdp_rtcp_fb_attribute_init(belle_sdp_rtcp_fb_attribute_t *attribute) {
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "rtcp-fb");
	attribute->id = -1;
	attribute->type = BELLE_SDP_RTCP_FB_TRR_INT;
	attribute->param = BELLE_SDP_RTCP_FB_NONE;
	attribute->trr_int = 0;
	attribute->smaxpr = 0;
}
BELLE_SDP_NEW_WITH_CTR(rtcp_fb_attribute, belle_sdp_attribute)
BELLE_SDP_PARSE(rtcp_fb_attribute)
GET_SET_INT(belle_sdp_rtcp_fb_attribute, id, int8_t)
GET_SET_INT(belle_sdp_rtcp_fb_attribute, type, belle_sdp_rtcp_fb_val_type_t)
GET_SET_INT(belle_sdp_rtcp_fb_attribute, param, belle_sdp_rtcp_fb_val_param_t)
GET_SET_INT(belle_sdp_rtcp_fb_attribute, trr_int, uint16_t)
GET_SET_INT(belle_sdp_rtcp_fb_attribute, smaxpr, uint32_t)
/***************************************************************************************
 * RTCP-XR Attribute
 *
 **************************************************************************************/
struct _belle_sdp_rtcp_xr_attribute {
	belle_sdp_attribute_t base;
	const char *rcvr_rtt_mode;
	int rcvr_rtt_max_size;
	unsigned int stat_summary;
	belle_sip_list_t *stat_summary_flags;
	unsigned int voip_metrics;
};
const belle_sip_list_t *
belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(const belle_sdp_rtcp_xr_attribute_t *attribute) {
	return attribute->stat_summary_flags;
}
void belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(belle_sdp_rtcp_xr_attribute_t *attribute, const char *flag) {
	attribute->stat_summary_flags = belle_sip_list_append(attribute->stat_summary_flags, belle_sip_strdup(flag));
}
void belle_sdp_rtcp_xr_attribute_enable_stat_summary(belle_sdp_rtcp_xr_attribute_t *attribute, const char *flag) {
	belle_sdp_rtcp_xr_attribute_set_stat_summary(attribute, 1);
}
void belle_sdp_rtcp_xr_attribute_enable_voip_metrics(belle_sdp_rtcp_xr_attribute_t *attribute, const char *flag) {
	belle_sdp_rtcp_xr_attribute_set_voip_metrics(attribute, 1);
}
void belle_sdp_rtcp_xr_attribute_destroy(belle_sdp_rtcp_xr_attribute_t *attribute) {
	DESTROY_STRING(attribute, rcvr_rtt_mode)
	belle_sip_list_free_with_data(attribute->stat_summary_flags, belle_sip_free);
}
void belle_sdp_rtcp_xr_attribute_clone(belle_sdp_rtcp_xr_attribute_t *attribute,
                                       const belle_sdp_rtcp_xr_attribute_t *orig) {
	CLONE_STRING(belle_sdp_rtcp_xr_attribute, rcvr_rtt_mode, attribute, orig)
	attribute->rcvr_rtt_max_size = orig->rcvr_rtt_max_size;
	attribute->stat_summary = orig->stat_summary;
	attribute->stat_summary_flags = belle_sip_list_copy_with_data(orig->stat_summary_flags, belle_sip_string_copyfunc);
	attribute->voip_metrics = orig->voip_metrics;
}
belle_sip_error_code belle_sdp_rtcp_xr_attribute_marshal(belle_sdp_rtcp_xr_attribute_t *attribute,
                                                         char *buff,
                                                         size_t buff_size,
                                                         size_t *offset) {
	const char *rcvr_rtt_mode = NULL;
	int rcvr_rtt_max_size = -1;
	int nb_xr_formats = 0;
	belle_sip_error_code error = belle_sdp_attribute_marshal(BELLE_SDP_ATTRIBUTE(attribute), buff, buff_size, offset);
	if (error != BELLE_SIP_OK) return error;
	rcvr_rtt_mode = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_mode(attribute);
	if (rcvr_rtt_mode != NULL) {
		error = belle_sip_snprintf(buff, buff_size, offset, "%srcvr-rtt=%s", nb_xr_formats++ == 0 ? ":" : " ",
		                           rcvr_rtt_mode);
		if (error != BELLE_SIP_OK) return error;
		rcvr_rtt_max_size = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_max_size(attribute);
		if (rcvr_rtt_max_size > 0) {
			error = belle_sip_snprintf(buff, buff_size, offset, ":%u", rcvr_rtt_max_size);
			if (error != BELLE_SIP_OK) return error;
		}
	}
	if (belle_sdp_rtcp_xr_attribute_has_stat_summary(attribute)) {
		belle_sip_list_t *list;
		int nb_stat_flags = 0;
		error = belle_sip_snprintf(buff, buff_size, offset, "%sstat-summary", nb_xr_formats++ == 0 ? ":" : " ");
		if (error != BELLE_SIP_OK) return error;
		for (list = attribute->stat_summary_flags; list != NULL; list = list->next) {
			error = belle_sip_snprintf(buff, buff_size, offset, "%s%s", nb_stat_flags++ == 0 ? "=" : ",",
			                           (const char *)list->data);
			if (error != BELLE_SIP_OK) return error;
		}
	}
	if (belle_sdp_rtcp_xr_attribute_has_voip_metrics(attribute)) {
		error = belle_sip_snprintf(buff, buff_size, offset, "%svoip-metrics", nb_xr_formats++ == 0 ? ":" : " ");
		if (error != BELLE_SIP_OK) return error;
	}
	return error;
}
static void belle_sdp_rtcp_xr_attribute_init(belle_sdp_rtcp_xr_attribute_t *attribute) {
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "rtcp-xr");
}
BELLE_SDP_NEW_WITH_CTR(rtcp_xr_attribute, belle_sdp_attribute)
BELLE_SDP_PARSE(rtcp_xr_attribute)
GET_SET_STRING(belle_sdp_rtcp_xr_attribute, rcvr_rtt_mode)
GET_SET_INT(belle_sdp_rtcp_xr_attribute, rcvr_rtt_max_size, int)
GET_SET_BOOL(belle_sdp_rtcp_xr_attribute, stat_summary, has)
GET_SET_BOOL(belle_sdp_rtcp_xr_attribute, voip_metrics, has)
/***************************************************************************************
 * Bandwidth
 *
 **************************************************************************************/
struct _belle_sdp_bandwidth {
	belle_sip_object_t base;
	const char *type;
	int value;
};
void belle_sdp_bandwidth_destroy(belle_sdp_bandwidth_t *bandwidth) {
	if (bandwidth->type) belle_sip_free((void *)bandwidth->type);
}

void belle_sdp_bandwidth_clone(belle_sdp_bandwidth_t *bandwidth, const belle_sdp_bandwidth_t *orig) {
	CLONE_STRING(belle_sdp_bandwidth, type, bandwidth, orig)
	bandwidth->value = orig->value;
}

belle_sip_error_code
belle_sdp_bandwidth_marshal(belle_sdp_bandwidth_t *bandwidth, char *buff, size_t buff_size, size_t *offset) {
	return belle_sip_snprintf(buff, buff_size, offset, "b=%s:%i", bandwidth->type, bandwidth->value);
}

BELLE_SDP_NEW(bandwidth, belle_sip_object)
BELLE_SDP_PARSE(bandwidth)
GET_SET_STRING(belle_sdp_bandwidth, type);
GET_SET_INT(belle_sdp_bandwidth, value, int)

/************************
 * connection
 ***********************/
struct _belle_sdp_connection {
	belle_sip_object_t base;
	const char *network_type;
	const char *address_type;
	const char *address;
	int ttl;
	int range;
};

void belle_sdp_connection_destroy(belle_sdp_connection_t *connection) {
	DESTROY_STRING(connection, network_type)
	DESTROY_STRING(connection, address_type)
	DESTROY_STRING(connection, address)
}

void belle_sdp_connection_clone(belle_sdp_connection_t *connection, const belle_sdp_connection_t *orig) {
	CLONE_STRING(belle_sdp_connection, network_type, connection, orig)
	CLONE_STRING(belle_sdp_connection, address_type, connection, orig)
	CLONE_STRING(belle_sdp_connection, address, connection, orig)
	connection->range = orig->range;
	connection->ttl = orig->ttl;
}

belle_sip_error_code
belle_sdp_connection_marshal(belle_sdp_connection_t *connection, char *buff, size_t buff_size, size_t *offset) {
	belle_sip_error_code error = belle_sip_snprintf(buff, buff_size, offset, "c=%s %s %s", connection->network_type,
	                                                connection->address_type, connection->address);
	if (error != BELLE_SIP_OK) return error;
	if (connection->ttl > 0) error = belle_sip_snprintf(buff, buff_size, offset, "/%i", connection->ttl);
	if (error != BELLE_SIP_OK) return error;
	if (connection->range > 0) error = belle_sip_snprintf(buff, buff_size, offset, "/%i", connection->range);
	return error;
}

BELLE_SDP_NEW(connection, belle_sip_object)
BELLE_SDP_PARSE(connection)
belle_sdp_connection_t *belle_sdp_connection_create(const char *net_type, const char *addr_type, const char *addr) {
	belle_sdp_connection_t *connection = belle_sdp_connection_new();
	belle_sdp_connection_set_network_type(connection, net_type);
	belle_sdp_connection_set_address_type(connection, addr_type);
	belle_sdp_connection_set_address(connection, addr);
	return connection;
}
GET_SET_STRING(belle_sdp_connection, network_type);
GET_SET_STRING(belle_sdp_connection, address_type);
GET_SET_STRING(belle_sdp_connection, address);
GET_SET_INT(belle_sdp_connection, ttl, int);
GET_SET_INT(belle_sdp_connection, range, int);
/************************
 * email
 ***********************/
struct _belle_sdp_email {
	belle_sip_object_t base;
	char *value;
};

void belle_sdp_email_destroy(belle_sdp_email_t *email) {
	DESTROY_STRING(email, value)
}

void belle_sdp_email_clone(belle_sdp_email_t *email,
                           const belle_sdp_email_t *orig){CLONE_STRING(belle_sdp_email, value, email, orig)}

belle_sip_error_code belle_sdp_email_marshal(belle_sdp_email_t *email, char *buff, size_t buff_size, size_t *offset) {
	return belle_sip_snprintf(buff, buff_size, offset, "e=%s", email->value);
}

BELLE_SDP_NEW(email, belle_sip_object)
BELLE_SDP_PARSE(email)
GET_SET_STRING(belle_sdp_email, value);
/************************
 * info
 ***********************/
struct _belle_sdp_info {
	belle_sip_object_t base;
	const char *value;
};

void belle_sdp_info_destroy(belle_sdp_info_t *info) {
	DESTROY_STRING(info, value)
}

void belle_sdp_info_clone(belle_sdp_info_t *info,
                          const belle_sdp_info_t *orig){CLONE_STRING(belle_sdp_info, value, info, orig)}

belle_sip_error_code belle_sdp_info_marshal(belle_sdp_info_t *info, char *buff, size_t buff_size, size_t *offset) {
	return belle_sip_snprintf(buff, buff_size, offset, "i=%s", info->value);
}

BELLE_SDP_NEW(info, belle_sip_object)
BELLE_SDP_PARSE(info)
GET_SET_STRING(belle_sdp_info, value);
/************************
 * media
 ***********************/
struct _belle_sdp_media {
	belle_sip_object_t base;
	const char *media_type;
	int media_port;
	belle_sip_list_t *media_formats;
	int port_count;
	const char *protocol;
	const char *raw_fmt;
};
belle_sip_list_t *belle_sdp_media_get_media_formats(const belle_sdp_media_t *media) {
	return media->media_formats;
}
void belle_sdp_media_set_media_formats(belle_sdp_media_t *media, belle_sip_list_t *formats) {
	/*belle_sip_list_free(media->media_formats); to allow easy list management might be better to add an append format
	 * method*/
	media->media_formats = formats;
}
void belle_sdp_media_media_formats_add(belle_sdp_media_t *media, const char *fmt) {
	media->media_formats = belle_sip_list_append(media->media_formats, (void *)(intptr_t)atoi(fmt));
}
void belle_sdp_media_destroy(belle_sdp_media_t *media) {
	DESTROY_STRING(media, media_type)
	belle_sip_list_free(media->media_formats);
	DESTROY_STRING(media, protocol)
}
static void belle_sdp_media_init(belle_sdp_media_t *media) {
	media->port_count = 1;
}
void belle_sdp_media_clone(belle_sdp_media_t *media, const belle_sdp_media_t *orig) {
	CLONE_STRING(belle_sdp_media, media_type, media, orig)
	media->media_port = orig->media_port;
	media->media_formats = belle_sip_list_copy(orig->media_formats);
	media->port_count = orig->port_count;
	CLONE_STRING(belle_sdp_media, protocol, media, orig)
}

belle_sip_error_code belle_sdp_media_marshal(belle_sdp_media_t *media, char *buff, size_t buff_size, size_t *offset) {
	belle_sip_list_t *list = media->media_formats;

	belle_sip_error_code error =
	    belle_sip_snprintf(buff, buff_size, offset, "m=%s %i", media->media_type, media->media_port);
	if (error != BELLE_SIP_OK) return error;

	if (media->port_count > 1) {
		error = belle_sip_snprintf(buff, buff_size, offset, "/%i", media->port_count);
		if (error != BELLE_SIP_OK) return error;
	}

	error = belle_sip_snprintf(buff, buff_size, offset, " %s", media->protocol);
	if (error != BELLE_SIP_OK) return error;

	for (; list != NULL; list = list->next) {
		error = belle_sip_snprintf(buff, buff_size, offset, " %li", (long)(intptr_t)list->data);
		if (error != BELLE_SIP_OK) return error;
	}

	return error;
}

BELLE_SDP_NEW_WITH_CTR(media, belle_sip_object)
BELLE_SDP_PARSE(media)
belle_sdp_media_t *belle_sdp_media_create(const char *media_type,
                                          int media_port,
                                          int port_count,
                                          const char *protocol,
                                          belle_sip_list_t *static_media_formats) {
	belle_sdp_media_t *media = belle_sdp_media_new();
	belle_sdp_media_set_media_type(media, media_type);
	belle_sdp_media_set_media_port(media, media_port);
	belle_sdp_media_set_port_count(media, port_count);
	belle_sdp_media_set_protocol(media, protocol);
	if (static_media_formats) belle_sdp_media_set_media_formats(media, static_media_formats);
	return media;
}
GET_SET_STRING(belle_sdp_media, media_type);
GET_SET_STRING(belle_sdp_media, protocol);
GET_SET_INT(belle_sdp_media, media_port, int)
GET_SET_INT(belle_sdp_media, port_count, int)

/************************
 * base_description
 ***********************/
typedef struct _belle_sdp_base_description {
	belle_sip_object_t base;
	belle_sdp_info_t *info;
	belle_sdp_connection_t *connection;
	belle_sip_list_t *bandwidths;
	belle_sip_list_t *attributes;
} belle_sdp_base_description_t;

static void belle_sdp_base_description_destroy(belle_sdp_base_description_t *base_description) {
	if (base_description->info) belle_sip_object_unref(BELLE_SIP_OBJECT(base_description->info));
	if (base_description->connection) belle_sip_object_unref(BELLE_SIP_OBJECT(base_description->connection));
	belle_sip_list_free_with_data(base_description->bandwidths, belle_sip_object_freefunc);
	belle_sip_list_free_with_data(base_description->attributes, belle_sip_object_freefunc);
}
static void belle_sdp_base_description_init(belle_sdp_base_description_t *base_description) {
}
static void belle_sdp_base_description_clone(belle_sdp_base_description_t *base_description,
                                             const belle_sdp_base_description_t *orig) {
	if (orig->info)
		base_description->info = BELLE_SDP_INFO(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->info)));
	if (orig->connection)
		base_description->connection =
		    BELLE_SDP_CONNECTION(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->connection)));
	base_description->bandwidths = belle_sip_list_copy_with_data(orig->bandwidths, belle_sip_object_copyfunc);
	base_description->attributes = belle_sip_list_copy_with_data(orig->attributes, belle_sip_object_copyfunc);
}

belle_sip_error_code belle_sdp_base_description_marshal(belle_sdp_base_description_t *base_description,
                                                        char *buff,
                                                        size_t buff_size,
                                                        size_t *offset) {
	belle_sip_error_code error = BELLE_SIP_OK;
	belle_sip_list_t *bandwidths;

	if (base_description->info) {
		error = belle_sip_object_marshal(BELLE_SIP_OBJECT(base_description->info), buff, buff_size, offset);
		if (error != BELLE_SIP_OK) return error;
		error = belle_sip_snprintf(buff, buff_size, offset, "\r\n");
		if (error != BELLE_SIP_OK) return error;
	}

	if (base_description->connection) {
		error = belle_sip_object_marshal(BELLE_SIP_OBJECT(base_description->connection), buff, buff_size, offset);
		if (error != BELLE_SIP_OK) return error;
		error = belle_sip_snprintf(buff, buff_size, offset, "\r\n");
		if (error != BELLE_SIP_OK) return error;
	}

	for (bandwidths = base_description->bandwidths; bandwidths != NULL; bandwidths = bandwidths->next) {
		error = belle_sip_object_marshal(BELLE_SIP_OBJECT(bandwidths->data), buff, buff_size, offset);
		if (error != BELLE_SIP_OK) return error;
		error = belle_sip_snprintf(buff, buff_size, offset, "\r\n");
		if (error != BELLE_SIP_OK) return error;
	}

	return error;
}

BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sdp_base_description_t);
BELLE_SIP_INSTANCIATE_VPTR(belle_sdp_base_description_t,
                           belle_sip_object_t,
                           belle_sdp_base_description_destroy,
                           belle_sdp_base_description_clone,
                           belle_sdp_base_description_marshal,
                           FALSE);

static int belle_sdp_base_description_attribute_comp_func(const belle_sdp_attribute_t *a, const char *b) {
	return strcmp(a->name, b);
}
belle_sdp_attribute_t *belle_sdp_base_description_get_attribute(const belle_sdp_base_description_t *base_description,
                                                                const char *name) {
	belle_sip_list_t *attribute;
	attribute = belle_sip_list_find_custom(
	    base_description->attributes, (belle_sip_compare_func)belle_sdp_base_description_attribute_comp_func, name);
	if (attribute) {
		return ((belle_sdp_attribute_t *)attribute->data);
	} else {
		return NULL;
	}
}
const char *belle_sdp_base_description_get_attribute_value(const belle_sdp_base_description_t *base_description,
                                                           const char *name) {
	belle_sdp_attribute_t *attribute = belle_sdp_base_description_get_attribute(base_description, name);
	if (attribute) {
		return belle_sdp_attribute_get_value(attribute);
	} else return NULL;
}
belle_sip_list_t *belle_sdp_base_description_get_attributes(const belle_sdp_base_description_t *base_description) {
	return base_description->attributes;
}
belle_sip_list_t *
belle_sdp_base_description_find_attributes_with_name(const belle_sdp_base_description_t *base_description,
                                                     const char *name) {
	belle_sip_list_t *matches = NULL;
	const belle_sip_list_t *attributes = belle_sdp_base_description_get_attributes(base_description);
	for (; attributes != NULL; attributes = attributes->next) {
		belle_sdp_attribute_t *attribute = ((belle_sdp_attribute_t *)attributes->data);
		if (belle_sdp_base_description_attribute_comp_func(attribute, name) == 0) {
			matches = belle_sip_list_append(matches, belle_sip_object_ref(attribute));
		}
	}
	return matches;
}
static int belle_sdp_base_description_bandwidth_comp_func(const belle_sdp_bandwidth_t *a, const char *b) {
	return strcmp(a->type, b);
}

belle_sdp_bandwidth_t *belle_sdp_base_description_get_bandwidth(const belle_sdp_base_description_t *base_description,
                                                                const char *name) {
	belle_sip_list_t *found = belle_sip_list_find_custom(
	    base_description->bandwidths, (belle_sip_compare_func)belle_sdp_base_description_bandwidth_comp_func, name);
	if (found) {
		return ((belle_sdp_bandwidth_t *)found->data);
	} else {
		return NULL;
	}
}

int belle_sdp_base_description_get_bandwidth_value(const belle_sdp_base_description_t *base_description,
                                                   const char *name) {
	belle_sip_list_t *bandwidth;
	bandwidth = belle_sip_list_find_custom(
	    base_description->bandwidths, (belle_sip_compare_func)belle_sdp_base_description_bandwidth_comp_func, name);
	if (bandwidth) {
		return ((belle_sdp_bandwidth_t *)bandwidth->data)->value;
	} else {
		return -1;
	}
}
void belle_sdp_base_description_remove_attribute(belle_sdp_base_description_t *base_description, const char *name) {
	belle_sip_list_t *attribute;
	attribute = belle_sip_list_find_custom(
	    base_description->attributes, (belle_sip_compare_func)belle_sdp_base_description_attribute_comp_func, name);
	if (attribute) {
		belle_sip_object_unref(BELLE_SIP_OBJECT(attribute->data));
		base_description->attributes = belle_sip_list_delete_link(base_description->attributes, attribute);
	}
}
void belle_sdp_base_description_remove_bandwidth(belle_sdp_base_description_t *base_description, const char *name) {
	belle_sip_list_t *bandwidth;
	bandwidth = belle_sip_list_find_custom(
	    base_description->bandwidths, (belle_sip_compare_func)belle_sdp_base_description_bandwidth_comp_func, name);
	if (bandwidth) {
		belle_sip_object_unref(BELLE_SIP_OBJECT(bandwidth->data));
		base_description->bandwidths = belle_sip_list_delete_link(base_description->bandwidths, bandwidth);
	}
}
void belle_sdp_base_description_set_attribute_value(belle_sdp_base_description_t *base_description,
                                                    const char *name,
                                                    const char *value) {
	belle_sdp_raw_attribute_t *attribute = belle_sdp_raw_attribute_new();
	belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), name);
	belle_sdp_raw_attribute_set_value(attribute, value);
	base_description->attributes = belle_sip_list_append(base_description->attributes, belle_sip_object_ref(attribute));
}

#define SPECIALIZED_ATTRIBUTE_HAS_INCORRECT_TYPE(attribute_ptr, attribute_name, attribute_type)                        \
	(strcmp((attribute_ptr)->name, attribute_name) == 0 && !BELLE_SIP_IS_INSTANCE_OF(attribute_ptr, attribute_type))

void belle_sdp_base_description_add_attribute(belle_sdp_base_description_t *base_description,
                                              belle_sdp_attribute_t *attribute) {
	/* Sanity check to avoid specialized attributes to be parsed as raw attribute as a fallback.
	 * The application code will be confused not to be able to cast the attribute in its specialized type.
	 */
	if (SPECIALIZED_ATTRIBUTE_HAS_INCORRECT_TYPE(attribute, "rtcp-fb", belle_sdp_rtcp_fb_attribute_t) ||
	    SPECIALIZED_ATTRIBUTE_HAS_INCORRECT_TYPE(attribute, "rtcp-xr", belle_sdp_rtcp_xr_attribute_t) ||
	    SPECIALIZED_ATTRIBUTE_HAS_INCORRECT_TYPE(attribute, "content", belle_sdp_content_attribute_t) ||
	    SPECIALIZED_ATTRIBUTE_HAS_INCORRECT_TYPE(attribute, "label", belle_sdp_label_attribute_t) ||
	    SPECIALIZED_ATTRIBUTE_HAS_INCORRECT_TYPE(attribute, "creq", belle_sdp_creq_attribute_t) ||
	    SPECIALIZED_ATTRIBUTE_HAS_INCORRECT_TYPE(attribute, "csup", belle_sdp_csup_attribute_t) ||
	    SPECIALIZED_ATTRIBUTE_HAS_INCORRECT_TYPE(attribute, "tcap", belle_sdp_tcap_attribute_t) ||
	    SPECIALIZED_ATTRIBUTE_HAS_INCORRECT_TYPE(attribute, "acap", belle_sdp_acap_attribute_t) ||
	    SPECIALIZED_ATTRIBUTE_HAS_INCORRECT_TYPE(attribute, "csup", belle_sdp_csup_attribute_t) ||
	    SPECIALIZED_ATTRIBUTE_HAS_INCORRECT_TYPE(attribute, "acfg", belle_sdp_acfg_attribute_t) ||
	    SPECIALIZED_ATTRIBUTE_HAS_INCORRECT_TYPE(attribute, "pcfg", belle_sdp_pcfg_attribute_t)) {
		belle_sip_error("Erroneously parsed attribute with name [%s] and value [%s], skipped",
		                belle_sdp_attribute_get_name(attribute), belle_sdp_attribute_get_value(attribute));
		belle_sip_object_unref(attribute);
		return;
	}
	base_description->attributes =
	    belle_sip_list_append(base_description->attributes, (void *)belle_sip_object_ref(BELLE_SIP_OBJECT(attribute)));
}

#define SET_LIST(list_name, value)                                                                                     \
	belle_sip_list_t *list;                                                                                            \
	if (list_name) {                                                                                                   \
		belle_sip_list_free_with_data(list_name, belle_sip_object_unref);                                              \
	}                                                                                                                  \
	for (list = value; list != NULL; list = list->next) {                                                              \
		belle_sip_object_ref(BELLE_SIP_OBJECT(list->data));                                                            \
	}                                                                                                                  \
	list_name = value;

void belle_sdp_base_description_set_attributes(belle_sdp_base_description_t *base_description,
                                               belle_sip_list_t *attributes) {
	SET_LIST(base_description->attributes, attributes)
}
void belle_sdp_base_description_set_bandwidth(belle_sdp_base_description_t *base_description,
                                              const char *type,
                                              int value) {

	belle_sdp_bandwidth_t *bandwidth =
	    BELLE_SDP_BANDWIDTH(belle_sdp_base_description_get_bandwidth(base_description, type));
	if (bandwidth == NULL) {
		bandwidth = belle_sdp_bandwidth_new();
		belle_sdp_bandwidth_set_type(bandwidth, type);
		belle_sdp_bandwidth_set_value(bandwidth, value);
		base_description->bandwidths =
		    belle_sip_list_append(base_description->bandwidths, belle_sip_object_ref(bandwidth));
	} else {
		belle_sdp_bandwidth_set_value(bandwidth, value);
	}
}
void belle_sdp_base_description_add_bandwidth(belle_sdp_base_description_t *base_description,
                                              const belle_sdp_bandwidth_t *bandwidth) {

	base_description->bandwidths =
	    belle_sip_list_append(base_description->bandwidths, (void *)belle_sip_object_ref((void *)bandwidth));
}
void belle_sdp_base_description_set_bandwidths(belle_sdp_base_description_t *base_description,
                                               belle_sip_list_t *bandwidths) {
	SET_LIST(base_description->bandwidths, bandwidths)
}

/************************
 * media_description
 ***********************/
struct _belle_sdp_media_description {
	belle_sdp_base_description_t base_description;
	belle_sdp_media_t *media;
};
void belle_sdp_media_description_destroy(belle_sdp_media_description_t *media_description) {
	if (media_description->media) belle_sip_object_unref(BELLE_SIP_OBJECT((media_description->media)));
}

void belle_sdp_media_description_clone(belle_sdp_media_description_t *media_description,
                                       const belle_sdp_media_description_t *orig) {
	if (orig->media)
		media_description->media = BELLE_SDP_MEDIA(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT((orig->media))));
}

belle_sip_error_code belle_sdp_media_description_marshal(belle_sdp_media_description_t *media_description,
                                                         char *buff,
                                                         size_t buff_size,
                                                         size_t *offset) {
	belle_sip_list_t *attributes;
	belle_sip_error_code error =
	    belle_sip_object_marshal(BELLE_SIP_OBJECT(media_description->media), buff, buff_size, offset);

	if (error != BELLE_SIP_OK) return error;
	error = belle_sip_snprintf(buff, buff_size, offset, "\r\n");

	if (error != BELLE_SIP_OK) return error;
	error = belle_sdp_base_description_marshal(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t), buff,
	                                           buff_size, offset);

	if (error != BELLE_SIP_OK) return error;

	for (attributes = media_description->base_description.attributes; attributes != NULL;
	     attributes = attributes->next) {
		error = belle_sip_object_marshal(BELLE_SIP_OBJECT(attributes->data), buff, buff_size, offset);
		if (error != BELLE_SIP_OK) return error;
		error = belle_sip_snprintf(buff, buff_size, offset, "\r\n");
		if (error != BELLE_SIP_OK) return error;
	}

	return error;
}

BELLE_SDP_NEW(media_description, belle_sdp_base_description)
belle_sdp_media_description_t *belle_sdp_media_description_create(const char *media_type,
                                                                  int media_port,
                                                                  int port_count,
                                                                  const char *protocol,
                                                                  belle_sip_list_t *static_media_formats) {
	belle_sdp_media_description_t *media_desc = belle_sdp_media_description_new();
	belle_sdp_media_description_set_media(
	    media_desc, belle_sdp_media_create(media_type, media_port, port_count, protocol, static_media_formats));
	return media_desc;
}
BELLE_SDP_PARSE(media_description)
void belle_sdp_media_description_add_dynamic_payloads(belle_sdp_media_description_t *media_description,
                                                      belle_sip_list_t *payloadNames,
                                                      belle_sip_list_t *payloadValues) {
	belle_sip_error("belle_sdp_media_description_add_dynamic_payloads not implemented yet");
}
belle_sdp_attribute_t *belle_sdp_media_description_get_attribute(const belle_sdp_media_description_t *media_description,
                                                                 const char *name) {
	return belle_sdp_base_description_get_attribute(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t),
	                                                name);
}
const char *belle_sdp_media_description_get_attribute_value(const belle_sdp_media_description_t *media_description,
                                                            const char *name) {
	return belle_sdp_base_description_get_attribute_value(
	    BELLE_SIP_CAST(media_description, belle_sdp_base_description_t), name);
}
belle_sip_list_t *belle_sdp_media_description_get_attributes(const belle_sdp_media_description_t *media_description) {
	return BELLE_SIP_CAST(media_description, belle_sdp_base_description_t)->attributes;
}
belle_sip_list_t *
belle_sdp_media_description_find_attributes_with_name(const belle_sdp_media_description_t *media_description,
                                                      const char *name) {
	return belle_sdp_base_description_find_attributes_with_name(
	    BELLE_SIP_CAST(media_description, belle_sdp_base_description_t), name);
}

int belle_sdp_media_description_get_bandwidth(const belle_sdp_media_description_t *media_description,
                                              const char *name) {
	return belle_sdp_base_description_get_bandwidth_value(
	    BELLE_SIP_CAST(media_description, belle_sdp_base_description_t), name);
}
belle_sip_list_t *belle_sdp_media_description_get_bandwidths(const belle_sdp_media_description_t *media_description) {
	return BELLE_SIP_CAST(media_description, belle_sdp_base_description_t)->bandwidths;
}
belle_sdp_connection_t *
belle_sdp_media_description_get_connection(const belle_sdp_media_description_t *media_description) {
	return BELLE_SIP_CAST(media_description, belle_sdp_base_description_t)->connection;
}
belle_sdp_info_t *belle_sdp_media_description_get_info(const belle_sdp_media_description_t *media_description) {
	return BELLE_SIP_CAST(media_description, belle_sdp_base_description_t)->info;
}
/*belle_sdp_key_t*  belle_sdp_media_description_get_key(const belle_sdp_media_description_t* media_description);*/
belle_sdp_media_t *belle_sdp_media_description_get_media(const belle_sdp_media_description_t *media_description) {
	return media_description->media;
}

void belle_sdp_media_description_add_attribute_holder(belle_sdp_media_description_t *media_description,
                                                      belle_sdp_attribute_holder_t *holder) {
	belle_sdp_base_description_add_attribute(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t),
	                                         holder->attribute);
	belle_sip_free(holder);
}

struct static_payload {
	unsigned char number;
	int channel_count;
	const char *type;
	int rate;
};
#define STATIC_PAYLOAD_LIST_LENTH 8
/*
 * rfc 3551
 * PT   encoding    media type  clock rate   channels
                    name                    (Hz)
               ___________________________________________________
               0    PCMU        A            8,000       1
               1    reserved    A
               2    reserved    A
               3    GSM         A            8,000       1
               4    G723        A            8,000       1
               5    DVI4        A            8,000       1
               6    DVI4        A           16,000       1
               7    LPC         A            8,000       1
               8    PCMA        A            8,000       1
               9    G722        A            8,000       1
               10   L16         A           44,100       2
               11   L16         A           44,100       1
               12   QCELP       A            8,000       1
               13   CN          A            8,000       1
               14   MPA         A           90,000       (see text)
               15   G728        A            8,000       1
               16   DVI4        A           11,025       1
               17   DVI4        A           22,050       1
               18   G729        A            8,000       1
               Table 4: Payload types (PT) for audio encodings

  PT      encoding    media type  clock rate
                       name                    (Hz)
               _____________________________________________
               24      unassigned  V
               25      CelB        V           90,000
               26      JPEG        V           90,000
               27      unassigned  V
               28      nv          V           90,000
               29      unassigned  V
               30      unassigned  V
               31      H261        V           90,000
               32      MPV         V           90,000
               33      MP2T        AV          90,000
               34      H263        V           90,000

               Table 5: Payload types (PT) for video and combined
                        encodings


 *
 * */

const struct static_payload static_payload_list[] = {
    /*audio*/
    {0, 1, "PCMU", 8000},
    {3, 1, "GSM", 8000},
    {4, 1, "G723", 8000},
    {5, 1, "DVI4", 8000},
    {6, 1, "DVI4", 16000},
    {7, 1, "LPC", 8000},
    {8, 1, "PCMA", 8000},
    {9, 1, "G722", 8000},
    {10, 2, "L16", 44100},
    {11, 1, "L16", 44100},
    {12, 1, "QCELP", 8000},
    {13, 1, "CN", 8000},
    {14, 1, "MPA", 90000},
    {15, 1, "G728", 8000},
    {16, 1, "DVI4", 11025},
    {17, 1, "DVI4", 22050},
    {18, 1, "G729", 8000},
    /*video*/
    {25, 0, "CelB", 90000},
    {26, 0, "JPEG", 90000},
    {28, 0, "nv", 90000},
    {31, 0, "H261", 90000},
    {32, 0, "MPV", 90000},
    {33, 0, "MP2T", 90000},
    {34, 0, "H263", 90000}};

static const size_t payload_list_elements = sizeof(static_payload_list) / sizeof(struct static_payload);

static int mime_parameter_is_static(const belle_sdp_mime_parameter_t *param) {
	const struct static_payload *iterator;
	size_t i;

	for (iterator = static_payload_list, i = 0; i < payload_list_elements; i++, iterator++) {
		if (iterator->number == param->media_format && strcasecmp(iterator->type, param->type) == 0 &&
		    iterator->channel_count == param->channel_count && iterator->rate == param->rate) {
			return TRUE;
		}
	}
	return FALSE;
}

static int mime_parameter_fill_from_static(belle_sdp_mime_parameter_t *mime_parameter, int format) {
	const struct static_payload *iterator;
	size_t i;

	for (iterator = static_payload_list, i = 0; i < payload_list_elements; i++, iterator++) {
		if (iterator->number == format) {
			belle_sdp_mime_parameter_set_type(mime_parameter, iterator->type);
			belle_sdp_mime_parameter_set_rate(mime_parameter, iterator->rate);
			belle_sdp_mime_parameter_set_channel_count(mime_parameter, iterator->channel_count);
			break;
		}
	}
	return 0;
}

static int
mime_parameter_fill_from_rtpmap(belle_sdp_mime_parameter_t *mime_parameter, const char *rtpmap, int is_audio) {
	char *mime = belle_sip_strdup(rtpmap);
	char *p = strchr(mime, '/');
	if (p) {
		char *chans;
		*p = '\0';
		p++;
		chans = strchr(p, '/');
		if (chans) {
			*chans = '\0';
			chans++;
			belle_sdp_mime_parameter_set_channel_count(mime_parameter, atoi(chans));
		} else if (is_audio)
			belle_sdp_mime_parameter_set_channel_count(
			    mime_parameter, 1); /*in absence of channel count, 1 is implicit for audio streams*/
		belle_sdp_mime_parameter_set_rate(mime_parameter, atoi(p));
	}
	belle_sdp_mime_parameter_set_type(mime_parameter, mime);
	belle_sip_free(mime);
	return 0;
}
/* return the value of attr "field" for payload pt at line pos (field=rtpmap,fmtp...)*/
static const char *belle_sdp_media_description_a_attr_value_get_with_pt(
    const belle_sdp_media_description_t *media_description, int pt, const char *field) {
	int tmppt = 0, scanned = 0;
	const char *tmp;
	belle_sdp_attribute_t *attr;
	belle_sip_list_t *attribute_list;
	for (attribute_list = belle_sdp_media_description_get_attributes(media_description); attribute_list != NULL;
	     attribute_list = attribute_list->next) {

		attr = BELLE_SDP_ATTRIBUTE(attribute_list->data);
		if (strcmp(field, belle_sdp_attribute_get_name(attr)) == 0 && belle_sdp_attribute_get_value(attr) != NULL) {
			int nb = sscanf(belle_sdp_attribute_get_value(attr), "%i %n", &tmppt, &scanned);
			/* the return value may depend on how %n is interpreted by the libc: see manpage*/
			if (nb == 1 || nb == 2) {
				if (pt == tmppt) {
					tmp = belle_sdp_attribute_get_value(attr) + scanned;
					if (strlen(tmp) > 0) return tmp;
				}
			} else belle_sip_warning("sdp has a strange a= line (%s) nb=%i", belle_sdp_attribute_get_value(attr), nb);
		}
	}
	return NULL;
}

belle_sip_list_t *
belle_sdp_media_description_build_mime_parameters(const belle_sdp_media_description_t *media_description) {
	/*First, get media type*/
	belle_sdp_media_t *media = belle_sdp_media_description_get_media(media_description);
	belle_sip_list_t *mime_parameter_list = NULL;
	belle_sip_list_t *media_formats = NULL;
	belle_sdp_mime_parameter_t *mime_parameter;
	const char *rtpmap = NULL;
	const char *fmtp = NULL;
	const char *ptime = NULL;
	const char *max_ptime = NULL;
	int ptime_as_int = -1;
	int max_ptime_as_int = -1;
	int is_audio = 0;

	if (!media) {
		belle_sip_error("belle_sdp_media_description_build_mime_parameters: no media");
		return NULL;
	}
	if (strcasecmp(belle_sdp_media_get_media_type(media), "audio") == 0) is_audio = 1;
	ptime = belle_sdp_media_description_get_attribute_value(media_description, "ptime");
	ptime ? ptime_as_int = atoi(ptime) : -1;
	max_ptime = belle_sdp_media_description_get_attribute_value(media_description, "maxptime");
	max_ptime ? max_ptime_as_int = atoi(max_ptime) : -1;

	for (media_formats = belle_sdp_media_get_media_formats(media); media_formats != NULL;
	     media_formats = media_formats->next) {
		/*create mime parameters with format*/
		mime_parameter = belle_sdp_mime_parameter_new();
		belle_sdp_mime_parameter_set_ptime(mime_parameter, ptime_as_int);
		belle_sdp_mime_parameter_set_max_ptime(mime_parameter, max_ptime_as_int);
		belle_sdp_mime_parameter_set_media_format(mime_parameter, (int)(intptr_t)media_formats->data);

		/*get rtpmap*/
		rtpmap = belle_sdp_media_description_a_attr_value_get_with_pt(
		    media_description, belle_sdp_mime_parameter_get_media_format(mime_parameter), "rtpmap");
		if (rtpmap) {
			mime_parameter_fill_from_rtpmap(mime_parameter, rtpmap, is_audio);
		} else {
			mime_parameter_fill_from_static(mime_parameter, belle_sdp_mime_parameter_get_media_format(mime_parameter));
		}
		fmtp = belle_sdp_media_description_a_attr_value_get_with_pt(
		    media_description, belle_sdp_mime_parameter_get_media_format(mime_parameter), "fmtp");
		if (fmtp) {
			belle_sdp_mime_parameter_set_parameters(mime_parameter, fmtp);
		}

		mime_parameter_list = belle_sip_list_append(mime_parameter_list, mime_parameter);
	}
	return mime_parameter_list;
}
#define MAX_FMTP_LENGTH 512

void belle_sdp_media_description_append_values_from_mime_parameter(belle_sdp_media_description_t *media_description,
                                                                   const belle_sdp_mime_parameter_t *mime_parameter) {
	belle_sdp_media_t *media = belle_sdp_media_description_get_media(media_description);
	char atribute_value[MAX_FMTP_LENGTH];
	int current_ptime = 0;
	int current_max_ptime = 0;

	belle_sdp_media_set_media_formats(
	    media, belle_sip_list_append(belle_sdp_media_get_media_formats(media),
	                                 (void *)(intptr_t)(belle_sdp_mime_parameter_get_media_format(mime_parameter))));

	if (belle_sdp_media_description_get_attribute_value(media_description, "ptime")) {
		current_ptime = atoi(belle_sdp_media_description_get_attribute_value(media_description, "ptime"));
		belle_sdp_media_description_remove_attribute(media_description, "ptime");
	}

	if (belle_sdp_media_description_get_attribute_value(media_description, "maxptime")) {
		current_max_ptime = atoi(belle_sdp_media_description_get_attribute_value(media_description, "maxptime"));
		belle_sdp_media_description_remove_attribute(media_description, "maxptime");
	}

#ifndef BELLE_SDP_FORCE_RTP_MAP /* defined to for RTP map even for static codec*/
	if (!mime_parameter_is_static(mime_parameter)) {
		/*dynamic payload*/
#endif
		if (belle_sdp_mime_parameter_get_channel_count(mime_parameter) > 1) {
			snprintf(atribute_value, MAX_FMTP_LENGTH, "%i %s/%i/%i",
			         belle_sdp_mime_parameter_get_media_format(mime_parameter),
			         belle_sdp_mime_parameter_get_type(mime_parameter),
			         belle_sdp_mime_parameter_get_rate(mime_parameter),
			         belle_sdp_mime_parameter_get_channel_count(mime_parameter));
		} else {
			snprintf(
			    atribute_value, MAX_FMTP_LENGTH, "%i %s/%i", belle_sdp_mime_parameter_get_media_format(mime_parameter),
			    belle_sdp_mime_parameter_get_type(mime_parameter), belle_sdp_mime_parameter_get_rate(mime_parameter));
		}
		belle_sdp_media_description_set_attribute_value(media_description, "rtpmap", atribute_value);
#ifndef BELLE_SDP_FORCE_RTP_MAP
	}
#endif

	// always include fmtp parameters if available
	if (belle_sdp_mime_parameter_get_parameters(mime_parameter)) {
		snprintf(atribute_value, MAX_FMTP_LENGTH, "%i %s", belle_sdp_mime_parameter_get_media_format(mime_parameter),
		         belle_sdp_mime_parameter_get_parameters(mime_parameter));
		belle_sdp_media_description_set_attribute_value(media_description, "fmtp", atribute_value);
	}

	if (belle_sdp_mime_parameter_get_ptime(mime_parameter) > current_ptime) {
		current_ptime = belle_sdp_mime_parameter_get_ptime(mime_parameter);
	}
	if (current_ptime > 0) {
		char ptime[12];
		snprintf(ptime, sizeof(ptime), "%i", current_ptime);
		belle_sdp_media_description_set_attribute_value(media_description, "ptime", ptime);
	}

	if (belle_sdp_mime_parameter_get_max_ptime(mime_parameter) > current_max_ptime) {
		current_max_ptime = belle_sdp_mime_parameter_get_max_ptime(mime_parameter);
	}
	if (current_max_ptime > 0) {
		char max_ptime[12];
		snprintf(max_ptime, sizeof(max_ptime), "%i", current_max_ptime);
		belle_sdp_media_description_set_attribute_value(media_description, "maxptime", max_ptime);
	}
}
belle_sip_list_t *belle_sdp_media_description_get_mime_types(const belle_sdp_media_description_t *media_description) {
	belle_sip_error("belle_sdp_media_description_get_mime_types: not implemented yet");
	return NULL;
}

void belle_sdp_media_description_remove_attribute(belle_sdp_media_description_t *media_description, const char *name) {
	belle_sdp_base_description_remove_attribute(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t), name);
}
void belle_sdp_media_description_remove_bandwidth(belle_sdp_media_description_t *media_description, const char *name) {
	belle_sdp_base_description_remove_bandwidth(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t), name);
}
void belle_sdp_media_description_set_attribute_value(belle_sdp_media_description_t *media_description,
                                                     const char *name,
                                                     const char *value) {
	belle_sdp_base_description_set_attribute_value(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t),
	                                               name, value);
}
void belle_sdp_media_description_set_attributes(belle_sdp_media_description_t *media_description,
                                                belle_sip_list_t *value) {
	belle_sdp_base_description_set_attributes(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t), value);
}

void belle_sdp_media_description_add_attribute(belle_sdp_media_description_t *media_description,
                                               belle_sdp_attribute_t *attribute) {
	belle_sdp_base_description_add_attribute(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t),
	                                         attribute);
}
void belle_sdp_media_description_set_bandwidth(belle_sdp_media_description_t *media_description,
                                               const char *type,
                                               int value) {
	belle_sdp_base_description_set_bandwidth(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t), type,
	                                         value);
}
void belle_sdp_media_description_add_bandwidth(belle_sdp_media_description_t *media_description,
                                               belle_sdp_bandwidth_t *bandwidth) {
	belle_sdp_base_description_add_bandwidth(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t),
	                                         bandwidth);
}
void belle_sdp_media_description_set_bandwidths(belle_sdp_media_description_t *media_description,
                                                belle_sip_list_t *bandwidths) {
	belle_sdp_base_description_set_bandwidths(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t),
	                                          bandwidths);
}
#define SET_OBJECT(object, param, param_type)                                                                          \
	param_type **current = &object->param;                                                                             \
	if (param) belle_sip_object_ref(param);                                                                            \
	if (*current) {                                                                                                    \
		belle_sip_object_unref(BELLE_SIP_OBJECT(*current));                                                            \
	}                                                                                                                  \
	*current = param;

void belle_sdp_media_description_set_connection(belle_sdp_media_description_t *media_description,
                                                belle_sdp_connection_t *connection) {
	SET_OBJECT(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t), connection, belle_sdp_connection_t)
}
void belle_sdp_media_description_set_info(belle_sdp_media_description_t *media_description, belle_sdp_info_t *info) {
	SET_OBJECT(BELLE_SIP_CAST(media_description, belle_sdp_base_description_t), info, belle_sdp_info_t)
}
void belle_sdp_media_description_set_media(belle_sdp_media_description_t *media_description, belle_sdp_media_t *media) {
	SET_OBJECT(media_description, media, belle_sdp_media_t)
}

/************************
 * origin
 ***********************/
struct _belle_sdp_origin {
	belle_sip_object_t base;
	const char *address;
	const char *address_type;
	const char *network_type;
	const char *username;
	unsigned int session_id;
	unsigned int session_version;
};

void belle_sdp_origin_destroy(belle_sdp_origin_t *origin) {
	DESTROY_STRING(origin, address)
	DESTROY_STRING(origin, address_type)
	DESTROY_STRING(origin, network_type)
	DESTROY_STRING(origin, username)
}

void belle_sdp_origin_clone(belle_sdp_origin_t *origin, const belle_sdp_origin_t *orig) {
	CLONE_STRING(belle_sdp_origin, username, origin, orig);
	CLONE_STRING(belle_sdp_origin, address, origin, orig);
	CLONE_STRING(belle_sdp_origin, address_type, origin, orig);
	CLONE_STRING(belle_sdp_origin, network_type, origin, orig);
	origin->session_id = orig->session_id;
	origin->session_version = orig->session_version;
}

belle_sip_error_code
belle_sdp_origin_marshal(belle_sdp_origin_t *origin, char *buff, size_t buff_size, size_t *offset) {
	return belle_sip_snprintf(buff, buff_size, offset, "o=%s %u %u %s %s %s", origin->username, origin->session_id,
	                          origin->session_version, origin->network_type, origin->address_type, origin->address);
}

BELLE_SDP_NEW(origin, belle_sip_object)
belle_sdp_origin_t *belle_sdp_origin_create(const char *user_name,
                                            unsigned int session_id,
                                            unsigned int session_version,
                                            const char *network_type,
                                            const char *addr_type,
                                            const char *address) {
	belle_sdp_origin_t *origin = belle_sdp_origin_new();
	belle_sdp_origin_set_username(origin, user_name);
	belle_sdp_origin_set_session_id(origin, session_id);
	belle_sdp_origin_set_session_version(origin, session_version);
	belle_sdp_origin_set_network_type(origin, network_type);
	belle_sdp_origin_set_address_type(origin, addr_type);
	belle_sdp_origin_set_address(origin, address);
	return origin;
}
BELLE_SDP_PARSE(origin)
GET_SET_STRING(belle_sdp_origin, username);
GET_SET_STRING(belle_sdp_origin, address);
GET_SET_STRING(belle_sdp_origin, address_type);
GET_SET_STRING(belle_sdp_origin, network_type);
GET_SET_INT(belle_sdp_origin, session_id, unsigned int);
GET_SET_INT(belle_sdp_origin, session_version, unsigned int);

/************************
 * session_name
 ***********************/
struct _belle_sdp_session_name {
	belle_sip_object_t base;
	const char *value;
};

void belle_sdp_session_name_destroy(belle_sdp_session_name_t *session_name) {
	DESTROY_STRING(session_name, value)
}

void belle_sdp_session_name_clone(belle_sdp_session_name_t *session_name, const belle_sdp_session_name_t *orig) {
	CLONE_STRING(belle_sdp_session_name, value, session_name, orig);
}

belle_sip_error_code
belle_sdp_session_name_marshal(belle_sdp_session_name_t *session_name, char *buff, size_t buff_size, size_t *offset) {
	return belle_sip_snprintf(buff, buff_size, offset, "s=%s", session_name->value);
}

BELLE_SDP_NEW(session_name, belle_sip_object)
belle_sdp_session_name_t *belle_sdp_session_name_create(const char *name) {
	belle_sdp_session_name_t *n = belle_sdp_session_name_new();
	belle_sdp_session_name_set_value(n, name);
	return n;
}
GET_SET_STRING(belle_sdp_session_name, value);

/************************
 * session_description
 ***********************/
struct _belle_sdp_session_description {
	belle_sdp_base_description_t base_description;
	belle_sdp_version_t *version;
	belle_sip_list_t *emails;
	belle_sdp_origin_t *origin;
	belle_sdp_session_name_t *session_name;
	belle_sip_list_t *phones;
	belle_sip_list_t *times;
	belle_sdp_uri_t *uri;
	belle_sdp_uri_t *zone_adjustments;
	belle_sip_list_t *media_descriptions;
};

void belle_sdp_session_description_destroy(belle_sdp_session_description_t *session_description) {
	if (session_description->version) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->version));
	belle_sip_list_free_with_data(session_description->emails, belle_sip_object_freefunc);
	if (session_description->origin) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->origin));
	if (session_description->session_name) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->session_name));
	belle_sip_list_free_with_data(session_description->phones, belle_sip_object_freefunc);
	belle_sip_list_free_with_data(session_description->times, belle_sip_object_freefunc);
	if (session_description->uri) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->uri));
	if (session_description->zone_adjustments)
		belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->zone_adjustments));
	belle_sip_list_free_with_data(session_description->media_descriptions, belle_sip_object_freefunc);
}

void belle_sdp_session_description_clone(belle_sdp_session_description_t *session_description,
                                         const belle_sdp_session_description_t *orig) {
	if (orig->version)
		session_description->version =
		    BELLE_SDP_VERSION(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->version)));
	session_description->emails = belle_sip_list_copy_with_data(orig->emails, belle_sip_object_copyfunc);
	if (orig->origin)
		session_description->origin = BELLE_SDP_ORIGIN(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->origin)));
	if (orig->session_name)
		session_description->session_name =
		    BELLE_SDP_SESSION_NAME(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->session_name)));
	session_description->phones = belle_sip_list_copy_with_data(orig->phones, belle_sip_object_copyfunc);
	session_description->times = belle_sip_list_copy_with_data(orig->times, belle_sip_object_copyfunc);
	if (orig->uri)
		session_description->uri = BELLE_SDP_URI(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->uri)));
	if (orig->zone_adjustments)
		session_description->zone_adjustments =
		    BELLE_SDP_URI(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->zone_adjustments)));
	session_description->media_descriptions =
	    belle_sip_list_copy_with_data(orig->media_descriptions, belle_sip_object_copyfunc);
}

belle_sip_error_code belle_sdp_session_description_marshal(belle_sdp_session_description_t *session_description,
                                                           char *buff,
                                                           size_t buff_size,
                                                           size_t *offset) {
	belle_sip_error_code error = BELLE_SIP_OK;
	belle_sip_list_t *media_descriptions;
	belle_sip_list_t *times;
	belle_sip_list_t *attributes;

	if (session_description->version) {
		error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_description->version), buff, buff_size, offset);
		if (error != BELLE_SIP_OK) return error;
		error = belle_sip_snprintf(buff, buff_size, offset, "\r\n");
		if (error != BELLE_SIP_OK) return error;
	}

	if (session_description->origin) {
		error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_description->origin), buff, buff_size, offset);
		if (error != BELLE_SIP_OK) return error;
		error = belle_sip_snprintf(buff, buff_size, offset, "\r\n");
		if (error != BELLE_SIP_OK) return error;
	}

	if (session_description->session_name) {
		error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_description->session_name), buff, buff_size, offset);
		if (error != BELLE_SIP_OK) return error;
		error = belle_sip_snprintf(buff, buff_size, offset, "\r\n");
		if (error != BELLE_SIP_OK) return error;
	}

	error = belle_sdp_base_description_marshal((belle_sdp_base_description_t *)(&session_description->base_description),
	                                           buff, buff_size, offset);
	if (error != BELLE_SIP_OK) return error;

	error = belle_sip_snprintf(buff, buff_size, offset, "t=");
	if (error != BELLE_SIP_OK) return error;

	for (times = session_description->times; times != NULL; times = times->next) {
		error = belle_sip_object_marshal(BELLE_SIP_OBJECT(times->data), buff, buff_size, offset);
		if (error != BELLE_SIP_OK) return error;
		error = belle_sip_snprintf(buff, buff_size, offset, "\r\n");
		if (error != BELLE_SIP_OK) return error;
	}

	for (attributes = session_description->base_description.attributes; attributes != NULL;
	     attributes = attributes->next) {
		error = belle_sip_object_marshal(BELLE_SIP_OBJECT(attributes->data), buff, buff_size, offset);
		if (error != BELLE_SIP_OK) return error;
		error = belle_sip_snprintf(buff, buff_size, offset, "\r\n");
		if (error != BELLE_SIP_OK) return error;
	}

	for (media_descriptions = session_description->media_descriptions; media_descriptions != NULL;
	     media_descriptions = media_descriptions->next) {
		error = belle_sip_object_marshal(BELLE_SIP_OBJECT(media_descriptions->data), buff, buff_size, offset);
		if (error != BELLE_SIP_OK) return error;
	}

	return error;
}

BELLE_SDP_NEW(session_description, belle_sdp_base_description)
BELLE_SDP_PARSE(session_description)

belle_sip_list_t *
belle_sdp_session_description_get_attributes(const belle_sdp_session_description_t *session_description) {
	return belle_sdp_base_description_get_attributes(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t));
}
belle_sip_list_t *
belle_sdp_session_description_find_attributes_with_name(const belle_sdp_session_description_t *session_description,
                                                        const char *name) {
	return belle_sdp_base_description_find_attributes_with_name(
	    BELLE_SIP_CAST(session_description, belle_sdp_base_description_t), name);
}

const char *
belle_sdp_session_description_get_attribute_value(const belle_sdp_session_description_t *session_description,
                                                  const char *name) {
	return belle_sdp_base_description_get_attribute_value(
	    BELLE_SIP_CAST(session_description, belle_sdp_base_description_t), name);
}
const belle_sdp_attribute_t *
belle_sdp_session_description_get_attribute(const belle_sdp_session_description_t *session_description,
                                            const char *name) {
	return belle_sdp_base_description_get_attribute(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t),
	                                                name);
}
int belle_sdp_session_description_get_bandwidth(const belle_sdp_session_description_t *session_description,
                                                const char *name) {
	return belle_sdp_base_description_get_bandwidth_value(
	    BELLE_SIP_CAST(session_description, belle_sdp_base_description_t), name);
}
belle_sip_list_t *
belle_sdp_session_description_get_bandwidths(const belle_sdp_session_description_t *session_description) {
	return BELLE_SIP_CAST(session_description, belle_sdp_base_description_t)->bandwidths;
}
belle_sdp_connection_t *
belle_sdp_session_description_get_connection(const belle_sdp_session_description_t *session_description) {
	return BELLE_SIP_CAST(session_description, belle_sdp_base_description_t)->connection;
}
belle_sip_list_t *belle_sdp_session_description_get_emails(const belle_sdp_session_description_t *session_description) {
	return session_description->emails;
}
belle_sdp_info_t *belle_sdp_session_description_get_info(const belle_sdp_session_description_t *session_description) {
	return BELLE_SIP_CAST(session_description, belle_sdp_base_description_t)->info;
}
belle_sip_list_t *
belle_sdp_session_description_get_media_descriptions(const belle_sdp_session_description_t *session_description) {
	return session_description->media_descriptions;
}
belle_sdp_origin_t *
belle_sdp_session_description_get_origin(const belle_sdp_session_description_t *session_description) {
	return session_description->origin;
}
belle_sip_list_t *belle_sdp_session_description_get_phones(const belle_sdp_session_description_t *session_description) {
	return session_description->phones;
}
belle_sdp_session_name_t *
belle_sdp_session_description_get_session_name(const belle_sdp_session_description_t *session_description) {
	return session_description->session_name;
}
belle_sip_list_t *
belle_sdp_session_description_get_time_descriptions(const belle_sdp_session_description_t *session_description) {
	return session_description->times;
}
belle_sdp_uri_t *belle_sdp_session_description_get_uri(const belle_sdp_session_description_t *session_description) {
	return session_description->uri;
}
belle_sdp_version_t *
belle_sdp_session_description_get_version(const belle_sdp_session_description_t *session_description) {
	return session_description->version;
}
belle_sdp_uri_t *
belle_sdp_session_description_get_zone_adjustments(const belle_sdp_session_description_t *session_description) {
	return session_description->zone_adjustments;
}
void belle_sdp_session_description_remove_attribute(belle_sdp_session_description_t *session_description,
                                                    const char *name) {
	belle_sdp_base_description_remove_attribute(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t),
	                                            name);
}
void belle_sdp_session_description_remove_bandwidth(belle_sdp_session_description_t *session_description,
                                                    const char *name) {
	belle_sdp_base_description_remove_bandwidth(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t),
	                                            name);
}
void belle_sdp_session_description_set_attribute_value(belle_sdp_session_description_t *session_description,
                                                       const char *name,
                                                       const char *value) {
	belle_sdp_base_description_set_attribute_value(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t),
	                                               name, value);
}
void belle_sdp_session_description_set_attributes(belle_sdp_session_description_t *session_description,
                                                  belle_sip_list_t *attributes) {
	belle_sdp_base_description_set_attributes(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t),
	                                          attributes);
}
void belle_sdp_session_description_add_attribute(belle_sdp_session_description_t *session_description,
                                                 belle_sdp_attribute_t *attribute) {
	belle_sdp_base_description_add_attribute(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t),
	                                         attribute);
}
void belle_sdp_session_description_set_bandwidth(belle_sdp_session_description_t *session_description,
                                                 const char *type,
                                                 int value) {
	belle_sdp_base_description_set_bandwidth(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t), type,
	                                         value);
}
void belle_sdp_session_description_set_bandwidths(belle_sdp_session_description_t *session_description,
                                                  belle_sip_list_t *bandwidths) {
	belle_sdp_base_description_set_bandwidths(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t),
	                                          bandwidths);
}
void belle_sdp_session_description_add_bandwidth(belle_sdp_session_description_t *session_description,
                                                 belle_sdp_bandwidth_t *bandwidth) {
	belle_sdp_base_description_add_bandwidth(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t),
	                                         bandwidth);
}
void belle_sdp_session_description_set_connection(belle_sdp_session_description_t *session_description,
                                                  belle_sdp_connection_t *connection) {
	SET_OBJECT(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t), connection, belle_sdp_connection_t)
}
void belle_sdp_session_description_set_emails(belle_sdp_session_description_t *session_description,
                                              belle_sip_list_t *emails) {
	SET_LIST(session_description->emails, emails)
}
void belle_sdp_session_description_set_info(belle_sdp_session_description_t *session_description,
                                            belle_sdp_info_t *info) {
	SET_OBJECT(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t), info, belle_sdp_info_t)
}
void belle_sdp_session_description_set_media_descriptions(belle_sdp_session_description_t *session_description,
                                                          belle_sip_list_t *media_descriptions) {
	SET_LIST(session_description->media_descriptions, media_descriptions)
}
void belle_sdp_session_description_add_media_description(belle_sdp_session_description_t *session_description,
                                                         belle_sdp_media_description_t *media_description) {
	session_description->media_descriptions =
	    belle_sip_list_append(session_description->media_descriptions, belle_sip_object_ref(media_description));
}

void belle_sdp_session_description_set_origin(belle_sdp_session_description_t *session_description,
                                              belle_sdp_origin_t *origin) {
	SET_OBJECT(session_description, origin, belle_sdp_origin_t)
}
void belle_sdp_session_description_set_phones(belle_sdp_session_description_t *session_description,
                                              belle_sip_list_t *phones) {
	SET_LIST(session_description->phones, phones)
}
void belle_sdp_session_description_set_session_name(belle_sdp_session_description_t *session_description,
                                                    belle_sdp_session_name_t *session_name) {
	SET_OBJECT(session_description, session_name, belle_sdp_session_name_t)
}
void belle_sdp_session_description_set_time_descriptions(belle_sdp_session_description_t *session_description,
                                                         belle_sip_list_t *times) {
	SET_LIST(session_description->times, times)
}
void belle_sdp_session_description_set_time_description(belle_sdp_session_description_t *session_description,
                                                        belle_sdp_time_description_t *time_desc) {
	belle_sdp_session_description_set_time_descriptions(session_description, belle_sip_list_new(time_desc));
}
void belle_sdp_session_description_set_uri(belle_sdp_session_description_t *session_description, belle_sdp_uri_t *uri) {
	SET_OBJECT(session_description, uri, belle_sdp_uri_t)
}
void belle_sdp_session_description_set_version(belle_sdp_session_description_t *session_description,
                                               belle_sdp_version_t *version) {
	SET_OBJECT(session_description, version, belle_sdp_version_t)
}
void belle_sdp_session_description_set_zone_adjustments(belle_sdp_session_description_t *session_description,
                                                        belle_sdp_uri_t *zone_adjustments) {
	SET_OBJECT(session_description, zone_adjustments, belle_sdp_uri_t)
}

void belle_sdp_session_description_add_attribute_holder(belle_sdp_session_description_t *session_description,
                                                        belle_sdp_attribute_holder_t *holder) {
	belle_sdp_base_description_add_attribute(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t),
	                                         holder->attribute);
	belle_sip_free(holder);
}

/************************
 * time
 ***********************/
struct _belle_sdp_time {
	belle_sip_object_t base;
	long long start;
	long long stop;
};

void belle_sdp_time_destroy(belle_sdp_time_t *time) {
}

void belle_sdp_time_clone(belle_sdp_time_t *time, const belle_sdp_time_t *orig) {
	time->start = orig->start;
	time->stop = orig->stop;
}

belle_sip_error_code belle_sdp_time_marshal(belle_sdp_time_t *time, char *buff, size_t buff_size, size_t *offset) {
	return belle_sip_snprintf(buff, buff_size, offset, "%lld %lld", time->start, time->stop);
}

BELLE_SDP_NEW(time, belle_sip_object)
GET_SET_INT(belle_sdp_time, start, long long);
GET_SET_INT(belle_sdp_time, stop, long long);

/************************
 * time description
 ***********************/
struct _belle_sdp_time_description {
	belle_sip_object_t base;
	belle_sdp_time_t *time;
};

void belle_sdp_time_description_destroy(belle_sdp_time_description_t *time_description) {
	if (time_description->time) belle_sip_object_unref(BELLE_SIP_OBJECT(time_description->time));
}

void belle_sdp_time_description_clone(belle_sdp_time_description_t *time_description,
                                      const belle_sdp_time_description_t *orig) {
	if (orig->time)
		time_description->time = BELLE_SDP_TIME(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->time)));
}

belle_sip_error_code belle_sdp_time_description_marshal(belle_sdp_time_description_t *time_description,
                                                        char *buff,
                                                        size_t buff_size,
                                                        size_t *offset) {
	return belle_sip_object_marshal(BELLE_SIP_OBJECT(time_description->time), buff, buff_size, offset);
}

BELLE_SDP_NEW(time_description, belle_sip_object)

belle_sdp_time_description_t *belle_sdp_time_description_create(long long start, long long stop) {
	belle_sdp_time_description_t *time_desc = belle_sdp_time_description_new();
	belle_sdp_time_t *time = belle_sdp_time_new();
	belle_sdp_time_set_start(time, start);
	belle_sdp_time_set_stop(time, stop);
	belle_sdp_time_description_set_time(time_desc, time);
	return time_desc;
}
belle_sip_list_t *belle_sdp_time_description_get_repeate_times(const belle_sdp_time_description_t *time_description) {
	return NULL;
}
belle_sdp_time_t *belle_sdp_time_description_get_time(const belle_sdp_time_description_t *time_description) {
	return time_description->time;
}
void belle_sdp_time_description_set_repeate_times(belle_sdp_time_description_t *time_description,
                                                  belle_sip_list_t *times) {
	belle_sip_error("time description repeat time not implemented");
}
void belle_sdp_time_description_set_time(belle_sdp_time_description_t *time_description, belle_sdp_time_t *time) {
	SET_OBJECT(time_description, time, belle_sdp_time_t)
}

/************************
 * version
 ***********************/
struct _belle_sdp_version {
	belle_sip_object_t base;
	int version;
};

void belle_sdp_version_destroy(belle_sdp_version_t *version) {
}

void belle_sdp_version_clone(belle_sdp_version_t *version, const belle_sdp_version_t *orig) {
	version->version = orig->version;
}

belle_sip_error_code
belle_sdp_version_marshal(belle_sdp_version_t *version, char *buff, size_t buff_size, size_t *offset) {
	return belle_sip_snprintf(buff, buff_size, offset, "v=%i", version->version);
}

BELLE_SDP_NEW(version, belle_sip_object)
belle_sdp_version_t *belle_sdp_version_create(int version) {
	belle_sdp_version_t *v = belle_sdp_version_new();
	belle_sdp_version_set_version(v, version);
	return v;
}
GET_SET_INT(belle_sdp_version, version, int);

/***************************************************************************************
 * mime_parameter
 *
 **************************************************************************************/

static void belle_sdp_mime_parameter_destroy(belle_sdp_mime_parameter_t *mime_parameter) {
	if (mime_parameter->type) belle_sip_free((void *)mime_parameter->type);
	if (mime_parameter->parameters) belle_sip_free((void *)mime_parameter->parameters);
}
static void belle_sdp_mime_parameter_clone(belle_sdp_mime_parameter_t *mime_parameter,
                                           belle_sdp_mime_parameter_t *orig) {
	mime_parameter->rate = orig->rate;
	mime_parameter->channel_count = orig->channel_count;
	mime_parameter->ptime = orig->ptime;
	mime_parameter->max_ptime = orig->max_ptime;
	mime_parameter->media_format = orig->media_format;
	CLONE_STRING(belle_sdp_mime_parameter, type, mime_parameter, orig);
	CLONE_STRING(belle_sdp_mime_parameter, parameters, mime_parameter, orig);
}
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sdp_mime_parameter_t);
BELLE_SIP_INSTANCIATE_VPTR(belle_sdp_mime_parameter_t,
                           belle_sip_object_t,
                           belle_sdp_mime_parameter_destroy,
                           belle_sdp_mime_parameter_clone,
                           NULL,
                           TRUE);

belle_sdp_mime_parameter_t *belle_sdp_mime_parameter_new() {
	belle_sdp_mime_parameter_t *l_param = belle_sip_object_new(belle_sdp_mime_parameter_t);
	l_param->ptime = -1;
	l_param->max_ptime = -1;
	return l_param;
}
belle_sdp_mime_parameter_t *
belle_sdp_mime_parameter_create(const char *type, int media_format, int rate, int channel_count) {
	belle_sdp_mime_parameter_t *mime_param = belle_sdp_mime_parameter_new();
	belle_sdp_mime_parameter_set_type(mime_param, type);
	belle_sdp_mime_parameter_set_media_format(mime_param, media_format);
	belle_sdp_mime_parameter_set_rate(mime_param, rate);
	belle_sdp_mime_parameter_set_channel_count(mime_param, channel_count);
	return mime_param;
}
GET_SET_INT(belle_sdp_mime_parameter, rate, int);
GET_SET_INT(belle_sdp_mime_parameter, channel_count, int);
GET_SET_INT(belle_sdp_mime_parameter, ptime, int);
GET_SET_INT(belle_sdp_mime_parameter, max_ptime, int);
GET_SET_INT(belle_sdp_mime_parameter, media_format, int);
GET_SET_STRING(belle_sdp_mime_parameter, type);
GET_SET_STRING(belle_sdp_mime_parameter, parameters);
