/*
 * OpenMRCP - Open Source Media Resource Control Protocol Stack
 * Copyright (C) 2007, Cepstral LLC
 *
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Author(s):
 * Arsen Chaloyan <achaloyan@yahoo.com>
 *
 * Contributor(s):
 *
 */

#include <apr_general.h>
#include <sofia-sip/sdp.h>

#include "mrcp_description.h"
#include "apt_text_stream.h"
#include "rtsp_message.h"

#define MRCP_CLIENT_SDP_ORIGIN "OpenMRCPClient"
#define MRCP_SERVER_SDP_ORIGIN "OpenMRCPServer"

/* generates sdp_media_audio (sdp string) by mrcp_media_audio */
static size_t mrcp_sdp_media_audio_generate(char *buffer, size_t size, mrcp_media_audio_t *audio_media)
{
	size_t i;
	size_t offset = 0;
	offset += sprintf(buffer+offset,"m=audio %d RTP/AVP", 
		audio_media->base.state == MRCP_MEDIA_ENABLED ? audio_media->base.port : 0);
	for(i=0; i<audio_media->codec_count; i++) {
		offset += sprintf(buffer+offset," %d", audio_media->codec_list[i]->payload_type);
	}
	offset += sprintf(buffer+offset,"\r\n");
	if(audio_media->base.state == MRCP_MEDIA_ENABLED) {
		for(i=0; i<audio_media->codec_count; i++) {
			offset += sprintf(buffer+offset,"a=rtpmap:%d %s/%d\r\n",
				audio_media->codec_list[i]->payload_type,
				audio_media->codec_list[i]->name,
				audio_media->codec_list[i]->sampling_rate);
		}
		offset += sprintf(buffer+offset,"a=%s\r\n",
			mrcp_audio_direction_str_get(audio_media->direction));
		if(audio_media->ptime) {
			offset += sprintf(buffer+offset,"a=ptime:%hu\r\n",
				audio_media->ptime);
		}
	}
	return offset;
}

/* generates mrcp_media_audio by sdp_media_audio */
static mrcp_status_t mrcp_audio_media_generate_by_sdp_media(mrcp_media_audio_t *audio_media, sdp_media_t *sdp_media, apr_pool_t *pool)
{
	sdp_rtpmap_t *map;
	mrcp_audio_attrib_id id;
	sdp_attribute_t *attrib;
	codec_descriptor_t *codec;

	audio_media->codec_count = 0;
	for(map = sdp_media->m_rtpmaps; map; map = map->rm_next) {
		codec = apr_palloc(pool,sizeof(codec_descriptor_t));
		codec_descriptor_init(codec);
		codec->payload_type = (apr_byte_t)map->rm_pt;
		codec->name = apr_pstrdup(pool,map->rm_encoding);
		codec->sampling_rate = (apr_uint16_t)map->rm_rate;
		codec->channel_count = 1;
		if(audio_media->codec_count < MRCP_MAX_CODEC_COUNT) {
			audio_media->codec_list[audio_media->codec_count++] = codec;
		}
	}

	switch(sdp_media->m_mode) {
		case sdp_inactive:
			audio_media->direction = MRCP_AUDIO_NONE;
			break;
		case sdp_sendonly:
			audio_media->direction = MRCP_AUDIO_SEND;
			break;
		case sdp_recvonly:
			audio_media->direction = MRCP_AUDIO_RECEIVE;
			break;
		case sdp_sendrecv:
			audio_media->direction = MRCP_AUDIO_DUPLEX;
			break;
	}

	for(attrib = sdp_media->m_attributes; attrib; attrib=attrib->a_next) {
		id = mrcp_audio_attrib_id_find(attrib->a_name);
		switch(id) {
			case MRCP_AUDIO_ATTRIB_PTIME:
				audio_media->ptime = (unsigned short)atoi(attrib->a_value);
				break;
			default:
				break;
		}
	}

	return MRCP_STATUS_SUCCESS;
}

/* generates mrcp_media_base by sdp_media */
static mrcp_status_t mrcp_media_generate_by_sdp_media(mrcp_media_base_t *mrcp_media, sdp_media_t *sdp_media, const char *ip, apr_pool_t *pool)
{
	if(sdp_media->m_connections) {
		mrcp_media->ip = apr_pstrdup(pool,sdp_media->m_connections->c_address);
	}
	else {
		mrcp_media->ip = ip;
	}
	if(sdp_media->m_port) {
		mrcp_media->port = (unsigned short)sdp_media->m_port;
		mrcp_media->state = MRCP_MEDIA_ENABLED;
	}
	else {
		mrcp_media->state = MRCP_MEDIA_DISABLED;
	}
	return MRCP_STATUS_SUCCESS;
}


/* generates rtsp request by mrcp_descriptor */
size_t rtsp_request_generate_by_mrcp_descriptor(rtsp_message_t *message, mrcp_descriptor_t *local_descriptor, mrcp_descriptor_t *remote_descriptor)
{
	size_t i;
	size_t offset = 0;
	mrcp_media_control_t *control_media;

	/* find the first mismatched control media */
	control_media = NULL;
	for(i=0; i<local_descriptor->media_count; i++) {
		if(!local_descriptor->media[i] || !remote_descriptor->media[i]) {
			continue;
		}
		if(local_descriptor->media[i]->type == MRCP_MEDIA_TYPE_CONTROL &&
			remote_descriptor->media[i]->type == MRCP_MEDIA_TYPE_CONTROL) {
			if(local_descriptor->media[i]->state != remote_descriptor->media[i]->state) {
				control_media = (mrcp_media_control_t*)local_descriptor->media[i];
				break;
			}
		}
	}

	if(!control_media) {
		return 0;
	}

	message->start_line.common.request_line.resource_name = control_media->resource_name;
	if(control_media->base.state == MRCP_MEDIA_ENABLED) {
		mrcp_media_audio_t *audio_media;
		char buffer[1500];
		/*setup*/
		message->start_line.common.request_line.method_id = RTSP_METHOD_SETUP;

		message->header.transport.profile = RTSP_PROFILE_RTP_AVP;
		message->header.transport.delivery = RTSP_DELIVERY_UNICAST;
		rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_TRANSPORT);

		offset += sprintf(buffer+offset,
				"v=0\r\n"
				"o=%s 0 0 IN IP4 %s\r\n"
				"s=-\r\n"
				"t=0 0\r\n",
				MRCP_CLIENT_SDP_ORIGIN,
				local_descriptor->ip);

		for(i=0; i<local_descriptor->media_count; i++) {
			if(!local_descriptor->media[i]) {
				continue;
			}
			switch(local_descriptor->media[i]->type) {
				case MRCP_MEDIA_TYPE_AUDIO:
					audio_media = (mrcp_media_audio_t*)local_descriptor->media[i];
					offset += sprintf(buffer+offset,"c=IN IP4 %s\r\n",
						audio_media->base.ip ? audio_media->base.ip : local_descriptor->ip);
					offset += mrcp_sdp_media_audio_generate(buffer+offset,sizeof(buffer)-offset,audio_media);
					message->header.transport.client_port_range.min = audio_media->base.port;
					message->header.transport.client_port_range.max = audio_media->base.port+1;
					break;
			}
		}
		if(offset) {
			message->body = apr_pstrdup(message->pool,buffer);
			message->header.content_type = RTSP_CONTENT_TYPE_SDP;
			rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE);
			message->header.content_length = offset;
			rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH);
		}
	}
	else {
		/*teardown*/
		message->start_line.common.request_line.method_id = RTSP_METHOD_TEARDOWN;
	}

	return offset;
}

/* generates rtsp response by mrcp_descriptor */
size_t rtsp_response_generate_by_mrcp_descriptor(rtsp_message_t *message, mrcp_descriptor_t *descriptor, const char *resource_name)
{
	size_t i;
	size_t offset = 0;
	mrcp_media_control_t *control_media;
	mrcp_media_audio_t *audio_media;
	char buffer[1500];

	offset += sprintf(buffer+offset,
			"v=0\r\n"
			"o=%s 0 0 IN IP4 %s\r\n"
			"s=-\r\n"
			"c=IN IP4 %s\r\n"
			"t=0 0\r\n",
			MRCP_SERVER_SDP_ORIGIN,
			descriptor->ip,
			descriptor->ip);
	control_media = NULL;
	for(i=0;i<descriptor->media_count; i++) {
		if(!descriptor->media[i]) {
			continue;
		}
		switch(descriptor->media[i]->type) {
			case MRCP_MEDIA_TYPE_CONTROL:
				if(!control_media) {
					control_media = (mrcp_media_control_t*)descriptor->media[i];
					if(apt_str_compare(control_media->resource_name,resource_name) != TRUE) {
						control_media = NULL;
					}
				}
				break;
			case MRCP_MEDIA_TYPE_AUDIO:
				audio_media = (mrcp_media_audio_t*)descriptor->media[i];
				offset += mrcp_sdp_media_audio_generate(buffer+offset,sizeof(buffer)-offset,audio_media);
				message->header.transport.server_port_range.min = audio_media->base.port;
				message->header.transport.server_port_range.max = audio_media->base.port+1;
				break;
		}
	}

	if(control_media && control_media->base.state == MRCP_MEDIA_ENABLED) {
		/* ok */
		message->header.transport.profile = RTSP_PROFILE_RTP_AVP;
		message->header.transport.delivery = RTSP_DELIVERY_UNICAST;
		rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_TRANSPORT);
	}
	else {
		/* not found */
		message->start_line.common.status_line.status_code = RTSP_STATUS_CODE_NOT_FOUND;
		message->start_line.common.status_line.reason = RTSP_REASON_PHRASE_NOT_FOUND;
		offset = 0;
	}

	if(offset) {
		message->body = apr_pstrdup(message->pool,buffer);
		message->header.content_type = RTSP_CONTENT_TYPE_SDP;
		rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE);
		message->header.content_length = offset;
		rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH);
	}
	return offset;
}

/* generates mrcp_descriptor by rtsp response */
mrcp_status_t mrcp_descriptor_generate_by_rtsp_response(mrcp_descriptor_t *descriptor, rtsp_message_t *response, rtsp_message_t *request, apr_pool_t *pool, su_home_t *home)
{
	size_t i=0;
	mrcp_media_base_t *mrcp_media;
	mrcp_media_control_t *control_media;
	const char *resource_name;
	if(!request || !request->start_line.common.request_line.resource_name) {
		return MRCP_STATUS_FAILURE;
	}

	resource_name = request->start_line.common.request_line.resource_name;
	for(i=0; i<descriptor->media_count; i++) {
		if(descriptor->media[i] && descriptor->media[i]->type == MRCP_MEDIA_TYPE_CONTROL) {
			control_media = (mrcp_media_control_t *)descriptor->media[i];
			if(control_media->resource_name) {
				if(apt_str_compare(control_media->resource_name,resource_name) == TRUE) {
					/*update*/
					if(request->start_line.common.request_line.method_id == RTSP_METHOD_SETUP && 
						response->start_line.common.status_line.status_code == RTSP_STATUS_CODE_OK) {
						control_media->base.state = MRCP_MEDIA_ENABLED;
					}
					else {
						control_media->base.state = MRCP_MEDIA_DISABLED;
					}
					break;
				}
			}
			else {
				/*new*/
				control_media->resource_name = resource_name;
				if(response->header.session_id) {
					control_media->session_id.hex_str = response->header.session_id;
					control_media->session_id.length = strlen(response->header.session_id);
				}
				if(request->start_line.common.request_line.method_id == RTSP_METHOD_SETUP && 
					response->start_line.common.status_line.status_code == RTSP_STATUS_CODE_OK) {
					control_media->base.state = MRCP_MEDIA_ENABLED;
				}
				else {
					control_media->base.state = MRCP_MEDIA_DISABLED;
				}
				break;
			}
		}
	}

	if(rtsp_header_property_check(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE) == MRCP_STATUS_SUCCESS &&
		rtsp_header_property_check(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH) == MRCP_STATUS_SUCCESS &&
		response->body) {
		
		sdp_parser_t *parser;
		sdp_session_t *sdp;
		sdp_media_t *sdp_media;

		parser = sdp_parse(home,response->body,(int)strlen(response->body),0);
		sdp = sdp_session(parser);

		if(sdp->sdp_connection) {
			descriptor->ip = apr_pstrdup(pool,sdp->sdp_connection->c_address);
		}
		/* updated media */
		sdp_media=sdp->sdp_media;
		for(i=0; i<descriptor->media_count && sdp_media; i++) {
			mrcp_media = descriptor->media[i];
			if(mrcp_media && mrcp_media->type == MRCP_MEDIA_TYPE_AUDIO) {
				mrcp_media_audio_t *audio_media = (mrcp_media_audio_t*)mrcp_media;
				mrcp_audio_media_generate_by_sdp_media(audio_media,sdp_media,pool);
				mrcp_media_generate_by_sdp_media(&audio_media->base,sdp_media,descriptor->ip,pool);
				
				sdp_media=sdp_media->m_next;
			}
		}

		sdp_parser_free(parser);
	}
	return MRCP_STATUS_SUCCESS;
}

/* generates mrcp_descriptor by rtsp request */
mrcp_status_t mrcp_descriptor_generate_by_rtsp_request(mrcp_descriptor_t *descriptor, rtsp_message_t *request, apr_pool_t *pool, su_home_t *home)
{
	size_t i=0;
	mrcp_media_base_t *mrcp_media;
	mrcp_media_control_t *control_media;
	const char *resource_name = request->start_line.common.request_line.resource_name;
	if(!resource_name) {
		return MRCP_STATUS_FAILURE;
	}

	control_media = mrcp_control_media_get(descriptor,resource_name);
	if(!control_media) {
		/* create new control media */
		control_media = mrcp_control_media_create(pool);
		mrcp_descriptor_media_add(descriptor,&control_media->base);

		control_media->resource_name = apr_pstrdup(pool,resource_name);
		control_media->base.state = MRCP_MEDIA_ENABLED;
	}
	
	if(rtsp_header_property_check(&request->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE) == MRCP_STATUS_SUCCESS &&
		rtsp_header_property_check(&request->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH) == MRCP_STATUS_SUCCESS &&
		request->body) {
		
		sdp_parser_t *parser;
		sdp_session_t *sdp;
		sdp_media_t *sdp_media;

		parser = sdp_parse(home,request->body,(int)strlen(request->body),0);
		sdp = sdp_session(parser);

		if(sdp->sdp_connection) {
			descriptor->ip = apr_pstrdup(pool,sdp->sdp_connection->c_address);
		}
		/* updated media */
		sdp_media=sdp->sdp_media;
		for(i=0; i<descriptor->media_count && sdp_media; i++) {
			mrcp_media = descriptor->media[i];
			if(mrcp_media && mrcp_media->type == MRCP_MEDIA_TYPE_AUDIO) {
				mrcp_media_audio_t *audio_media = (mrcp_media_audio_t*)mrcp_media;
				mrcp_audio_media_generate_by_sdp_media(audio_media,sdp_media,pool);
				mrcp_media_generate_by_sdp_media(&audio_media->base,sdp_media,descriptor->ip,pool);
				
				sdp_media=sdp_media->m_next;
			}
		}
		
		/* new media */
		for(; sdp_media; sdp_media=sdp_media->m_next) {
			switch(sdp_media->m_type) {
				case sdp_media_audio:
				{
					mrcp_media_audio_t *audio_media = mrcp_audio_media_create(pool);
					mrcp_descriptor_media_add(descriptor,&audio_media->base);
					mrcp_audio_media_generate_by_sdp_media(audio_media,sdp_media,pool);
					mrcp_media_generate_by_sdp_media(&audio_media->base,sdp_media,descriptor->ip,pool);
					break;
				}
				default:
					apt_log(APT_PRIO_INFO,"Not Supported SDP Media [%s]\n", sdp_media->m_type_name);
			}
		}

		sdp_parser_free(parser);
	}
	return MRCP_STATUS_SUCCESS;
}

/* generates resource discover answer (sdp string) by mrcp_descriptior */
size_t rtsp_resource_discover_sdp_generate(rtsp_message_t *message, mrcp_descriptor_t *descriptor)
{
	size_t i;
	size_t offset = 0;
	mrcp_status_t initiated = MRCP_STATUS_FAILURE;
	mrcp_media_control_t *control_media = NULL;
	mrcp_media_audio_t *audio_media = NULL;
	char buffer[1500];
	offset += sprintf(buffer+offset,
			"v=0\r\n"
			"o=%s 0 0 IN IP4 %s\r\n"
			"s=-\r\n"
			"c=IN IP4 %s\r\n"
			"t=0 0\r\n",
			MRCP_SERVER_SDP_ORIGIN,
			descriptor->ip,
			descriptor->ip);

	for(i=0;i<descriptor->media_count; i++) {
		if(!descriptor->media[i]) {
			continue;
		}
		if(descriptor->media[i]->type == MRCP_MEDIA_TYPE_CONTROL) {
			control_media = (mrcp_media_control_t*)descriptor->media[i];
			if(initiated == MRCP_STATUS_FAILURE) {
				initiated = MRCP_STATUS_SUCCESS;
				offset += sprintf(buffer+offset,
					"m=application 0 %s 1\r\n",
					mrcp_proto_get(control_media->proto));
			}
			offset += sprintf(buffer+offset,
				"a=resource:%s\r\n",
				control_media->resource_name);
		}
		else if(descriptor->media[i]->type == MRCP_MEDIA_TYPE_AUDIO) {
			audio_media = (mrcp_media_audio_t*)descriptor->media[i];
		}
	}

	if(audio_media) {
		offset += sprintf(buffer+offset,"m=audio 0 RTP/AVP 0 8\r\n");
	}

	if(offset) {
		message->body = apr_pstrdup(message->pool,buffer);
		message->header.content_type = RTSP_CONTENT_TYPE_SDP;
		rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE);
		message->header.content_length = offset;
		rtsp_header_property_add(&message->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH);
	}
	return offset;
}
