/**********************************************************************
 
	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.

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


#define STREAM_LIB
#define LIBRARY
#define S_FILE_LIBRARY

#include	<stdlib.h>
#include	<fcntl.h>
#ifdef VA2
#include	<varargs.h>
#else
#include	<stdarg.h>
#endif
#include	<errno.h>

#include	"memory_debug.h"
#include	"stream.h"
#include	"task.h"
#include	"machine/include.h"
#include	<sys/types.h>
#include <io.h>
#include <fcntl.h>


extern SEM stream_lock;
STREAM * s_error_stream();
int s_close_fpipe();
int s_write_fpipe();
int s_read_fpipe();
int s_flush_fpipe();
int s_proc_send_fpipe();
STREAM * s_proc_recv_fpipe();
STREAM * s_error_stream();

S_TABLE s_fpipe_table = {
	'P',
	0,
	s_error_stream,
	s_close_fpipe,
	s_write_fpipe,
	s_read_fpipe,
	s_flush_fpipe,
	s_proc_send_fpipe,
	s_proc_recv_fpipe
};

void
init_flush_pipe_stream()
{
	insert_s_table(&s_fpipe_table);
}

int
s_open_fpipe(STREAM * p[2])
{
int pfd[2];

/*
	if ( pipe(pfd) < 0 )
		return -1;
*/
	if( _pipe( pfd, 256, O_BINARY ) == -1 )
          return -1;

	errno = 0;
	p[0] = d_alloc(sizeof(S_FPIPE),134);
	p[0]->h.tbl = &s_fpipe_table;
	p[0]->h.thread = 0;
	p[0]->fpipe.fid = pfd[0];
	p[0]->fpipe.head = p[0]->fpipe.tail = 0;
	p[0]->fpipe.read_flag = 0;

	p[1] = d_alloc(sizeof(S_FPIPE),134);
	p[1]->h.tbl = &s_fpipe_table;
	p[1]->h.thread = 0;
	p[1]->fpipe.fid = pfd[1];
	p[1]->fpipe.head = p[1]->fpipe.tail = 0;
	p[1]->fpipe.read_flag = 0;

	lock_task(stream_lock);
	_s_open(p[0],O_RDONLY);
	_s_open(p[1],O_RDWR);
	unlock_task(stream_lock,"s_open_file");
	return 0;
}


int
s_close_fpipe(STREAM * s)
{
PIPE_READ_BUF * b;
	_s_close_sync(s);
	close(s->fpipe.fid);
	for ( ; s->fpipe.head ; ) {
		b = s->fpipe.head;
		s->fpipe.head = b->next;
		d_f_ree(b);
	}
	s->fpipe.tail = 0;
	return 0;
}

int
rw_loop(int (*func)(),STREAM * s,int fid,void * data,int len)
{
int ret;
char * ptr;
int _ret;
	ptr = data;
	_ret = 0;
	for ( ; len ; ) {
		errno = 0;
		ret = (*func)(fid,ptr,len);
		if ( ret <= 0 ) {
			_ret = -1;
			break;
		}
		len -= ret;
		ptr += ret;
		_ret += ret;
	}
	return _ret;
}



int
s_write_fpipe(STREAM * s,void * data,int len)
{
int ret;
char * ptr;
int _len;
	if ( len == 0 )
		return 0;
	lock_task(stream_lock);
	errno = 0; 
retry1:
	ret = rw_loop(write,s,s->fpipe.fid,&len,sizeof(len));
	if ( ret != sizeof(len) ) {
		ret = -1;
		goto end;
	}
	ret = rw_loop(write,s,s->fpipe.fid,data,len);
end:
	unlock_task(stream_lock,"s_write_fpip");

	s->h.last_size = len;
	s->h.last_err = errno;
	return ret;
}


int
read_phy_pipe(STREAM * s)
{
int len;
int ret;
PIPE_READ_BUF * b;
char * ptr;

	ret = rw_loop(read,s,s->fpipe.fid,&len,sizeof(len));
	if ( ret != sizeof(len) )
		return -1;
	if ( len < 0 )
		return 1;
	b = d_alloc(sizeof(*b)+len,123);
	ptr = (char*)(b+1);
	b->ptr = 0;
	b->len = len;
	ret = rw_loop(read,s,s->fpipe.fid,ptr,len);
	if ( ret != len ) {
		d_f_ree(b);
		return -1;
	}
	b->next = 0;
	if ( s->fpipe.head ) {
		s->fpipe.tail->next = b;
		s->fpipe.tail = b;
	}
	else	s->fpipe.tail = s->fpipe.head = b;
	return 0;
}

int
copy_out_head(STREAM * s,void * data,int len)
{
char * ptr;
PIPE_READ_BUF * b;
int copy_size;
int ret;
	ptr = data;
	ret = 0;
	for ( ; ; ) {
		if ( len == 0 )
			break;
		if ( s->fpipe.head == 0 )
			break;
		b = s->fpipe.head;
		if ( b->len - b->ptr > len ) {
			memcpy(ptr,((char*)(b+1))+b->ptr,len);
			b->ptr += len;
			ptr += len;
			ret += len;
			len = 0;
		}
		else {
			copy_size = b->len - b->ptr;
			memcpy(ptr,((char*)(b+1))+b->ptr,copy_size);
			ptr += copy_size;
			len -= copy_size;
			ret += copy_size;

			s->fpipe.head = b->next;
			d_f_ree(b);
			if ( s->fpipe.head == 0 )
				s->fpipe.tail = 0;
		}
	}
	return ret;
}

int
s_read_fpipe(STREAM * s,void * data,int len)
{
int ret;
	lock_task(stream_lock);
	for ( ; s->fpipe.read_flag ; ) {
		sleep_task((int)s,stream_lock);
		lock_task(stream_lock);
	}
	unlock_task(stream_lock,"s_read_fpipe");
	if ( len == 0 ){
		ret = 0;
		goto end;
	}
	if ( s->fpipe.head ) {
		ret = copy_out_head(s,data,len);
		goto end;
	}
	for ( ; ; ) {
		switch ( read_phy_pipe(s) ) {
		case 1:
			continue;
		case 0:
			break;
		case -1:
			ret = -1;
			goto end;
		default:
			er_panic("s_read_pipe");
		}
		break;
	}
	ret = copy_out_head(s,data,len);
end:
	lock_task(stream_lock);
	s->fpipe.read_flag = 0;
	wakeup_task((int)s);
	unlock_task(stream_lock,"s_read_fpipe");
	return ret;
}

int
s_flush_fpipe(STREAM * s)
{
int len;
int ret;
	len = -1;
	lock_task(stream_lock);
	ret = rw_loop(write,s,s->fpipe.fid,&len,sizeof(len));
	unlock_task(stream_lock,"s_flush_fpipe");
	if ( ret < 0 )
		return -1;
	return 0;
}

int
s_proc_send_fpipe(STREAM * s,PROC_SEND * ps)
{
	if ( s->fpipe.head )
		return -1;
	ps->msg[0] = s->h.tbl->type;
	ps->msg[1] = 0;
	ps->fid[0] = s->fpipe.fid;
	ps->fid[1] = -1;
	return 0;
}


STREAM *
s_proc_recv_fpipe(PROC_RECV * pr)
{
int fid;
STREAM * ret;
int mode;

	pr->ptr ++;

	ret->fpipe.fid = fid;	fid = pr->fidp ++;


/*	mode = fcntl(fid,F_GETFL);
	if ( mode < 0 )
		return 0;
	mode &= ~3;
	fcntl(fid,F_SETFL,mode);
*/
	ret = d_alloc(sizeof(S_FPIPE),133);
	ret->h.tbl = pr->tbl;
	ret->h.thread = 0;
	ret->fpipe.fid = fid;
	ret->fpipe.head = ret->fpipe.tail = 0;
	ret->fpipe.read_flag = 0;

	_s_open(ret,mode);
	return ret;
}


