/*
 * Copyright (c) 2010-2022 Belledonne Communications SARL.
 *
 * This file is part of oRTP
 * (see https://gitlab.linphone.org/BC/public/ortp).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
#include "ortp-config.h"
#endif

#include "ortp/logging.h"
#include "ortp/payloadtype.h"
#include "ortp/str_utils.h"
#include <bctoolbox/port.h>

char *payload_type_get_rtpmap(PayloadType *pt) {
	int len = (int)strlen(pt->mime_type) + 15;
	char *rtpmap = (char *)ortp_malloc(len);
	if (pt->channels > 0) snprintf(rtpmap, len, "%s/%i/%i", pt->mime_type, pt->clock_rate, pt->channels);
	else snprintf(rtpmap, len, "%s/%i", pt->mime_type, pt->clock_rate);
	return rtpmap;
}

PayloadType *payload_type_new() {
	PayloadType *newpayload = (PayloadType *)ortp_new0(PayloadType, 1);
	newpayload->flags |= PAYLOAD_TYPE_ALLOCATED;
	return newpayload;
}

PayloadType *payload_type_clone(const PayloadType *payload) {
	PayloadType *newpayload = (PayloadType *)ortp_new0(PayloadType, 1);
	memcpy(newpayload, payload, sizeof(PayloadType));
	newpayload->mime_type = ortp_strdup(payload->mime_type);
	if (payload->recv_fmtp != NULL) {
		newpayload->recv_fmtp = ortp_strdup(payload->recv_fmtp);
	}
	if (payload->send_fmtp != NULL) {
		newpayload->send_fmtp = ortp_strdup(payload->send_fmtp);
	}
	newpayload->flags |= PAYLOAD_TYPE_ALLOCATED;
	return newpayload;
}

static bool_t canWrite(PayloadType *pt) {
	if (!(pt->flags & PAYLOAD_TYPE_ALLOCATED)) {
		ortp_error("Cannot change parameters of statically defined payload types: make your"
		           " own copy using payload_type_clone() first.");
		return FALSE;
	}
	return TRUE;
}

/**
 * Sets a recv parameters (fmtp) for the PayloadType.
 * This method is provided for applications using RTP with SDP, but
 * actually the ftmp information is not used for RTP processing.
 **/
void payload_type_set_recv_fmtp(PayloadType *pt, const char *fmtp) {
	if (canWrite(pt)) {
		if (pt->recv_fmtp != NULL) ortp_free(pt->recv_fmtp);
		if (fmtp != NULL) pt->recv_fmtp = ortp_strdup(fmtp);
		else pt->recv_fmtp = NULL;
	}
}

/**
 * Sets a send parameters (fmtp) for the PayloadType.
 * This method is provided for applications using RTP with SDP, but
 * actually the ftmp information is not used for RTP processing.
 **/
void payload_type_set_send_fmtp(PayloadType *pt, const char *fmtp) {
	if (canWrite(pt)) {
		if (pt->send_fmtp != NULL) ortp_free(pt->send_fmtp);
		if (fmtp != NULL) pt->send_fmtp = ortp_strdup(fmtp);
		else pt->send_fmtp = NULL;
	}
}

void payload_type_append_recv_fmtp(PayloadType *pt, const char *fmtp) {
	if (canWrite(pt)) {
		if (pt->recv_fmtp == NULL) pt->recv_fmtp = ortp_strdup(fmtp);
		else {
			char *tmp = ortp_strdup_printf("%s;%s", pt->recv_fmtp, fmtp);
			ortp_free(pt->recv_fmtp);
			pt->recv_fmtp = tmp;
		}
	}
}

void payload_type_append_send_fmtp(PayloadType *pt, const char *fmtp) {
	if (canWrite(pt)) {
		if (pt->send_fmtp == NULL) pt->send_fmtp = ortp_strdup(fmtp);
		else {
			char *tmp = ortp_strdup_printf("%s;%s", pt->send_fmtp, fmtp);
			ortp_free(pt->send_fmtp);
			pt->send_fmtp = tmp;
		}
	}
}

void payload_type_set_avpf_params(PayloadType *pt, PayloadTypeAvpfParams params) {
	if (canWrite(pt)) {
		memcpy(&pt->avpf, &params, sizeof(pt->avpf));
	}
}

bool_t payload_type_is_vbr(const PayloadType *pt) {
	if (pt->type == PAYLOAD_VIDEO) return TRUE;
	return !!(pt->flags & PAYLOAD_TYPE_IS_VBR);
}

/**
 * Frees a PayloadType.
 **/
void payload_type_destroy(PayloadType *pt) {
	if (pt->mime_type) ortp_free(pt->mime_type);
	if (pt->recv_fmtp) ortp_free(pt->recv_fmtp);
	if (pt->send_fmtp) ortp_free(pt->send_fmtp);
	ortp_free(pt);
}

static const char *find_param_occurence_of(const char *fmtp, const char *param) {
	const char *pos = fmtp;
	int param_len = (int)strlen(param);
	do {
		pos = strstr(pos, param);
		if (pos) {
			/*check that the occurence found is not a subword of a parameter name*/
			if (pos == fmtp) {
				if (pos[param_len] == '=') break; /* found it */
			} else if ((pos[-1] == ';' || pos[-1] == ' ') && pos[param_len] == '=') {
				break; /* found it */
			}
			pos += strlen(param);
		}
	} while (pos != NULL);
	return pos;
}

static const char *find_last_param_occurence_of(const char *fmtp, const char *param) {
	const char *pos = fmtp;
	const char *lastpos = NULL;
	do {
		pos = find_param_occurence_of(pos, param);
		if (pos) {
			lastpos = pos;
			pos += strlen(param);
		}
	} while (pos != NULL);
	return lastpos;
}
/**
 * Parses a fmtp string such as "profile=0;level=10", finds the value matching
 * parameter param_name, and writes it into result.
 * If a parameter name is found multiple times, only the value of the last occurence is returned.
 * Despite fmtp strings are not used anywhere within oRTP, this function can
 * be useful for people using RTP streams described from SDP.
 * @param fmtp the fmtp line (format parameters)
 * @param param_name the parameter to search for
 * @param result the value given for the parameter (if found)
 * @param result_len the size allocated to hold the result string
 * @return TRUE if the parameter was found, else FALSE.
 **/
bool_t fmtp_get_value(const char *fmtp, const char *param_name, char *result, size_t result_len) {
	const char *pos = find_last_param_occurence_of(fmtp, param_name);
	memset(result, '\0', result_len);
	if (pos) {
		const char *equal = strchr(pos, '=');
		if (equal) {
			int copied;
			const char *end = strchr(equal + 1, ';');
			if (end == NULL) end = fmtp + strlen(fmtp); /*assuming this is the last param */
			copied = MIN((int)(result_len - 1), (int)(end - (equal + 1)));
			strncpy(result, equal + 1, copied);
			result[copied] = '\0';
			return TRUE;
		}
	}
	return FALSE;
}
