#include "htlib2.h"
#include "Header.h"

static BOOL
proceedUntilSpace(char** text)
{
	BOOL f = FALSE;
	char* p = *text;
	while (*p != 0 && isspace(*p)) {
		p++;
		f = TRUE;
	}
	*text = p;
	return f;
}
static int
backUntilSpace(char** text, int max)
{
	char* p = *text;
	while (0<max && isspace(p[-1])) {
		*(--p) = 0;
		max--;
	}
	*text = p;
	return max;
}
static int
trim(char** text)
{
	proceedUntilSpace(text);
	int len = strlen(*text);
	char* tend = *text + len;
	return backUntilSpace(&tend, len);
}
int
M(Header,Parse)(HTLIB* o,
				HTLIB_Header* header_buffer, USHORT blen,
				HTLIB_NEEDS_FUNC needs,				
				HTLIB_ERROR* err)
{
	LOGINFO("Header_Parse(blen=%d\n", blen);
	*err = HTLIB_E_NO_ERROR;

	char* header_text = o->_header;
	char* last_value_end = NULL;
	char* hend = header_text + strlen(header_text);
	
	int hnum = 0;
	while (header_text < hend) {
		if (blen <= hnum) {
			*err = HTLIB_E_OUT_OF_MEMORY;
			LOGWARN("Parse(%.10s...): out of memory (%d <= %d)\n",
					header_text, blen, hnum);
			break;
		}
		HTLIB_Header* one = &header_buffer[hnum];
		
		/* name candidate */
		char* name = header_text;
		LOGINFO("processing: %.30s...\n", name);

		/* First, finds line-end */
		char* p;
		if ((p=strpbrk(header_text, "\n"))!=NULL) {
			*p = 0;
			header_text = p+1;
		} else {
			header_text = hend;
		}

		/* for the last value? */
		if (proceedUntilSpace(&name)) {
			if (last_value_end != NULL) {
				/* proceeding value for the previous one */
				*last_value_end++ = ' ';
				*last_value_end = 0;

				int len = strlen(name);
				/* strcpy can't be used here */
				memmove(last_value_end, name, len+1);
				last_value_end += len;
				backUntilSpace(&last_value_end, len);
				continue;
			}
		}				
		/* name found */
		char* value = strpbrk(name, ":");
		if (value != NULL) {
			*value++ = 0;
		} else {
			*err = HTLIB_E_INVALID_SYNTAX;
			LOGERR("Header: %s without ':'?\n", one->name);
			return -1;
		}
		trim(&name);
		/* name determined */
		one->name = name;
		
		/* for value */
		trim(&value);
		last_value_end = value + strlen(value);
		one->value = value;
		
		/* initialize param */
		one->param_num = 0;
		one->param = NULL;

		if (needs==NULL ||
			strcasecmp(one->name, "Content-Length")==0 ||
			strcasecmp(one->name, "Connection")==0 ||
			(one->param_num /* temporarily */=needs(o, one->name))
			!=HTLIB_N_NONE) {
			/* only if the application needs it */
			hnum++;
		} else {
			LOGINFO("Header:%s ignored\n", one->name);
		}
	}
	{
		/* parses parameter */
		int index = hnum;
		int i;
		HTLIB_ERROR err2;
		for (i=0; i<hnum; i++) {
			HTLIB_NEEDS_ENUM n = header_buffer[i].param_num;
			header_buffer[i].param_num = 0;
			
			switch (n) {
			case HTLIB_N_PARSE_PARAM:
				if (M(Header,ParseParam)(&header_buffer[i],
										 &header_buffer[index], blen-index,
										 &err2)) {
					index += header_buffer[i].param_num;
				}
				if (*err == HTLIB_E_NO_ERROR && err2 != HTLIB_E_NO_ERROR) {
					*err = err2;
				}
				break;

			default:
				/* don't need to parse */
				break;
			}
		}
	}
	return hnum;
}

BOOL
M(Header,ParseParam)(HTLIB_Header* hone,
					 HTLIB_Header* header_buffer, USHORT blen,
					 HTLIB_ERROR* err)
{
	const char* vend = hone->value + strlen(hone->value);
	char* p;
	char* vp;

	*err = HTLIB_E_NO_ERROR;
	
	if ((p=strchr(hone->value, ';'))==NULL) {
		return FALSE;
	}

	hone->param_num = 0;
	*p++ = 0;
	vp = p;

	trim((char**)&hone->value);
	
	while (vp < vend) {
		HTLIB_Header* pone = &header_buffer[hone->param_num];

		if (blen <= hone->param_num) {
			*err = HTLIB_E_OUT_OF_MEMORY;
			LOGWARN("ParseParam(%.10s...): out of memory (%d <= %d)\n",
					vp, blen, hone->param_num);
			break;
		}

		/* name candidate */
		pone->name = vp;
		if ((p=strpbrk(vp, ";"))!=NULL) {
			*p++ = 0;
			vp = p;
		} else {
			vp = vp + strlen(vp);
		}

		/* value candidate */
		if ((pone->value=strchr(pone->name, '='))!=NULL) {
			*(char*)pone->value++ = 0;
			trim((char**)&pone->value);
		}

		/* now, name doesn't incude value */
		if (trim((char**)&pone->name)==0) {
			break;
		}
		pone->param_num = 0;
		pone->param = NULL;
		if (hone->param == NULL) {
			hone->param = pone;
		}
		hone->param_num++;
	}
	return TRUE;
}

int
M(Header,Find)(const HTLIB_Header* headers_or_params,
			   USHORT len,
			   const char* name)
{
	int i;
	for (i=0; i<len; i++) {
		if (strcasecmp(headers_or_params[i].name, name)==0) {
			return i;
		}
	}
	return -1;
}

int
M(Header,Serialize)(const HTLIB_Header* headers, USHORT header_len,
					char* buffer, USHORT buffer_len,
					HTLIB_ERROR* err)
{
	int windex = 0;
	int rest = buffer_len;
	int i;

	for (i=0; i<header_len; i++) {
		int len;
		len = snprintf(buffer+windex, rest, "%s: %s",
					   headers[i].name, headers[i].value);
		if (rest <= len) {
			LOGERR("would overflow(%s): %d <= %d\n",
				   headers[i].name, rest, len);
			goto memerr;
		}
		windex += len;
		rest -= len;

		if (headers[i].param) {
			int j;
			const HTLIB_Header* param = headers[i].param;
			for (j=0; j<headers[i].param_num; j++) {
				if (param[j].value != NULL) {
					len = snprintf(buffer+windex, rest, "; %s=%s",
								   param[j].name, param[j].value);
				} else {
					len = snprintf(buffer+windex, rest, "; %s",
								   param[j].name);
					
				}
				if (rest <= len) {
					LOGERR("would overflow(%s): %d <= %d\n",
						   param[j].name, rest, len);
					goto memerr;
				}
				windex += len;
				rest -= len;
			}
		}
		len = snprintf(buffer+windex, rest, "\r\n");
		if (rest <= len) {
			LOGERR("would overflow(CRLF): %d <= %d\n",
				   rest, len);
			goto memerr;
		}
		windex += len;
		rest -= len;
	}
	return windex;

memerr:
	*err = HTLIB_E_OUT_OF_MEMORY;
	return -1;
}

BOOL
M(Header,HasConnectionClose)(const HTLIB_Header* header_buffer, USHORT blen)
{
	int i;
	if ((i=M(Header,Find)(header_buffer, blen, "Connection"))!=-1 &&
		strstr(header_buffer[i].value, "close")!=NULL) {
		return TRUE;
	}
	return FALSE;
}

const char*
M(Header,GetHttpStatusMessage)(int status)
{
	static const struct {
		int status;
		const char* msg;
	} s[] = {
		{ 100, "Continue" },
		{ 101, "Switching Protocols" },

		{ 200, "OK" },
		{ 201, "Created" },
		{ 202, "Accepted" },
		{ 203, "Non-Authoritative Information" },
		{ 204, "No Content" },
		{ 205, "Reset Content" },
		{ 206, "Partial Content" },

		{ 300, "Multiple Choices" },
		{ 301, "Moved Permanently" },
		{ 302, "Found" },
		{ 303, "See Other" },
		{ 304, "Not Modified" },
		{ 305, "Use Proxy" },
		{ 306, "(Unused)" },
		{ 307, "Temporary Redirect" },

		{ 400, "Bad Request" },
		{ 401, "Unauthorized" },
		{ 402, "Payment Required" },
		{ 403, "Forbidden" },
		{ 404, "Not Found" },
		{ 405, "Method Not Allowed" },
		{ 406, "Not Acceptable" },
		{ 407, "Proxy Authentication Required" },
		{ 408, "Request Timeout" },
		{ 409, "Conflict" },
		{ 410, "Gone" },
		{ 411, "Length Required" },
		{ 412, "Precondition Failed" },
		{ 413, "Request Entity Too Large" },
		{ 414, "Request-URI Too Long" },
		{ 415, "Unsupported Media Type" },
		{ 416, "Requested Range Not Satisfiable" },
		{ 417, "Expectation Failed" },

		{ 500, "Internal Server Error" },
		{ 501, "Not Implemented" },
		{ 502, "Bad Gateway" },
		{ 503, "Service Unavailable" },
		{ 504, "Gateway Timeout" },
		{ 505, "HTTP Version Not Supported" },
	};

	int i;
	for (i=0; i<sizeof(s)/sizeof(s[0]); i++) {
		if (s[i].status == status) {
			return s[i].msg;
		}
	}
	return NULL;
}
