/**********************************************************************
 
	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	"memory_routine.h"
#include	"xlerror.h"
#include	"xl.h"
#include	"utils.h"
#include	"XLoHTTP.h"
#include	"long_char.h"
#include	"queue.h"
#include	"pri_level.h"
#include	"s_buf.h"

extern SEM session_lock;
SYS_QUEUE session_http_que;

void gc_s_que();
void gc_get_s_que();
void session_task();
void gc_gb_file();
void gc_d_sexp();

typedef struct session_http_n {
	Q_HEADER		h;
	STREAM *		st;
	D_SEXP *		d;
	XL_FILE *		f;
	int			ln;
} SESSION_HTTP_N;


void
init_session_http()
{
	memset(&session_http_que,0,sizeof(SYS_QUEUE));
	session_http_que.flags = QF_FIFO;
	session_http_que.gc_func = gc_s_que;
	session_http_que.gc_get = gc_get_s_que;
	session_http_que.key_func = session_task;
	session_http_que.pri = PRI_WRITE_BUF;
	setup_queue(&session_http_que);
}

void
gc_s_que(SESSION_HTTP_N * n)
{
void gc_gb_file();
	gc_gb_file(n->f);
	gc_d_sexp(n->d);
}

void
gc_get_s_que(SESSION_HTTP_N * n)
{
	lock_mem();
	gc_set_nl(n->f,gc_gb_file);
	gc_set_nl(n->f,gc_d_sexp);
	unlock_mem();
}


int
open_session_http(int open_opt)
{
	return 1;
}


void
close_session_http(int id)
{
	return;
}


void
output_session_http(STREAM * con,char * server,int port)
{

}


L_CHAR * 
get_method(L_CHAR * method)
{
L_CHAR * ret;
L_CHAR * p;
	ret = ll_copy_str(method);
	for ( p = ret ; *p ; p ++ ) {
		if ( 'a' <= *p && *p <= 'z' )
			*p += 'A' - 'a';
	}
	return ret;
}


XL_SEXP *
read_reply(STREAM * st,XL_FILE * f,int ln)
{
HTTP_INFO info;
XL_SEXP * ret, * sx;
char * ptr;
int p_size;
int size;
int er;
CODE_METHOD * cm;
	if ( scan_HTTP(&info,st,0) < 0 )
		return get_error(
			f,
			ln,
			XLE_PROTO_ACCESS_STREAM,
			l_string(std_cm,"RemoteSession"),
			List(n_get_string("http connection error"),
				-1));
	if ( info.err != 200 ) {
		return get_error(
			f,
			ln,
			XLE_PROTO_ACCESS_STREAM,
			l_string(std_cm,"RemoteSession"),
			List(n_get_string("http error"),
				get_integer(info.err,0),-1));
	}
	if ( strcmp(info.content_type,"text/xml") == 0 ) {
		cm = get_encoding(l_string(std_cm,info.charset));
		if ( cm )
			s_set_cm(st,cm);
		ret = List(n_get_symbol("text/xml"),
			init_parse(st,l_string(std_cm,"text/xml"),
					l_string(std_cm,"text/xml")),
			-1);
	}
	else if ( strcmp(info.content_type,"text/html") == 0 ) {
		cm = get_encoding(l_string(std_cm,info.charset));
		if ( cm )
			s_set_cm(st,cm);
		ret = List(n_get_symbol("text/xml"),
			sx = init_parse(st,l_string(std_cm,"text/http"),
					l_string(std_cm,"text/http")),
			-1);
		sx->h.file->flags |= XLF_HTML;
	}
	else {
		p_size = 10;
		ptr = d_alloc(p_size);
		size = 0;
		for ( ; ; ) {
			if ( size >= p_size ) {
				ptr = d_re_alloc(ptr,2*p_size);
				p_size = 2*p_size;
			}
			er = s_read(st,&ptr[size],p_size-size);
			if ( er <= 0 )
				break;
			size += er;
		}
		s_close(st);
		ret = List(n_get_symbol(info.content_type),
				get_raw(ptr,size),
				-1);
		d_f_ree(ptr);
	}
	free_HTTP_info(&info);
	return ret;
}


void
session_task()
{
XL_INTERPRETER * xli;
SESSION_HTTP_N * n;
L_CHAR * key;

	key = touch_qkey(&session_http_que);
	if ( key == 0 )
		return;

	xli = new_xl_interpreter();
	xli->a_type = XLA_SELF;
	setup_i(xli);

	for ( ; ; ) {
		gc_push(0,0,"session_task");
		n = delete_queue(&session_http_que,
			sq_key_cond,
			key,0);
		if ( n == 0 ) {
			gc_pop(0,0);
			break;
		}
		set_d_sexp(n->d,read_reply(n->st,n->f,n->ln));
		gc_pop(0,0);
	}

	release_qkey(&session_http_que,key);
	d_f_ree(key);

	close_self_interpreter();
}

XL_SEXP *
one_session(URL * u,XL_SEXP * s,XL_FILE * f,int ln,int wait_flag)
{
XL_SEXP * ret,*ss;
STREAM * st;
int cerr;
XL_SEXP * method;
L_CHAR * _method;
XL_SYM_FIELD * sf;
XL_SEXP * path;
XL_SEXP * send_data;
int send_size;
char * send_ptr;
int send_free;
CODE_METHOD * cm;
L_CHAR * encode;
STREAM * out;
int _len;
D_SEXP * d;
SESSION_HTTP_N * n;
	method = car(s);
	if ( get_type(method) != XLT_SYMBOL ) {
		ret = get_error(
			f,
			ln,
			XLE_SYNTAX_INVALID_ENTITY,
			l_string(std_cm,"RemoteSession"),
			List(n_get_string("method error"),
				method,-1));
		goto end;
	}
	_method = get_method(method->symbol.data);

	cm = 0;
	send_data = 0;
	send_ptr = 0;
	send_size = 0;
	send_free = 0;
	if ( list_length(s) >= 2 ) {
		ss = s;
		switch ( get_type(get_el(ss,1)) ) {
		case XLT_STRING:
		case XLT_NULL:
			ss = cdr(cdr(ss));
			break;
		default:
			ss = cdr(ss);
			break;
		}
		if ( get_type(ss) == XLT_PAIR ) {
			send_data = car(ss);
			switch ( get_type(send_data) ) {
			case XLT_RAW:
				send_free = 0;
				send_ptr = send_data->raw.data;
				send_size = send_data->raw.size;
				break;
			case XLT_NULL:
				break;
			default:
				encode = get_sf_attribute(
						method->symbol.field,
						l_string(std_cm,"encode"));
				if ( encode == 0 )
					cm = std_cm;
				else {
					cm = get_encoding(encode);
					if ( cm == 0 )
						cm = std_cm;
				}
				out = s_open_string_write(&int_cm);
				print_sexp(out,send_data,0);
				s_printf(out,"\n");
				send_ptr = ln_copy_str(cm,s_get_l_string(out));
				send_size = strlen(send_ptr);
				break;
			}
		}
	}


	st = bl_new_connection(
		&cerr,
		n_string(std_cm,u->server),
		0,
		u->port,
		0,0);
	if ( st == 0 ) {
		ret = get_error(
			f,
			ln,
			XLE_PROTO_OPEN_FILE,
			l_string(std_cm,"RemoteSession"),
			List(n_get_string("cannot open the HTTP connection"),
				get_string(get_url_str2(u)),
				-1));
		goto end;
	}
	encode = get_sf_attribute(method->symbol.field,
				  l_string(std_cm,"encode"));
	cm = get_encoding(encode);
	if ( cm )
		s_set_cm(st,cm);
	if ( list_length(s) == 1 ) {
		s_printf(st,"%ls %ls HTTP/1.0\r\n",
			_method,
			get_url_str2(u));
	}
	else {
		path = get_el(s,1);
		switch ( get_type(path) ) {
		case XLT_STRING:
			s_printf(st,"%ls %ls HTTP/1.0\r\n",
				_method,
				path->string.data);
			break;
		case XLT_NULL:
			s_printf(st,"%ls %ls HTTP/1.0\r\n",
				_method,
				get_url_str2(u));
			break;
		default:
			s_printf(st,"%ls %ls HTTP/1.0\r\n",
				_method,
				get_url_str2(u));
			break;
		}
	}
	if ( cm ) {
		s_printf(st,"Encoding: %s\r\n",
			cm->name);
	}
	for ( sf = method->symbol.field ; sf ; sf  = sf->next ) {
		if ( sf->name[0] != '*' )
			continue;
		s_printf(st,"%ls: %ls\r\n",
			&sf->name[1],
			sf->data);
	}
	s_printf(st,"Connection: close\r\n");
	s_printf(st,"Host: %s:%i\r\n",u->server,u->port);
	s_printf(st,"\r\n");
	if ( send_ptr ) {
		en_do(&_len,s_write,st,send_ptr,send_size);
		if ( send_free )
			d_f_ree(send_ptr);
	}
	if ( wait_flag ) {
		ret = read_reply(st,f,ln);
	}
	else {
	char buf[3];
	static int cnt;
		ret = new_d_sexp(&d);

		n = new_queue_node(sizeof(*n));
		sprintf(buf,"%i",cnt++);
		if ( cnt >= 5 )
			cnt = 0;
		n->h.key = nl_copy_str(std_cm,buf);
		n->st = st;
		n->f = f;
		n->ln = ln;
		n->d = d;
		insert_queue(&session_http_que,n,0);
	}
end:
	d_f_ree(_method);
	return ret;
}


XL_SEXP *
remote_session_http(
	XLISP_ENV * env,
	int id,
	URL * u,
	L_CHAR * _a_agent,
	L_CHAR * _a_login_mode,
	L_CHAR * _a_center_cmd,
	XL_SEXP * cmd,
	XL_FILE * f,
	int ln,
	int session_lock_enable,
	REMOTE_SESSION_OPT * opt)
{
	for ( ; get_type(cmd) == XLT_PAIR ; ) {
		if ( get_type(cdr(cmd)) == XLT_NULL ) {
			return one_session(u,car(cmd),f,ln,0);
		}
		else {
			one_session(u,car(cmd),f,ln,1);
		}
	}
	return 0;
}



