/* Copyright (C) 2001 by Alex Kompel <shurikk@pacbell.net> */
/* NetHack may be freely redistributed.  See license for details. */

#define NEED_VARARGS
#include "hack.h"
#include <fcntl.h>
// #include "wceconf.h"

static union {
	time_t t_val;
	struct time_pack {
		unsigned int ss:6;
		unsigned int mm:6;
		unsigned int dd:5;
		unsigned int hh:6;
		unsigned int mo:4;
		unsigned int yr:10;
		unsigned int wd:3;
	} tm_val;
} _t_cnv;

#define IS_LEAP(yr) (((yr)%4==0 || (yr)%100==0) && !(yr)%400==0)
static char _day_mo_leap[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static char _day_mo[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

struct tm * __cdecl localtime ( const time_t *ptime )
{
	static struct tm ptm;
	int i;
	if( !ptime ) return NULL;

	_t_cnv.t_val = *ptime;

    ptm.tm_sec		= _t_cnv.tm_val.ss ;     /* seconds after the minute - [0,59] */
    ptm.tm_min		= _t_cnv.tm_val.mm;     /* minutes after the hour - [0,59] */
    ptm.tm_hour	= _t_cnv.tm_val.hh;    /* hours since midnight - [0,23] */
    ptm.tm_mday	= _t_cnv.tm_val.dd;    /* day of the month - [1,31] */
    ptm.tm_mon		= _t_cnv.tm_val.mo-1;     /* months since January - [0,11] */
    ptm.tm_year	= _t_cnv.tm_val.yr;    /* years since 1900 */
    ptm.tm_wday	= _t_cnv.tm_val.wd;    /* days since Sunday - [0,6] */

	ptm.tm_yday = _t_cnv.tm_val.dd;    /* days since January 1 - [0,365] */
	for( i=0; i<ptm.tm_mon; i++ )
		ptm.tm_yday += IS_LEAP(_t_cnv.tm_val.yr+1900)?_day_mo_leap[i] : _day_mo[i] ; 

	ptm.tm_isdst = 0;   /* daylight savings time flag  - NOT IMPLEMENTED */
	return &ptm;
}

time_t __cdecl time(time_t * timeptr)
{
	SYSTEMTIME stm;
	GetLocalTime(&stm);

	_t_cnv.tm_val.yr = stm.wYear-1900; 
	_t_cnv.tm_val.mo = stm.wMonth; 
	_t_cnv.tm_val.dd = stm.wDay; 
	_t_cnv.tm_val.hh = stm.wHour; 
	_t_cnv.tm_val.mm = stm.wMinute; 
	_t_cnv.tm_val.ss = stm.wSecond; 
	_t_cnv.tm_val.wd = stm.wDayOfWeek; 

	if( timeptr) 
		*timeptr = _t_cnv.t_val;
	return _t_cnv.t_val;
}

/*------------------------------------------------------------------------------*/
/* __io.h__ */
/* Hack io.h function with stdio.h functions */
/* ASSUMPTION : int can hold FILE* */
static TCHAR _nh_cwd[MAX_PATH];
const int MAGIC_OFFSET=5;
#define FILE_TABLE_SIZE  256
static HANDLE _nh_file_table[FILE_TABLE_SIZE];
static int file_pointer = -1;

static HANDLE get_file_handle(int i) 
{
	i -= MAGIC_OFFSET;
	if( i>=0 && i<FILE_TABLE_SIZE )
		return _nh_file_table[i];
	else 
		return INVALID_HANDLE_VALUE;
} 

static int alloc_file_handle(HANDLE h) 
{
	int i;
	if( file_pointer==-1 ) {
		file_pointer=0;
		for(i=0; i<FILE_TABLE_SIZE; i++ )
			_nh_file_table[i] = INVALID_HANDLE_VALUE;
	}

	i = (file_pointer+1)%FILE_TABLE_SIZE;
	while(_nh_file_table[i]!=INVALID_HANDLE_VALUE ) {
		if( i==file_pointer ) {
			MessageBox(NULL, _T("Ran out of file handles."), _T("Fatal Error"), MB_OK);
			abort();
		}
		i = (i+1) % FILE_TABLE_SIZE;
	}
	
	file_pointer = i;
	_nh_file_table[file_pointer] = h;
	return file_pointer+MAGIC_OFFSET;
} 

int __cdecl close(int f) {
	int retval;
	f -= MAGIC_OFFSET;
	if( f<0 || f>=FILE_TABLE_SIZE )	return -1;
	retval = (CloseHandle(_nh_file_table[f])? 0 : -1);
	_nh_file_table[f] = INVALID_HANDLE_VALUE;
	return retval;
}

int __cdecl creat(const char *fname , int mode)
{
	HANDLE f;
	TCHAR wbuf[MAX_PATH+1];
	ZeroMemory(wbuf, sizeof(wbuf));
	NH_A2W(fname, wbuf, MAX_PATH);

	f = CreateFile(
		wbuf, 
		GENERIC_READ | GENERIC_WRITE,
		0,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if( f==INVALID_HANDLE_VALUE ) return -1;
	else     return alloc_file_handle(f);
}

int __cdecl eof(int f)
{
	DWORD fpos, fsize;
	HANDLE p = get_file_handle(f);

	if( f==-1 ) return -1;

	fpos = SetFilePointer(p, 0, NULL, FILE_CURRENT);
	fsize = SetFilePointer(p, 0, NULL, FILE_END);
	if( fpos==0xFFFFFFFF || fsize==0xFFFFFFFF ) return -1;
	if( fpos==fsize ) return 1;
	else {
		SetFilePointer(p, fpos, NULL, FILE_BEGIN);
		return 0;
	}
}

long __cdecl lseek( int f, long offset, int origin )
{
	HANDLE p = get_file_handle(f);
	DWORD fpos;
	switch(origin) {
	case SEEK_SET:
		fpos = SetFilePointer(p, offset, NULL, FILE_BEGIN);
		break;
	case SEEK_CUR:
		fpos = SetFilePointer(p, offset, NULL, FILE_CURRENT);
		break;
	case SEEK_END:
		fpos = SetFilePointer(p, offset, NULL, FILE_END);
		break;
	default:
		fpos = 0xFFFFFFFF;
		break;
	}
	if( fpos==0xFFFFFFFF ) return -1;
	else return (long)fpos;
}

int __cdecl open( const char *filename, int oflag, ... )
{
	TCHAR fname[MAX_PATH+1];
	TCHAR path[MAX_PATH+1];
    HANDLE f;                       
    DWORD fileaccess;               
    DWORD filecreate;               

	/* O_TEXT is not supported */

    /*
     * decode the access flags
     */
    switch( oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR) ) {

        case _O_RDONLY:         /* read access */
                fileaccess = GENERIC_READ;
                break;
        case _O_WRONLY:         /* write access */
                fileaccess = GENERIC_READ | GENERIC_WRITE;
                break;
        case _O_RDWR:           /* read and write access */
                fileaccess = GENERIC_READ | GENERIC_WRITE;
                break;
        default:                /* error, bad oflag */
                return -1;
    }

    /*
     * decode open/create method flags
     */
    switch ( oflag & (_O_CREAT | _O_EXCL | _O_TRUNC) ) {
        case 0:
        case _O_EXCL:                   // ignore EXCL w/o CREAT
            filecreate = OPEN_EXISTING;
            break;

        case _O_CREAT:
            filecreate = OPEN_ALWAYS;
            break;

        case _O_CREAT | _O_EXCL:
        case _O_CREAT | _O_TRUNC | _O_EXCL:
            filecreate = CREATE_NEW;
            break;

        case _O_TRUNC:
        case _O_TRUNC | _O_EXCL:        // ignore EXCL w/o CREAT
            filecreate = TRUNCATE_EXISTING;
            break;

        case _O_CREAT | _O_TRUNC:
            filecreate = CREATE_ALWAYS;
            break;

        default:
            return -1;
    }

	/* assemple the file name */
	ZeroMemory(fname, sizeof(fname));
	ZeroMemory(path, sizeof(path));
	NH_A2W(filename, fname, MAX_PATH);
	if( *filename!='\\' && *filename!='/' ) {
		_tcscpy(path, _nh_cwd);
		_tcsncat(path, _T("\\"), MAX_PATH - _tcslen(path));
	}
	_tcsncat(path, fname, MAX_PATH - _tcslen(path));

    /*
     * try to open/create the file
     */
    if ( (f = CreateFile( path,
                         fileaccess,
                         0,
                         NULL,
                         filecreate,
                         FILE_ATTRIBUTE_NORMAL,
                         NULL ))
         == INVALID_HANDLE_VALUE )
    {
        return -1;
    }

	if( !(oflag & O_APPEND) ) SetFilePointer(f, 0, NULL, FILE_BEGIN);
	return alloc_file_handle(f);
}

int __cdecl read( int f, void *buffer, unsigned int count )
{
	HANDLE p = get_file_handle(f);
	DWORD bytes_read;
	if( !ReadFile(p, buffer, count, &bytes_read, NULL) )
		return -1;
	else 
		return (int)bytes_read;
}

int __cdecl unlink(const char * filename)
{
	TCHAR wbuf[MAX_PATH+1];
	TCHAR fname[MAX_PATH+1];
	
	ZeroMemory(wbuf, sizeof(wbuf));
	ZeroMemory(fname, sizeof(fname));
	NH_A2W(filename, wbuf, MAX_PATH);
	if( *filename!='\\' && *filename!='/' ) {
		_tcscpy(fname, _nh_cwd);
		_tcsncat(fname, _T("\\"), MAX_PATH - _tcslen(fname));
	}
	_tcsncat(fname, wbuf, MAX_PATH - _tcslen(fname));

	return !DeleteFileW(fname);
}

int __cdecl write( int f, const void *buffer, unsigned int count )
{
	HANDLE p = get_file_handle(f);
	DWORD bytes_written;
	if( !WriteFile(p, buffer, count, &bytes_written, NULL) ) 
		return -1;
	else 
		return (int)bytes_written;
}

int __cdecl rename( const char *oldname, const char *newname )
{
	WCHAR f1[MAX_PATH+1];
	WCHAR f2[MAX_PATH+1];
	ZeroMemory(f1, sizeof(f1));
	ZeroMemory(f2, sizeof(f2));
	MultiByteToWideChar(CP_ACP, 0, oldname, -1, f1, MAX_PATH);
	MultiByteToWideChar(CP_ACP, 0, newname, -1, f2, MAX_PATH);
	return !MoveFile(f1, f2);
}

int __cdecl access( const char *path, int mode )
{
	DWORD attr;
	WCHAR f[MAX_PATH+1];
	ZeroMemory(f, sizeof(f));
	MultiByteToWideChar(CP_ACP, 0, path, -1, f, MAX_PATH);
	
	attr = GetFileAttributes(f);
	if( attr == (DWORD)-1 )	return -1;

	if ( (attr & FILE_ATTRIBUTE_READONLY) && (mode & 2) )
		return -1;
	else
        return 0;
}

int chdir( const char *dirname )
{
	ZeroMemory(_nh_cwd, sizeof(_nh_cwd));
	NH_A2W(dirname, _nh_cwd, MAX_PATH);
	return 0;
}

char *getcwd( char *buffer, int maxlen )
{
	if( maxlen<(int)_tcslen(_nh_cwd) ) return NULL;
	else return NH_W2A(_nh_cwd, buffer, maxlen);
}

/*------------------------------------------------------------------------------*/
/* __errno.h__ */
int errno;

/*------------------------------------------------------------------------------*/
/*
 * Chdrive() changes the default drive.
 */
void
chdrive(char *str)
{
	return;
}

/*
 * This is used in nhlan.c to implement some of the LAN_FEATURES.
 */
char *get_username(lan_username_size)
int *lan_username_size;
{
	static char username_buffer[BUFSZ];
	strcpy(username_buffer, "nhsave");
	return username_buffer;
}

void Delay(int ms)
{
	(void)Sleep(ms);
}

void more()
{

}

int isatty(int f)
{
	return 0;
}


#if defined(WIN_CE_PS2xx) || defined(WIN32_PLATFORM_HPCPRO)
int __cdecl isupper(int c)
{
	char str[2];
	WCHAR wstr[2];
	str[0] = c;
	str[1] = 0;

	NH_A2W(str, wstr, 1);
	return iswupper(wstr[0]);
}

int __cdecl isdigit(int c)
{
	return  ('0' <= c && c <= '9');
}

int __cdecl isxdigit(int c)
{
	return  (('0' <= c && c <= '9') || 
		     ('a' <= c && c <= 'f') || 
			 ('A' <= c && c <= 'F'));
}

int __cdecl isspace(int c)
{
	char str[2];
	WCHAR wstr[2];
	str[0] = c;
	str[1] = 0;

	NH_A2W(str, wstr, 1);
	return iswspace(wstr[0]);
}

int __cdecl isprint(int c)
{
	char str[2];
	WCHAR wstr[2];
	str[0] = c;
	str[1] = 0;

	NH_A2W(str, wstr, 1);
	return iswprint(wstr[0]);
}

char* __cdecl _strdup(const char* s)
{
	char* p;
	p = malloc(strlen(s)+1);
	return strcpy(p, s);
}

char* __cdecl strrchr( const char *s, int c )
{
	WCHAR wstr[1024];
	WCHAR *w;
	w = wcsrchr(NH_A2W(s, wstr, 1024), c);
	if(w) return (char*)(s + (w - wstr));
	else return NULL;
}

int   __cdecl _stricmp(const char* a, const char* b)
{
	 return strncmpi(a, b, 65535u);
}

#endif

#if defined(WIN_CE_PS2xx)
/* stdio.h functions are missing from PAlm Size PC SDK 1.2 (SH3 and MIPS) */

#pragma warning(disable:4273)

FILE * __cdecl fopen(const char* filename, const char *mode)
{
    int modeflag;
    int whileflag;
    int filedes;

    /* First mode character must be 'r', 'w', or 'a'. */
    switch (*mode) {
    case 'r':
            modeflag = _O_RDONLY;
            break;
    case 'w':
            modeflag = _O_WRONLY | _O_CREAT | _O_TRUNC;
            break;
    case 'a':
            modeflag = _O_WRONLY | _O_CREAT | _O_APPEND;
            break;
    default:
            return NULL;
    }

    whileflag=1;
    while(*++mode && whileflag)
            switch(*mode) {

            case '+':
                    if (modeflag & _O_RDWR)
                            whileflag=0;
                    else {
                            modeflag |= _O_RDWR;
                            modeflag &= ~(_O_RDONLY | _O_WRONLY);
                    }
                    break;

            case 'b':
                    if (modeflag & (_O_TEXT | _O_BINARY))
                            whileflag=0;
                    else
                            modeflag |= _O_BINARY;
                    break;

            case 't': /* not supported */
					whileflag=0; 
                    break;

	        default:
                    whileflag=0;
                    break;
            }

    if ((filedes = open(filename, modeflag))==-1) return NULL;

    return (FILE*)filedes;
}

int    __cdecl fscanf(FILE *f , const char *format, ...)
{
	/* Format spec:  %[*] [width] [l] type ] */
	int ch;
	int sch;
	int matched = 0;
	int width = 65535;
	int modifier = -1;
	int skip_flag = 0;
	int n_read = 0;
	char buf[BUFSZ];
	TCHAR wbuf[BUFSZ];
	char* p;
	va_list args;

#define RETURN_SCANF(i) { va_end(args); return i; }
#define NEXT_CHAR(f) (n_read++, fgetc(f))

	va_start(args, format);

	ch = *format++;
	sch = NEXT_CHAR(f);
	while( ch && sch!=EOF ) {
		if( isspace(ch) ) {
			while( ch && isspace(ch) ) ch = *format++;
			while( sch!=EOF && isspace(sch) ) sch = NEXT_CHAR(f);
			format--;
			goto next_spec;
		}

		/* read % */
		if( ch!='%' ) {
			if( sch!=ch ) RETURN_SCANF(matched);
			sch = NEXT_CHAR(f);
			goto next_spec;
		} else {
			/* process '%%' */
			ch = *format++;
			if( ch=='%' ) {
				if( sch!='%' ) RETURN_SCANF(matched);
				sch = NEXT_CHAR(f);
				goto next_spec;
			}
			
			if( ch=='*' ) {
				/* read skip flag - '*' */
				skip_flag=1;
				ch = *format++;
			}

			/* get width */
			if( isdigit(ch) ) {
				width = 0;
				while(ch && isdigit(ch)) {
					width = width*10 + (ch-'0');
					ch = *format++;
				}
			}

			/* get modifier */
			if( ch=='l' ) {
				modifier = 'l';
				ch = *format++;
			}

			/* get type */
			switch(ch) {
			case 'c':
				if( !skip_flag ) {
					*(va_arg(args, char*))=sch;
					matched++;
				}
				sch = NEXT_CHAR(f);
				goto next_spec;
			case 'd':
				p = buf;
				/* skip space */
				while(sch!=EOF && isspace(sch)) sch=NEXT_CHAR(f);
				while(sch!=EOF && isdigit(sch) && --width>=0) { *p++ = sch; sch=NEXT_CHAR(f); }
				*p = '\x0';
				if( !skip_flag ) {
					matched++;
					if( modifier=='l' ) {
						*(va_arg(args, long*))=wcstol(NH_A2W(buf, wbuf, BUFSZ), NULL, 10);
					} else {
						*(va_arg(args, int*))=wcstol(NH_A2W(buf, wbuf, BUFSZ), NULL, 10);
					}
				}
				goto next_spec;
			case 'x':
				p = buf;
				while(sch!=EOF && isspace(sch)) sch=NEXT_CHAR(f);
				while(sch!=EOF && isxdigit(sch) && --width>=0) { *p++ = sch; sch=NEXT_CHAR(f); }
				*p = '\x0';
				if( !skip_flag ) {
					matched++;
					if( modifier=='l' ) {
						*(va_arg(args, long*))=wcstol(NH_A2W(buf, wbuf, BUFSZ), NULL, 16);
					} else {
						*(va_arg(args, int*))=wcstol(NH_A2W(buf, wbuf, BUFSZ), NULL, 16);
					}
				}
				goto next_spec;
			case 'n':
				*(va_arg(args, int*)) = n_read;
				matched++;
				goto next_spec;
			case 's':
				if( skip_flag ) {
					while(sch!=EOF && !isspace(sch) && --width>=0) { sch=NEXT_CHAR(f); }
				} else {
					p = va_arg(args, char*);
					while(sch!=EOF && !isspace(sch) && --width>=0) { *p++ = sch; sch=NEXT_CHAR(f); }
					*p = '\x0';
					matched++;
				}
				goto next_spec;
			case '[': {
					char pattern[256];
					int start, end;
					int negate;

					ZeroMemory(pattern, sizeof(pattern));
					p = pattern;
					
					/* try to parse '^' modifier */
					ch = *format++;
					if( ch=='^' ) { negate=1; ch=*format++; }
					else		  { negate=0; }
					if( ch==0 ) RETURN_SCANF(EOF);

					for( ; ch && ch!=']'; ch = *format++ ) {
						/* try to parse range: a-z */
						if( format[0]=='-' &&
							format[1] && format[1]!=']' ) {
							start = ch;
							format++;
							end = *format++;
							while(start<=end) {
								if(!strchr(pattern, (char)start))
									*p++ = (char)start;
								start++;
							}
						} else {
							if(!strchr(pattern, (char)ch)) *p++ = (char)ch;
						}
					}

					if( skip_flag ) {
						while(sch!=EOF && strchr(pattern, sch) && --width>=0) { sch=NEXT_CHAR(f); }
					} else {
						p = va_arg(args, char*);
						if( negate )
							while(sch!=EOF && !strchr(pattern, sch) && --width>=0) { *p++ = sch; sch=NEXT_CHAR(f); }
						else
							while(sch!=EOF && strchr(pattern, sch) && --width>=0) { *p++ = sch; sch=NEXT_CHAR(f); }
						*p = '\x0';
						matched++;
					}
				  } goto next_spec;
			default:
				RETURN_SCANF(EOF);
			}
		}

next_spec:
		width = 65535;
		modifier = -1;
		skip_flag = 0;
		ch = *format++;
	}
	fseek(f, -1, SEEK_CUR);
	RETURN_SCANF(matched);

#undef RETURN_SCANF
#undef NEXT_CHAR
}

int __cdecl fprintf(FILE *f , const char *format, ...)
{
	int retval;
	va_list args;

	if( !f || !format ) return 0;

	va_start(args, format);
	retval = vfprintf(f, format, args);
	va_end(args);
	
	return retval;
}

int    __cdecl vfprintf(FILE* f, const char *format, va_list args)
{
	char buf[4096];
	int retval;

	if( !f || !format ) return 0;

	retval = vsprintf(buf, format, args);

	write((int)f, buf, strlen(buf));
	
	return retval;
}

int __cdecl fgetc(FILE * f)
{
	char c;
	int fh = (int)f;

	if( !f ) return EOF;
	if( read(fh, &c, 1)==1 ) return c;
	else					 return EOF;
}

char * __cdecl fgets(char *s, int size, FILE *f)
{
	/* not the best performance but it will do for now...*/
	char c;
	if( !f || !s || size==0 ) return NULL;
	while( --size>0 ) {
		if( (c = fgetc(f))==EOF ) return NULL;

		*s++ = c;
		if( c=='\n' ) break;
	}
	*s = '\x0';
	return s;
}

int    __cdecl printf(const char *format, ...)
{
	int retval;
	va_list args;

	if( !format ) return 0;

	va_start(args, format);
	retval = vprintf(format, args);
	va_end(args);

	return retval;
}

int    __cdecl vprintf(const char *format, va_list args)
{
	char buf[4096];
	int retval;

	retval = vsprintf(buf, format, args);
	puts(buf);
	return retval;
}

// int    __cdecl putchar(int);
int    __cdecl puts(const char * s)
{
	TCHAR wbuf[4096];
	NH_A2W(s, wbuf, 4096);
	MessageBox(NULL, wbuf, _T("stdout"), MB_OK);
	return 0;
}

FILE*  __cdecl _getstdfilex(int desc)
{
	return NULL;
}

int __cdecl fclose(FILE * f)
{
	if(!f) return EOF;
	return close((int)f)==-1? EOF : 0;
}

size_t __cdecl fread(void *p, size_t size, size_t count, FILE *f)
{
	int read_bytes;
	if(!f || !p || size==0 || count==0) return 0;
	read_bytes = read((int)f, p, size*count);
	return read_bytes>0? (read_bytes/size) : 0;
}

size_t __cdecl fwrite(const void *p, size_t size, size_t count, FILE * f)
{
	int write_bytes;
	if(!f || !p || size==0 || count==0) return 0;
	write_bytes = write((int)f, p, size*count);
	return write_bytes>0? write_bytes/size : 0;
}

int    __cdecl fflush(FILE *f)
{ 
	return 0;
}

int    __cdecl feof(FILE *f)
{
	return (f && eof((int)f)==0)? 0 : 1;
}

int    __cdecl fseek(FILE *f, long offset, int from)
{
	return (f && lseek((int)f, offset, from)>=0)? 0 : 1;
}

long   __cdecl ftell(FILE * f)
{
	return f? lseek((int)f, 0, SEEK_CUR) : -1;
}

#endif 
