#ifdef __cplusplus
extern "C" {
#endif

#define WIN32_LEAN_AND_MEAN
#define WIN32IO_IS_STDIO
#define EXT
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <process.h>

#include "win32iop.h"

//
// The following is just a basic wrapping of the stdio
//
//
//  redirected io subsystem for all XS modules
//
//

static int * dummy_errno()
{
	return (&(errno));
}

static FILE *dummy_stderr()
{
	return stderr;
}

static FILE *dummy_stdin()
{
	return stdin;
}

static FILE *dummy_stdout()
{
	return stdout;
}

static int dummy_globalmode(int mode)
{
	int o = _fmode;
	_fmode = mode;

	return o;
}


#if defined(_WIN32) && !defined(WIN95_OSFHANDLE_FIXED) && defined(_M_IX86)

#	ifdef __cplusplus
#define EXT_C_FUNC	extern "C"
#	else
#define EXT_C_FUNC	extern
#	endif

EXT_C_FUNC int __cdecl _alloc_osfhnd(void);
EXT_C_FUNC int __cdecl _set_osfhnd(int fh, long value);
EXT_C_FUNC void __cdecl _lock_fhandle(int);
EXT_C_FUNC void __cdecl _unlock_fhandle(int);
EXT_C_FUNC void __cdecl _unlock(int);

#if	(_MSC_VER >= 1000)
	typedef struct
	{
		long osfhnd;    /* underlying OS file HANDLE */
		char osfile;    /* attributes of file (e.g., open in text mode?) */
		char pipech;    /* one char buffer for handles opened on pipes */
#if defined (_MT) && !defined (DLL_FOR_WIN32S)
        int lockinitflag;
        CRITICAL_SECTION lock;
#endif  /* defined (_MT) && !defined (DLL_FOR_WIN32S) */
	}	ioinfo;

	EXT_C_FUNC ioinfo * __pioinfo[];

	#define IOINFO_L2E			5
	#define IOINFO_ARRAY_ELTS	(1 << IOINFO_L2E)
	#define _pioinfo(i)	(__pioinfo[i >> IOINFO_L2E] + (i & (IOINFO_ARRAY_ELTS - 1)))
	#define _osfile(i)	(_pioinfo(i)->osfile)
#else
	extern "C" extern char _osfile[];
#endif	// (_MSC_VER >= 1000)

#define FOPEN			0x01	// file handle open
#define FAPPEND			0x20	// file handle opened O_APPEND
#define FDEV			0x40	// file handle refers to device
#define FTEXT			0x80	// file handle is in text mode

#define _STREAM_LOCKS   26		// Table of stream locks
#define _LAST_STREAM_LOCK  (_STREAM_LOCKS+_NSTREAM_-1)	// Last stream lock
#define _FH_LOCKS          (_LAST_STREAM_LOCK+1)		// Table of fh locks

/***
*int _patch_open_osfhandle(long osfhandle, int flags) - open C Runtime file handle
*
*Purpose:
*       This function allocates a free C Runtime file handle and associates
*       it with the Win32 HANDLE specified by the first parameter. This is a
*		temperary fix for WIN95's brain damage GetFileType() error on socket
*		we just bypass that call for socket
*
*Entry:
*       long osfhandle - Win32 HANDLE to associate with C Runtime file handle.
*       int flags      - flags to associate with C Runtime file handle.
*
*Exit:
*       returns index of entry in fh, if successful
*       return -1, if no free entry is found
*
*Exceptions:
*
*******************************************************************************/

int my_open_osfhandle(long osfhandle, int flags)
{
	int fh;
	char fileflags;		// _osfile flags 

	// copy relevant flags from second parameter 
	fileflags = FDEV;

	if(flags & _O_APPEND)
		fileflags |= FAPPEND;

	if(flags & _O_TEXT)
		fileflags |= FTEXT;

	// attempt to allocate a C Runtime file handle
	if((fh = _alloc_osfhnd()) == -1)
	{
		errno = EMFILE;		// too many open files 
		_doserrno = 0L;		// not an OS error
		return -1;			// return error to caller
	}

	// the file is open. now, set the info in _osfhnd array
	_set_osfhnd(fh, osfhandle);

	fileflags |= FOPEN;			// mark as open

#if (_MSC_VER >= 1000)
	_osfile(fh) = fileflags;	// set osfile entry
	_unlock_fhandle(fh);
#else
	_osfile[fh] = fileflags;	// set osfile entry
	_unlock(fh+_FH_LOCKS);		// unlock handle
#endif


	return fh;					// return handle
}
#else
int __cdecl stolen_open_osfhandle(long osfhandle, int flags)
{
	return _open_osfhandle(osfhandle, flags);
}
#endif	// _M_IX86

long my_get_osfhandle( int filehandle )
{
	return _get_osfhandle(filehandle);
}

WIN32_IOSUBSYSTEM	win32stdio = {
	12345678L,		//	begin of structure;
	dummy_errno,	//	(*pfunc_errno)(void);
	dummy_stdin,	//	(*pfunc_stdin)(void);
	dummy_stdout,	//	(*pfunc_stdout)(void);
	dummy_stderr,	//	(*pfunc_stderr)(void);
	ferror,			//	(*pfunc_ferror)(FILE *fp);
	feof,			//	(*pfunc_feof)(FILE *fp);
	strerror,		//	(*strerror)(int e);
	vfprintf,		//	(*pfunc_vfprintf)(FILE *pf, const char *format, va_list arg);
	vprintf,		//	(*pfunc_vprintf)(const char *format, va_list arg);
	fread,			//	(*pfunc_fread)(void *buf, size_t size, size_t count, FILE *pf);
	fwrite,			//  (*pfunc_fwrite)(void *buf, size_t size, size_t count, FILE *pf);
	fopen,			//	(*pfunc_fopen)(const char *path, const char *mode);
	fdopen,			//	(*pfunc_fdopen)(int fh, const char *mode);
	freopen,		//	(*pfunc_freopen)(const char *path, const char *mode, FILE *pf);
	fclose,			//	(*pfunc_fclose)(FILE *pf);
	fputs,			//  (*pfunc_fputs)(const char *s,FILE *pf);
	fputc,			//  (*pfunc_fputc)(int c,FILE *pf);
	ungetc,			//  (*pfunc_ungetc)(int c,FILE *pf);
	getc,			//  (*pfunc_getc)(FILE *pf);
	fileno,			//	(*pfunc_fileno)(FILE *pf);
	clearerr,		//  (*pfunc_clearerr)(FILE *pf);
	fflush,			//	(*pfunc_fflush)(FILE *pf);
	ftell,			//	(*pfunc_ftell)(FILE *pf);
	fseek,			//  (*pfunc_fseek)(FILE *pf,long offset,int origin);
	fgetpos,		//	(*pfunc_fgetpos)(FILE *pf,fpos_t *p);
	fsetpos,		//	(*pfunc_fsetpos)(FILE *pf,fpos_t *p);
	rewind,			//	(*pfunc_rewind)(FILE *pf);
	tmpfile,		//	(*pfunc_tmpfile)(void);
	abort,			//	(*pfunc_abort)(void);
	fstat,  		//  (*pfunc_fstat)(int fd,struct stat *bufptr);
	stat,			// 	(*pfunc_stat)(const char *name,struct stat *bufptr);
	_pipe,			//	(*pfunc_pipe)( int *phandles, unsigned int psize, int textmode );
	_popen,			//	(*pfunc_popen)( const char *command, const char *mode );
	_pclose,		//	(*pfunc_pclose)( FILE *pf);
	setmode,		//  (*pfunc_setmode)( int fd, int mode);
	lseek,			//	(*pfunc_lseek)( int fd, long offset, int origin);
	tell,			//	(*pfunc_tell)( int fd);
	dup,			//	(*pfunc_dup)( int fd);
	dup2,			//	(*pfunc_dup2)(int h1, int h2);
	open,			//	(*pfunc_open)(const char *path, int oflag,...);
	close,			//	(*pfunc_close)(int fd);
	eof,			//	(*pfunc_eof)(int fd);
	read,			//	(*pfunc_read)(int fd, void *buf, unsigned int cnt);
	write,			//	(*pfunc_write)(int fd, const void *buf, unsigned int cnt);
	dummy_globalmode,//	(*pfunc_globalmode)(int mode)
	my_open_osfhandle,
	my_get_osfhandle,
	spawnvpe,
	87654321L,		//  end of structure
};


#ifdef __cplusplus
}
#endif