/*  flvstreamer
 *  Copyright (C) 2009 Andrej Stepanchuk
 *  Copyright (C) 2009 Howard Chu
 *
 *  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 2, 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 flvstreamer; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */

#include <stdlib.h>
#include <string.h>

#include <assert.h>
#include <ctype.h>

#include "log.h"
#include "parseurl.h"

#define RTMP_PROTOCOL_UNDEFINED	-1
#define RTMP_PROTOCOL_RTMP      0
#define RTMP_PROTOCOL_RTMPT     1 // not yet supported
#define RTMP_PROTOCOL_RTMPS     2 // not yet supported
#define RTMP_PROTOCOL_RTMPE     3
#define RTMP_PROTOCOL_RTMPTE    4 // not yet supported
#define RTMP_PROTOCOL_RTMFP     5 // not yet supported

char *str2lower(char *str, int len)
{
	char *res = (char *)malloc(len+1);
	char *p;

	for(p=res; p<res+len; p++, str++) {
		*p = tolower(*str);
	}

	*p = 0;

	return res;
}

int chr2hex(char c)
{
	if(c <= 57 && c >= 48)
        	return c-48;
        else if(c <= 102 && c >= 97)
                return c-97+10;
        return -1;
}

int hex2bin(char *str, char **hex)
{
	if(!str || !hex)
		return 0;

	int len = strlen(str);

	if(len % 2 != 0)
		return 0;

	int ret = len/2;

	*hex = (char *)malloc(ret);
	if((*hex)==0)
		return 0;

	char *hexptr = *hex;
	char *lwo = str2lower(str, len);
	char *lw = lwo;

	len /= 2;

	while(len) {
		int d1 = chr2hex(*lw); lw++;
		int d2 = chr2hex(*lw); lw++;

		if(d1<0 || d2<0) {
			free(*hex);
			free(lwo);
			*hex=NULL;
			return -1;
		}

		*hexptr = (unsigned char)(d1*16+d2);
		hexptr++;
		len--;
	}

	free(lwo);
	return ret;
}

int ParseUrl(char *url, int *protocol, char **host, unsigned int *port, char **playpath, char **app)
{
	assert(url != 0 && protocol != 0 && host != 0 && port != 0 && playpath != 0 && app != 0);

	Log(LOGDEBUG, "Parsing...");

	*protocol = 0; // default: RTMP

	// Old School Parsing
	char *lw = str2lower(url, 6);
	char *temp;

	// look for usual :// pattern
	char *p = strstr(url, "://");
	int len = (int)(p-url);
	if(p == 0) {
		Log(LOGWARNING, "RTMP URL: No :// in url!");
		free(lw);
		return 0;
	}

	if(len == 4 && strncmp(lw, "rtmp", 4)==0)
		*protocol = RTMP_PROTOCOL_RTMP;
	else if(len == 5 && strncmp(lw, "rtmpt", 5)==0)
		*protocol = RTMP_PROTOCOL_RTMPT;
	else if(len == 5 && strncmp(lw, "rtmps", 5)==0)
	        *protocol = RTMP_PROTOCOL_RTMPS;
	else if(len == 5 && strncmp(lw, "rtmpe", 5)==0)
	        *protocol = RTMP_PROTOCOL_RTMPE;
	else if(len == 5 && strncmp(lw, "rtmfp", 5)==0)
	        *protocol = RTMP_PROTOCOL_RTMFP;
	else if(len == 6 && strncmp(lw, "rtmpte", 6)==0)
	        *protocol = RTMP_PROTOCOL_RTMPTE;
	else {
		Log(LOGWARNING, "Unknown protocol!\n");
		goto parsehost;
	}

	Log(LOGDEBUG, "Parsed protocol: %d", *protocol);

parsehost:
	free(lw);

	// lets get the hostname
	p+=3;

	// check for sudden death
	if(*p==0) {
		Log(LOGWARNING, "No hostname in URL!");
		return 0;
	}

	int iEnd   = strlen(p);
	int iCol   = iEnd+1;
	int iQues  = iEnd+1;
	int iSlash = iEnd+1;

	if((temp=strstr(p, ":"))!=0)
		iCol = temp-p;
	if((temp=strstr(p, "?"))!=0)
	        iQues = temp-p;
	if((temp=strstr(p, "/"))!=0)
	        iSlash = temp-p;

	int min = iSlash < iEnd ? iSlash : iEnd+1;
	min = iQues   < min ? iQues   : min;

	int hostlen = iCol < min ? iCol : min;

	if(min < 256) {
		*host = (char *)malloc((hostlen+1)*sizeof(char));
		strncpy(*host, p, hostlen);
		(*host)[hostlen]=0;

		Log(LOGDEBUG, "Parsed host    : %s", *host);
	} else {
		Log(LOGWARNING, "Hostname exceeds 255 characters!");
	}

	p+=hostlen; iEnd-=hostlen;

	// get the port number if available
	if(*p == ':') {
		p++; iEnd--;

		int portlen = min-hostlen-1;
		if(portlen < 6) {
			char portstr[6];
			strncpy(portstr,p,portlen);
			portstr[portlen]=0;

			*port = atoi(portstr);
			if(*port == 0)
				*port = 1935;

			Log(LOGDEBUG, "Parsed port    : %d", *port);
		} else {
			Log(LOGWARNING, "Port number is longer than 5 characters!");
		}

		p+=portlen; iEnd-=portlen;
	}

	if(*p != '/') {
		Log(LOGWARNING, "No application or playpath in URL!");
		return 1;
	}
	p++; iEnd--;

	// parse application
	//
	// rtmp://host[:port]/app[/appinstance][/...]
	// application = app[/appinstance]
	int iSlash2 = iEnd+1; // 2nd slash
        int iSlash3 = iEnd+1; // 3rd slash

        if((temp=strstr(p, "/"))!=0)
        	iSlash2 = temp-p;

	if((temp=strstr(p, "?"))!=0)
	        iQues = temp-p;

	if(iSlash2 < iEnd)
		if((temp=strstr(p+iSlash2+1, "/"))!=0)
			iSlash3 = temp-p;

	//Log(LOGDEBUG, "p:%s, iEnd: %d\niSlash : %d\niSlash2: %d\niSlash3: %d", p, iEnd, iSlash, iSlash2, iSlash3);

	int applen = iEnd+1; // ondemand, pass all parameters as app
	int appnamelen = 8; // ondemand length

	if(iQues < iEnd && strstr(p, "slist=")) { // whatever it is, the '?' and slist= means we need to use everything as app and parse plapath from slist=
		appnamelen = iQues;
		applen = iEnd+1; // pass the parameters as well
	}
	else if(strncmp(p, "ondemand/", 9)==0) {
                // app = ondemand/foobar, only pass app=ondemand
                applen = 8;
        }
	else { // app!=ondemand, so app is app[/appinstance]
		appnamelen = iSlash2 < iEnd ? iSlash2 : iEnd;
        	if(iSlash3 < iEnd)
                	appnamelen = iSlash3;

		applen = appnamelen;
	}

	*app = (char *)malloc((applen+1)*sizeof(char));
	strncpy(*app, p, applen);
	(*app)[applen]=0;
	Log(LOGDEBUG, "Parsed app     : %s", *app);

	p += appnamelen;
	iEnd -= appnamelen;

	if (*p == '/') {
		p += 1;
		iEnd -= 1;
	}

	*playpath = ParsePlaypath(p);

        return 1;
}

/*
 * Extracts playpath from RTMP URL. playpath is the file part of the
 * URL, i.e. the part that comes after rtmp://host:port/app/
 *
 * Returns the stream name in a format understood by FMS. The name is
 * the playpath part of the URL with formating depending on the stream
 * type:
 *
 * mp4 streams: prepend "mp4:", remove extension
 * mp3 streams: prepend "mp3:", remove extension
 * flv streams: remove extension
 */
char *ParsePlaypath(const char *playpath) {
	if (!playpath || !*playpath)
		return NULL;

	int addMP4 = 0;
	int addMP3 = 0;
	int subExt = 0;
	const char *temp, *q, *ext = NULL;
	const char *ppstart = playpath;

	int pplen = strlen(playpath);

	if ((*ppstart == '?') &&
	    (temp=strstr(ppstart, "slist=")) != 0) {
		ppstart = temp+6;
		pplen = strlen(ppstart);

		temp = strchr(ppstart, '&');
		if (temp) {
			pplen = temp-ppstart;
		}
	}

	q = strchr(ppstart, '?');
	if (pplen >= 4) {
		if (q)
			ext = q-4;
		else
			ext = &ppstart[pplen-4];
		if ((strncmp(ext, ".f4v", 4) == 0) ||
		    (strncmp(ext, ".mp4", 4) == 0)) {
			addMP4 = 1;
			subExt = 1;
		// Only remove .flv from rtmp URL, not slist params
		} else if ((ppstart == playpath) &&
		    (strncmp(ext, ".flv", 4) == 0)) {
			subExt = 1;
		} else if (strncmp(ext, ".mp3", 4) == 0) {
			addMP3 = 1;
			subExt = 1;
		}
	}

	char *streamname = (char *)malloc((pplen+4+1)*sizeof(char));
	if (!streamname)
		return NULL;

	char *destptr = streamname, *p;
	if (addMP4 && (strncmp(ppstart, "mp4:", 4) != 0)) {
		strcpy(destptr, "mp4:");
		destptr += 4;
	} else if (addMP3 && (strncmp(ppstart, "mp3:", 4) != 0)) {
		strcpy(destptr, "mp3:");
		destptr += 4;
	}

 	for (p=(char *)ppstart; pplen >0;) {
		/* skip extension */
		if (subExt && p == ext) {
			p += 4;
			pplen -= 4;
		}
		if (*p == '%') {
			int c;
			sscanf(p+1, "%02x", &c);
			*destptr++ = c;
			pplen -= 3;
			p += 3;
		} else {
			*destptr++ = *p++;
			pplen--;
		}
	}
	*destptr = '\0';

	return streamname;
}
