/*
 * 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 "rtsp_start_line.h"
#include "apt_string_table.h"
#include "apt_log.h"

/** Protocol name used in version string */
#define RTSP_NAME "RTSP"

/** Separators used in mrcp version string parse/generate */
#define RTSP_NAME_VERSION_SEPARATOR        '/'
#define RTSP_VERSION_MAJOR_MINOR_SEPARATOR '.'

/** String table of RTSP methods (rtsp_method_id) */
static const apt_string_table_item_t rtsp_method_string_table[] = {
	{"SETUP",    5,0},
	{"ANNOUNCE", 8,0},
	{"TEARDOWN", 8,0},
	{"DESCRIBE", 8,0},
};

/** Parse resource name fom RTSP URL */
static const char* rtsp_resource_name_parse(const char *rtsp_url)
{
	char *str = strrchr(rtsp_url,'/');
	if(str) {
		str++;
	}
	return str;
}

/** Parse RTSP version */
static rtsp_version_t rtsp_version_parse(char *field)
{
	rtsp_version_t version = RTSP_VERSION_UNKNOWN;
	char *name = apt_read_field(&field,RTSP_NAME_VERSION_SEPARATOR,1);
	if(name && apt_str_compare(name,RTSP_NAME) == TRUE) {
		name = apt_read_field(&field,RTSP_VERSION_MAJOR_MINOR_SEPARATOR,1);
		if(name) {
			switch(*name)
			{
				case '1': version = RTSP_VERSION_1; break;
				default: ;
			}
		}
	}
	return version;
}

/** Generate RTSP version */
static size_t rtsp_version_generate(rtsp_version_t version, char *field)
{
	int ret = sprintf(field,"%s%c%d%c0",RTSP_NAME,RTSP_NAME_VERSION_SEPARATOR,version,RTSP_VERSION_MAJOR_MINOR_SEPARATOR);
	if(ret < 0) {
		ret = 0;
	}
	return ret;
}

/** Parse RTSP status-code */
static APR_INLINE rtsp_status_code_t rtsp_status_code_parse(const char *field)
{
	return apt_size_value_parse(field);
}

/** Generate RTSP status-code */
static APR_INLINE size_t rtsp_status_code_generate(rtsp_status_code_t status_code, char *field)
{
	return apt_size_value_generate(status_code,field);
}

/** Generate RTSP request-line */
static size_t rtsp_request_line_generate(rtsp_request_line_t *start_line, char *line)
{
	size_t offset = 0;
	size_t length = 0;
	
	start_line->method_name = apt_string_table_get(rtsp_method_string_table,RTSP_METHOD_COUNT,start_line->method_id);
	if(!start_line->method_name || !start_line->url) {
		return 0;
	}
	length = strlen(start_line->method_name);
	memcpy(line+offset,start_line->method_name,length);
	offset += length;
	line[offset++] = APT_TOKEN_SP;

	length = strlen(start_line->url);
	memcpy(line+offset,start_line->url,length);
	offset += length;
	line[offset++] = APT_TOKEN_SP;

	length = rtsp_version_generate(start_line->version,line+offset);
	if(length == 0) {
		return length;
	}
	offset += length;
	return offset;
}

/** Generate RTSP status-line */
static size_t rtsp_status_line_generate(rtsp_status_line_t *start_line, char *line)
{
	size_t offset = 0;
	size_t length = rtsp_version_generate(start_line->version,line+offset);
	if(length == 0) {
		return length;
	}
	offset += length;
	line[offset++] = APT_TOKEN_SP;

	length = rtsp_status_code_generate(start_line->status_code,line+offset);
	if(length == 0) {
		return length;
	}
	offset += length;
	line[offset++] = APT_TOKEN_SP;

	if(start_line->reason) {
		length = strlen(start_line->reason);
		memcpy(line+offset,start_line->reason,length);
		offset += length;
	}

	return offset;
}


/** Parse RTSP start-line */
apt_bool_t rtsp_start_line_parse(rtsp_start_line_t *start_line, apt_text_stream_t *text_stream, apr_pool_t *pool)
{
	char *line;
	char *field;
	apt_bool_t status = TRUE;
	start_line->message_type = RTSP_MESSAGE_TYPE_UNKNOWN;
	line = apt_read_line(text_stream);
	if(!line) {
		apt_log(APT_PRIO_WARNING,"cannot parse rtsp start-line \n");
		return FALSE;
	}

	field = apt_read_field(&line,APT_TOKEN_SP,1);
	if(!field) {
		apt_log(APT_PRIO_WARNING,"cannot read the first field in start-line\n");
		return FALSE;
	}

	if(field == strstr(field,RTSP_NAME)) {
		/* parsing RTSP response */
		start_line->message_type = RTSP_MESSAGE_TYPE_RESPONSE;
		rtsp_status_line_init(&start_line->common.status_line);

		start_line->common.status_line.version = rtsp_version_parse(field);

		field = apt_read_field(&line,APT_TOKEN_SP,1);
		if(!field) {
			apt_log(APT_PRIO_WARNING,"cannot parse status-code in status-line\n");
			return FALSE;
		}
		start_line->common.status_line.status_code = rtsp_status_code_parse(field);

		field = apt_read_field(&line,APT_TOKEN_SP,1);
		if(!field) {
			apt_log(APT_PRIO_WARNING,"cannot parse reason phrase in status-line\n");
			return FALSE;
		}
		start_line->common.status_line.reason = apr_pstrdup(pool,field);
	}
	else {
		/* parsing RTSP request */
		start_line->message_type = RTSP_MESSAGE_TYPE_REQUEST;
		rtsp_request_line_init(&start_line->common.request_line);

		start_line->common.request_line.method_name = apr_pstrdup(pool,field);
		start_line->common.request_line.method_id = apt_string_table_find(rtsp_method_string_table,RTSP_METHOD_COUNT,field);

		field = apt_read_field(&line,APT_TOKEN_SP,1);
		if(!field) {
			apt_log(APT_PRIO_WARNING,"cannot parse request url in request-line\n");
			return FALSE;
		}
		start_line->common.request_line.url = apr_pstrdup(pool,field);
		start_line->common.request_line.resource_name = rtsp_resource_name_parse(start_line->common.request_line.url);

		field = apt_read_field(&line,APT_TOKEN_SP,1);
		if(!field) {
			apt_log(APT_PRIO_WARNING,"cannot parse version in request-line\n");
			return FALSE;
		}
		start_line->common.request_line.version = rtsp_version_parse(field);
	}
	return status;
}

/** Generate RTSP start-line */
apt_bool_t rtsp_start_line_generate(rtsp_start_line_t *start_line, apt_text_stream_t *text_stream)
{
	apt_bool_t status = FALSE;
	size_t length = 0;
	switch(start_line->message_type) {
		case RTSP_MESSAGE_TYPE_REQUEST:
			length = rtsp_request_line_generate(&start_line->common.request_line,text_stream->pos);
			break;
		case RTSP_MESSAGE_TYPE_RESPONSE:
			length = rtsp_status_line_generate(&start_line->common.status_line,text_stream->pos);
			break;
		default:
			break;
	}

	if(length > 0) {
		status = TRUE;
		text_stream->pos += length;
		apt_insert_end_of_line(text_stream);
	}
	
	return status;
}
