/**********************************************************************
 
	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	"utils.h"
#include	<sys/types.h>
#include	"machine/stream.h"


extern SEM stream_lock;
int max_fid = 3;
int now_open_files = 4;

int s_close_file();
int s_write_file();
int s_read_file();
int s_vprintf_file();
int s_vprintf_cr_file();
int s_flush_file();
int s_vscanf_file();
int s_proc_send_file();
STREAM * s_proc_recv_file();
STREAM * _s_open_file_descripter();

S_TABLE s_file_table = {
	'f',
	0,
	{0,0},
	NULL,
	/* In windows, we can not get open-mode from descripter, so don't use s_open_file_descripter */
	_s_open_file_descripter,
	s_close_file,
	s_write_file,
	s_read_file,
	s_flush_file,
	s_proc_send_file,
	s_proc_recv_file
};


S_FILE __s_std[3] = {
	{	
		{	&s_file_table,
			O_RDONLY
		},
		0
	},
	{	
		{	&s_file_table,
			O_WRONLY
		},
		0
	},
	{	
		{	&s_file_table,
			O_WRONLY
		},
		0
	}
};



void
init_file_stream()
{
void insert_s_table(S_TABLE * tbl);

	__s_std[0].hfile = GetStdHandle(STD_INPUT_HANDLE);
	__s_std[1].hfile = GetStdHandle(STD_OUTPUT_HANDLE);
	__s_std[2].hfile = GetStdHandle(STD_ERROR_HANDLE);
	
	insert_s_table(&s_file_table);
}

STREAM *_s_open_file_descripter(HANDLE hfile, S_TABLE * tbl){
	printf("WARNING: stream who can not resolve open mode opened.");
	return s_open_file_descripter(hfile, O_RDWR, TRUE);	
}


STREAM *
s_open_file_descripter(HANDLE hfile, int open_mode, BOOL do_lock)
{
STREAM * ret;

	
	if ( hfile == INVALID_HANDLE_VALUE)
		return 0; 
	
	ret = d_alloc(sizeof(S_FILE));
	ret->h.tbl = &s_file_table;
	ret->h.thread = 0;
	ret->file.hfile= hfile;
	
	if(do_lock)
		lock_task(stream_lock);
	_s_open(ret,open_mode);
	if(do_lock)
		unlock_task(stream_lock,"s_open_file_descripter");
	return ret;
}

STREAM *
s_open_file(char * filename,int mode,int flags)
{
STREAM * ret;
DWORD access_mode;
DWORD create_disposition;
char * _f;
int retry_count;
	
	errno = 0;
	ret = d_alloc(sizeof(S_FILE));
	ret->h.tbl = &s_file_table;
	ret->h.thread = 0;
	access_mode = GENERIC_READ;
	if (mode & O_RDWR)
		access_mode = GENERIC_WRITE|GENERIC_READ;
	
	if( (mode & O_CREAT) || (mode & O_RDWR) || (mode & O_WRONLY)){
		if(mode & O_APPEND){
			create_disposition = OPEN_ALWAYS;
		}
		else{
			create_disposition = CREATE_ALWAYS;
		}
	}
	else if(mode & O_TRUNC){
		create_disposition = TRUNCATE_EXISTING;
	}
	else{
		create_disposition = OPEN_EXISTING;
	}
	_f = change_delim_str(filename);
	ret->file.h.mode = mode;
	if(strcmp(_f, "&")==0){
		er_panic("invalid filename");
	}

	retry_count = 300;
	do{
		ret->file.hfile = CreateFile(_f, 
						access_mode, 
						FILE_SHARE_READ, 0, 
						create_disposition, 
						FILE_ATTRIBUTE_NORMAL, NULL);
		
		if ( ret->file.hfile == INVALID_HANDLE_VALUE) {
			int errcode = GetLastError();
			if(errcode == 32){
				Sleep(100);
			}
			else{
				d_f_ree(ret);
				return 0;
			}
		}
		else{
			break;
		}
	}while(--retry_count);
	if(retry_count==0){
int errcode = GetLastError();
er_panic("err");
			
		d_f_ree(ret);
		return 0;
	}
	
	if ( _f != filename )
		d_f_ree(_f);

	if(mode & O_APPEND){
		SetFilePointer(ret->file.hfile, 0, 0, FILE_END);
	}


	lock_task(stream_lock);
	_s_open(ret,mode);
	unlock_task(stream_lock,"s_open_file");
	return ret;
}


int
s_close_file(STREAM * s)
{
	_s_close_sync(s);
	CloseHandle(s->file.hfile);
	return 0;
}

int
s_write_file(STREAM * s,void * data,int len)
{
DWORD wrote=0;
	if(s->file.hfile != INVALID_HANDLE_VALUE)
		WriteFile(s->file.hfile, data, len, &wrote, NULL );
	s->h.last_size = len;
	s->h.last_err = GetLastError();
	return (int)wrote;
}

int
s_read_file(STREAM * s,void * data,int len)
{
DWORD read=0;
	if(s->file.hfile != INVALID_HANDLE_VALUE){
		if(ReadFile(s->file.hfile, data, len, &read, NULL)){
			/* 
			if(read == 0){
				if(GetLastError()==ERROR_HANDLE_EOF);
					return -1;
			}
			*/
		}
	}
	return (int)read;
}

int
s_flush_file(STREAM * s)
{
	FlushFileBuffers(s->file.hfile);
	return 0;
}




int
s_seek_file(STREAM * s,unsigned int offset,int where)
{
DWORD origin;
	if ( s == 0 )
		return 0;
	if ( s->h.tbl != &s_file_table )
		return -1;
	origin = 0;
	switch(where){
		case SEEK_SET: origin=FILE_BEGIN; break;
		case SEEK_CUR: origin=FILE_CURRENT; break;
		case SEEK_END: origin=FILE_END; break;
	}

	return SetFilePointer(s->file.hfile, offset, NULL, origin);
}


int
s_proc_send_file(STREAM * s, PROC_SEND_CHUNK** chunk, HANDLE hTargetProcess)
{
	int flags;
	PROC_SEND_FILE *ps = (PROC_SEND_FILE *)d_alloc(sizeof(PROC_SEND_FILE));
/* DEBUG */
	er_panic("s_proc_send_file");
	
	ps->header.size = sizeof(PROC_SEND_FILE);
	ps->header.type = s->h.tbl->type;
	ps->open_mode = s->h.mode;
	
	flags = DUPLICATE_SAME_ACCESS;

	if(!DuplicateHandle(
		GetCurrentProcess(), 
		s->file.hfile, 
		hTargetProcess, 
		&ps->hfile,
		0,
		FALSE,
		flags)){
		er_panic("s_proc_send_file DuplicateHandle error");
	}
	
	*chunk = (PROC_SEND_CHUNK*)ps;
	
	return 0;
}


STREAM *
s_proc_recv_file(S_TABLE *table, PROC_SEND_CHUNK* chunk)
{
	PROC_SEND_FILE *ps = (PROC_SEND_FILE *)chunk;
	return s_open_file_descripter(ps->hfile, ps->open_mode, FALSE);
}

