/**********************************************************************
 
	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	"lock_level.h"
#include	"pri_level.h"
#include	"task.h"
#include	"stream.h"
#include	"s_buf.h"
#include	"xl.h"
#include	"XLoHTTP.h"

#define DATA_SIZE	10000

void download_task();
void upload_task();

typedef struct XoH_entry_v2 {
	struct XoH_entry_v2 *	next;
	int			flags;
#define XoH_ET_DOWNLOAD_CLOSE	0x00000001
#define XoH_ET_UPLOAD_CLOSE	0x00000002
#define XoH_ET_CLOSE	(XoH_ET_DOWNLOAD_CLOSE|XoH_ET_UPLOAD_CLOSE)
	STREAM *		upload;
	STREAM *		download;
	STREAM *		pipe;
	unsigned int		cockie[2];
	int			remain_size;
} XoH_ENTRY_V2;

extern SEM	XoH_lock;

XoH_ENTRY_V2 * _new_entry_v2();
XoH_ENTRY_V2 * _search_entry_v2(unsigned int * cockie);
void _delete_entry_v2(XoH_ENTRY_V2 * e);
void close_all(XoH_ENTRY_V2 * e);

XoH_ENTRY_V2 * entry_list_v2;

XoH_ENTRY_V2 *
_new_entry_v2()
{
XoH_ENTRY_V2 * ret;
	ret = d_alloc(sizeof(*ret));
	memset(ret,0,sizeof(*ret));
	ret->next = entry_list_v2;
	entry_list_v2 = ret;
	return ret;
}

XoH_ENTRY_V2 *
_search_entry_v2(unsigned int * cockie)
{
XoH_ENTRY_V2 * ret;
	for ( ret = entry_list_v2 ; ret ; ret = ret->next )
		if ( ret->cockie[0] == cockie[0] &&
			ret->cockie[1] == cockie[1] )
			return ret;
	return 0;
}

void
_delete_entry_v2(XoH_ENTRY_V2 * e)
{
XoH_ENTRY_V2 ** ep;
	for ( ep = &entry_list_v2 ; *ep && *ep != e ; ep = &(*ep)->next );
	if ( *ep == 0 )
		return;
	*ep = e->next;

	d_f_ree(e);
}

void
close_all(XoH_ENTRY_V2 * e)
{
STREAM * u,* d, * p;
	u = e->upload;
	d = e->download;
	p = e->pipe;

	e->upload = 0;
	e->download = 0;
	e->pipe = 0;

	s_close(u);
	s_close(d);
	s_close(p);
}

int
new_accept_2(STREAM ** new_st,ACCESS_KEY * k)
{
STREAM * p[2];
XoH_ENTRY_V2 * e;
XLoHTTP_HEADER h;
HTTP_INFO o_info;
int ret_len;
	lock_task(XoH_lock);
	e = _new_entry_v2();
	_new_cockie(e->cockie);
	unlock_task(XoH_lock,"new_accept_2");
	e->flags = XoH_ET_UPLOAD_CLOSE;
	e->upload = 0;
	e->download = k->s;
	s_open_fpipe(p);
	e->pipe = p[0];
	s_set_fpipe_ip_addr(e->pipe,k->ip);
	*new_st = p[1];

	create_task(download_task,(int)e,PRI_WRITE_BUF);

	h.type = XoH_T_DOWNLOAD_SESSION;

	h.sum = 0;
	h.seq = 0;
	memcpy(h.cockie,e->cockie,sizeof(e->cockie));
	change_endian_XLoHTTP_HEADER(&h);
	h.sum = XoH_checksum(&h);

	o_info.flags = 0;
	o_info.content_length = -1;

	output_server_header(e->download,&o_info);
	en_do(&ret_len,s_write,e->download,&h,sizeof(h));

	return 1;
}


int
new_upload_session(XoH_LIST * lst)
{
XoH_ENTRY_V2 * e;
	e = _search_entry_v2(lst->h.cockie);
	if ( e == 0 ) {
		free_HTTP_info(&lst->info);
		d_f_ree(lst);
		return -1;
	}
	if ( e->upload ) {
		free_HTTP_info(&lst->info);
		d_f_ree(lst);
		return -1;
	}
	e->upload = lst->con;
	e->remain_size = lst->info.content_length - sizeof(lst->h);
	e->flags &= ~XoH_ET_UPLOAD_CLOSE;
	create_task(upload_task,(int)e,PRI_WRITE_BUF);
	return 0;
}


void
download_task(TKEY d)
{
XoH_ENTRY_V2 * e;
char * data;
int ret,ret_len;
	data = d_alloc(DATA_SIZE);
	e = (XoH_ENTRY_V2*)GET_TKEY(d);
	for ( ; ; ) {
		ret = s_read(e->pipe,data,DATA_SIZE);
		if ( ret <= 0 ) {
			close_all(e);
			break;
		}
		if ( en_do(&ret_len,s_write,e->download,data,ret) <= 0 ) {
			close_all(e);
			break;
		}
	}
	d_f_ree(data);
	lock_task(XoH_lock);
	e->flags |= XoH_ET_DOWNLOAD_CLOSE;
	if ( (e->flags & XoH_ET_CLOSE) == XoH_ET_CLOSE )
		_delete_entry_v2(e);
	unlock_task(XoH_lock,"download_task");
}


void
upload_task(TKEY d)
{
XoH_ENTRY_V2 * e;
char * data;
int ret,ret_len;
int siz;
XLoHTTP_HEADER h;
HTTP_INFO o_info;
int rem_size;
	data = d_alloc(DATA_SIZE);
	e = (XoH_ENTRY_V2*)GET_TKEY(d);
	for ( ; ; ) {
		siz = DATA_SIZE < e->remain_size ? DATA_SIZE :
				e->remain_size;
		ret = s_read(e->upload,data,siz);
		if ( ret <= 0 ) {
			close_all(e);
			break;
		}
		e->remain_size -= ret;
		rem_size = e->remain_size;
		if ( rem_size == 0 ) {
			h.type = XoH_T_UPLOAD_SESSION;
			h.sum = 0;
			h.seq = 0;
			memcpy(h.cockie,e->cockie,sizeof(e->cockie));
			change_endian_XLoHTTP_HEADER(&h);
			h.sum = XoH_checksum(&h);

			o_info.flags = 0;
			o_info.content_length = -1;

			output_server_header(e->upload,&o_info);
			en_do(&ret_len,s_write,e->upload,&h,sizeof(h));

			s_close(e->upload);
			e->upload = 0;
		}
		if ( en_do(&ret_len,s_write,e->pipe,data,ret) <= 0 ) {
			close_all(e);
			break;
		}
		if ( rem_size == 0 )
			break;
	}
	d_f_ree(data);
	lock_task(XoH_lock);
	e->flags |= XoH_ET_UPLOAD_CLOSE;
	if ( (e->flags & XoH_ET_CLOSE) == XoH_ET_CLOSE )
		_delete_entry_v2(e);
	unlock_task(XoH_lock,"download_task");
}


