/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/


#include	"memory_debug.h"
#include	"http.h"
#include	"utils.h"
#include 	"xl.h"
#include "HTMLDB.h"

extern GET_TBL get_table[];
extern GET_TBL pol_table[];

char * change_page;
int polling_flag;
int viewer_ok;
int http_system_wait;
extern char http_hostname[];
extern int http_port;
char * current_page;

#define KANJI_CODE_UNKNOWN	0
#define KANJI_CODE_EUC		1
#define KANJI_CODE_SJIS		2
#define KANJI_CODE_JIS		3

extern CODE_METHOD euc_cm, jis_cm,sjis_cm;

static int GetKanjiCode(unsigned char *str){
	int val = 0;
    unsigned char b1, b2;
	while(*str && (b1=*str++) <= 0x80){
		if (*str == 0x1b) {
			//jis
			return KANJI_CODE_JIS;
		}
	}
	if(!*str)
		return KANJI_CODE_UNKNOWN;
    b2 = *str++;
    if ((b1 >= 0xa1 && b1 <= 0xfe) && (b2 >= 0xa1 && b1 <= 0xfe)){
		//euc
		return KANJI_CODE_EUC;
    }
    if ((b1 >= 0x81 && b1 <= 0x9f ||
		b1 >= 0xe0 && b1 <= 0xfc) && 
        (b2 >= 0x40 && b2 <= 0xfc && b2 != 0x7f)){
		//shift jis
		return KANJI_CODE_SJIS;
    }

    return KANJI_CODE_UNKNOWN;
}

static L_CHAR *GetLString(int KanjiCode, char *strSrc){
	switch(KanjiCode){
		case KANJI_CODE_SJIS:
			return l_string(&sjis_cm, strSrc);
		case KANJI_CODE_EUC:
			return l_string(&euc_cm, strSrc);
		case KANJI_CODE_JIS:
			return l_string(&jis_cm, strSrc);
		default:
			return l_string(std_cm, strSrc);
	}
}

int DecodeUrlEncoding(const char *strSrc, char *strDest)
{
	int i;
	int nDestPos=0;
	int nSrcLength = strlen(strSrc);
	if(strDest){
		for(i=0; i<nSrcLength; ++i){
			unsigned char ch = strSrc[i];
			if(ch=='+'){
				strDest[nDestPos++] = ' ';
			}else if(ch=='%'){
				char tmp[3];
				tmp[0] = strSrc[++i];
				tmp[1] = strSrc[++i];
				tmp[2] = '\0';
				strDest[nDestPos++] = (char)strtol(tmp,NULL,16);
			}
			else{
				strDest[nDestPos++] = ch;
			}
		}
		strDest[nDestPos] = '\0';
	}
	else{
		for(i=0; i<nSrcLength; ++i,++nDestPos){
			if(strSrc[i]=='%'){
				i+=2;
			}
		}
	}
	return nDestPos;
}

void ParseHttpQuery(XLISP_ENV * env,const char *strQueryString)
{
	char * name;
	char * data;
	char * DecodedName;
	char * DecodedData;
	int i,m,p;
	int length;

	length = strlen(strQueryString);
	name = (char*)d_alloc(length+1,1212);
	data = (char*)d_alloc(length+1,1313);
	DecodedName = (char*)d_alloc(length+1,1414);
	DecodedData = (char*)d_alloc(length+1,1231);
	
	for(i = 0,p = 0 ; i < length ; )
	{
		name[0] = 0;
		data[0] = 0;
		for(m = 0 ; m < length  ; i++,m++)
		{
			name[m] = strQueryString[p++];
			if(name[m] == '=' || (length == i))
			{
				break;
			}
		}
		name[m] = 0;
		
		for(m = 0 ; m < length ; i++,m++)
		{
			data[m] = strQueryString[p++];
			if((data[m] == '&') || (length == i))
			{
				break;
			}
		}
		data[m] = 0;
		
		DecodeUrlEncoding(name,DecodedName);
		DecodeUrlEncoding(data,DecodedData);

//printf("name:=%s\nvalue:=%s\n",DecodedName,DecodedData);

		set_env(env,GetLString(GetKanjiCode(DecodedName),DecodedName),
			get_string(GetLString(GetKanjiCode(DecodedData),DecodedData)));
	}
	d_f_ree(name);
	d_f_ree(data);
	d_f_ree(DecodedName);
	d_f_ree(DecodedData);
}

unsigned int change_time;
int change_delay;
#define CHANGE_TIM 60

void
launch_http_system(int force)
{
	if ( http_system_wait <= 0 ) {
		if ( force == 0 &&
				change_time + CHANGE_TIM > get_xltime() ) {
			change_delay = 1;
			return;
		}
		if ( change_page == 0 )
			return;
		change_delay = 0;
		change_time = get_xltime();
		http_system(
			http_hostname,
			http_port,
			change_page);
		d_f_ree(change_page);
		change_page = 0;
		viewer_ok = 1;
	}
}

void
polling_tick()
{
	lock_task(http_lock);
	if ( change_delay )
		launch_http_system(0);

	if ( polling_flag ) {
		viewer_ok = 1;
		polling_flag = 0;
	}
	else if ( http_system_wait > 0 ) {
		viewer_ok = 1;
	}
	else if ( change_page ) {
		launch_http_system(0);
	}
	else 
		viewer_ok = 0;
	if ( http_system_wait > 0 )
		http_system_wait -= 1;
	unlock_task(http_lock,"polling_tick");
}

void
get_connection(HTTP_ENV * he,HTTP_ELEMENT * el)
{
	if ( el->next == 0 )
		return;
	if ( strcmp(el->next->data,"Keep-Alive") == 0 )
		he->flags |= HEF_KEEP;
}

void
set_change_page(char * page,int force)
{

	lock_task(http_lock);
	if ( change_page )
		d_f_ree(change_page);
	change_page = copy_str(page);

	if ( viewer_ok == 0) {
		launch_http_system(force);
	}
	else if ( force ) {
		change_delay = 0;
		change_time = 0;
	}
	unlock_task(http_lock,"set_change_page");
}

void
polling(HTTP_ENV * he,HTTP_ELEMENT * el)
{
char * path;
int i;
int pol;

	lock_task(http_lock);
	polling_flag = 1;
	unlock_task(http_lock,"polling");

	el = el->next;
	if ( el == 0 )
		goto cut;
	pol = atoi(el->data);
	if ( he->polcnt != pol )
		goto cut;
	el = el->next;
	if ( el == 0 )
		path = "/";
	else	path = el->data;

	lock_task(http_lock);
	if ( current_page )
		d_f_ree(current_page);
	if ( strcmp(path,"other") )
		current_page = copy_str(path);
	else	current_page = 0;
	http_system_wait = 0;
	if ( change_page && change_time + CHANGE_TIM < get_xltime() ) {
		change_time = get_xltime();
		s_printf(he->key->s,"%i\n%s\n",
			POL_URL,
			change_page);
		d_f_ree(change_page);
		change_page = 0;
		unlock_task(http_lock,"polling");
		return;
	}
	s_printf(he->key->s,"%i\n",
		POL_IDLE);
	unlock_task(http_lock,"polling");
	return;
cut:
	s_printf(he->key->s,"%i\n",POL_KILL);
}

XL_SEXP *
get_option_list(HTTP_ELEMENT * el)
{
XL_SEXP * ret;
	if ( el == 0 )
		return 0;
	ret = cons(n_get_string(el->data),0);
	el = el->next;
	for ( ; el ; el = el->next )
		ret = append(ret,cons(n_get_string(el->data),0));
	return ret;
}


char *
get_after_question(char * path)
{
char * ptr;
	for ( ptr =path ; *ptr ; ptr ++ )
		if ( *ptr == '?' ) {
			ptr ++;
			return ptr;
		}
	return ptr;
}


XL_SEXP * quote_trace();

void
http_get(HTTP_ENV * he)
{
HTTP_ELEMENT * el, * el2;
char * path;
int i;
char * ptr;
XLISP_ENV * env;
XL_SEXP * pc;
extern XLISP_ENV * env_HTMLdb;
char * after_path;
XL_SEXP * tmp2,* tmp3;

int length;


	he->flags = 0;
	gc_push(0,0,"http_get");
	env = new_env(env_HTMLdb);
retry:
	for ( ; ; ) {
		el = http_parser(he);
		if ( el == 0 )
			goto end;
		if ( strcmp(el->data,"GET") == 0 )
			goto get_op;
		if(strcmp(el->data,"POST") == 0)
			goto post_op;
		if ( strcmp(el->data,"*POLLING") == 0 )
			goto pol_op;
	}
get_op:
	if ( el->next == 0 ) {
		path = "/";
		set_env(env,l_string(std_cm,"GET"),
			n_get_string(path));
		set_env(env,l_string(std_cm,"GET-option"),0);
		set_env(env,l_string(std_cm,"QUERY_STRING"),
			n_get_string(""));
		goto start;
	}
	path = el->next->data;
	if ( el->next->next == 0 ) {
		set_env(env,l_string(std_cm,"GET"),
			n_get_string(path));
		set_env(env,l_string(std_cm,"GET-option"),0);
		set_env(env,l_string(std_cm,"QUERY_STRING"),
			n_get_string(""));
		goto start;
	}
	set_env(env,l_string(std_cm,"GET"),
		n_get_string(path));
	set_env(env,l_string(std_cm,"GET-option"),
		get_option_list(el->next));
	set_env(env,l_string(std_cm,"QUERY_STRING"),
		n_get_string(after_path = get_after_question(path)));
	ParseHttpQuery(env,after_path);
	for ( ; ; ) {
		el2 = http_parser(he);
		if ( el2 == 0 )
			break;
		if ( strcmp(el2->data,"Host:") == 0 ) {
			if ( el2->next == 0 )
				goto next;
			if ( he->hostpath )
				d_f_ree(he->hostpath);
			he->hostpath = copy_str(el2->next->data);
			for ( ptr = he->hostpath ; *ptr && *ptr != ':';
				ptr ++);
			*ptr = 0;
			ptr ++;
			set_env(env,l_string(std_cm,"Host-name"),
				n_get_string(he->hostpath));
			set_env(env,l_string(std_cm,"Host-port"),
				n_get_string(ptr));
		}
	next:
		free_http_element(el2);
	}
	goto start;
	
post_op:
	length = 0;
	if ( el->next == 0 ) {
		path = "/";
		set_env(env,l_string(std_cm,"POST"),
			n_get_string(path));
		set_env(env,l_string(std_cm,"POST-option"),0);
		set_env(env,l_string(std_cm,"QUERY_STRING"),
			n_get_string(""));
		goto start;
	}
	path = el->next->data;
	if ( el->next->next == 0 ) {
		set_env(env,l_string(std_cm,"POST"),
			n_get_string(path));
		set_env(env,l_string(std_cm,"POST-option"),0);
		set_env(env,l_string(std_cm,"QUERY_STRING"),
			n_get_string(""));
		goto start;
	}
	set_env(env,l_string(std_cm,"POST"),
		n_get_string(path));
	set_env(env,l_string(std_cm,"POST-option"),
		get_option_list(el->next));

	for ( ; ; ) {
		el2 = http_parser(he);
		if ( el2 == 0 )
		{
			char * buf;
			if(length == 0)
				goto next2;
			buf = (char*)d_alloc(length+1,1234);
			for(i = 0 ; i < length ; i++)
				s_read(he->key->s,&buf[i],1);
			buf[i] = 0;
			ParseHttpQuery(env,buf);
			d_f_ree(buf);
			
			goto start;
		}
		if(strcmp(el2->data,"Content-Length:") == 0)
			length = atoi(el2->next->data);
		if ( strcmp(el2->data,"Host:") == 0 ) {
			if ( el2->next == 0 )
				goto next2;
			if ( he->hostpath )
				d_f_ree(he->hostpath);
			he->hostpath = copy_str(el2->next->data);
			for ( ptr = he->hostpath ; *ptr && *ptr != ':';
				ptr ++);
			*ptr = 0;
			ptr ++;
			set_env(env,l_string(std_cm,"Host-name"),
				n_get_string(he->hostpath));
			set_env(env,l_string(std_cm,"Host-port"),
				n_get_string(ptr));
		}
	next2:
		free_http_element(el2);
	}
	
	goto start;

start:

	set_env(env,l_string(std_cm,"polling-count"),
		get_integer(he->polcnt,0));



	{
		XL_SEXP * elem;
		XL_SEXP * tmp;
		int size,s;
		char * ptr;
		char buf[1024];

lock_task(http_lock);
		elem = GetElement(l_string(std_cm,path),HTMLDataBase);
unlock_task(http_lock,"http_get()");
/*
		print_sexp(s_stdout,elem,PF_LISP|PF_RAW_DISABLE);
*/


		if(elem)
		{
			switch(CheckElementType(elem))
			{
			case HTMLDoc:
				s_printf(he->key->s,
					"HTTP/1.0 200 OK\n\n");
				print_sexp(he->key->s,
					quote_trace(env,
						GetDocument(elem)),
					PF_HTML|PF_TEXT|PF_INDENT);
				break;
			case Raw:
				s_printf(he->key->s,
					"HTTP/1.0 200 OK\n\n");
				tmp = GetDocument(elem);
				tmp = eval(env,tmp);
				switch ( get_type(tmp) ) {
				case XLT_RAW:
					ptr = tmp->raw.data;
					size = tmp->raw.size;
					break;
				case XLT_PAIR:
					tmp2 = get_el(tmp,0);
					tmp3 = get_el(tmp,1);
					if ( get_type(tmp2) == XLT_STRING )
						s_printf(he->key->s,
							"%ls",
							tmp2->string.data);
					if ( get_type(tmp3) == XLT_RAW ) {
						ptr = tmp3->raw.data;
						size = tmp3->raw.size;
					}
					else {
						ptr = 0;
						size = 0;
					}
					break;
				default:
					ptr = 0;
					size = 0;
				}
				if ( size == 0 ) {
					s_printf(he->key->s,"\n\n");
				}
				else for ( ; size ; ) {
					s = s_write(he->key->s,
						ptr,size);
					if ( s < 0 )
						break;
					size -= s;
					ptr += s;
				}
				break;
			case XLScript:
				switch(CheckFileType(
					l_string(std_cm,path)))
				{
				case html:
				case cgi:
					s_printf(he->key->s,
						"HTTP/1.0 200 OK\n\n");
					print_sexp(he->key->s,
 						eval(gblisp_top_env0,
						GetScript(elem)),PF_HTML);
					break;
				default:
					eval(gblisp_top_env0,GetScript(elem));
				}
				break;
			case Primitive:
				ExecPrimitive(he->key->s,elem);	
				break;
			}
			pc = eval(env,get_symbol(
				l_string(std_cm,"polling-count")));
			if ( get_type(pc) == XLT_INTEGER )
				he->polcnt = pc->integer.data;
			goto end;
		}	
	}
	he->path = path;
	
	for ( i = 0 ; get_table[i].path[0] ; i ++ ) {
		if ( strcmp(get_table[i].path,path) )
			continue;
		if ( get_table[i].action ) {
			(*get_table[i].action)(he);
			goto end;
		}
	}

	(*get_table[i].action)(he);
	goto end;

pol_op:
	polling(he,el);
	goto end;
end:
	s_flush(he->key->s);
	free_http_element(el);
	if ( he->flags & HEF_KEEP )
		goto retry;
	gc_pop(0,0);
	return;
}
