//
// (c) Yuri Kiryanov, openh323@kiryanov.com and
//     Yuriy Gorvitovskiy
//
// for Openh323, www.Openh323.org
//
// Windows CE Port
//
// stdlib routines implemented through Windows CE API
//

#include <ptlib.h>
#include <Atlconv.h>
#include <winbase.h>
#include <winnt.h>
#include <snmp.h>

#include <ptlib/sockets.h>

#define DELETE (0x00010000L) // defined in <winnt.h> and undef "msos/ptlib/contain.h"

#ifndef __cplusplus
#include <tchar.h>
int isprint(int c) { return _istprint(c);}
int isxdigit(int c) { return _istxdigit(c); }
int isspace( int c ) { return _istspace(c); }
int isupper( int c ) { return _istupper(c); }
int islower( int c ) { return _istlower(c); }
int isalnum( int c ) { return _istalnum(c); }
int isalpha( int c ) { return _istalpha(c); }
int iscntrl( int c ) { return _istcntrl(c); }
int isdigit( int c ) { return _istdigit(c); }
int ispunct( int c ) { return _istpunct(c); }
#endif

void __cdecl abort(void) { 
	if(IDYES == MessageBox(NULL, _T("Abort?"), _T("stdlibx"), MB_YESNO))
		exit(3);
}

void __cdecl perror(const char * s) {
	USES_CONVERSION;
	MessageBox(NULL, s && *s ? A2T(s) : _T("null"), _T("stdlibx"), MB_OK); 
}

long _lseek(int nHandle, long off, int orig)
{
	DWORD dwMoveMethod=FILE_BEGIN;
	switch(orig)
	{
		case SEEK_SET: dwMoveMethod=FILE_BEGIN; break;
		case SEEK_CUR: dwMoveMethod=FILE_CURRENT; break;
		case SEEK_END: dwMoveMethod=FILE_END; break;
	}
	return SetFilePointer((HANDLE)nHandle,off,NULL,dwMoveMethod);
}

int  _close(int nHandle)
{
        FlushFileBuffers((HANDLE)nHandle);
	return (CloseHandle((HANDLE)nHandle)) ? 0 : -1;
}
int  _read(int nHandle, void *p, unsigned int s)
{
	DWORD size=0;
	ReadFile((HANDLE)nHandle,p,s,&size,NULL);
	return size;
}

int  _write(int nHandle, const void *p, unsigned int s)
{
	DWORD size=0;
	WriteFile((HANDLE)nHandle,p,s,&size,NULL);
	//[YG]???? FlushFileBuffers((HANDLE)nHandle);
	return size;
}
int	_open( const char *filename, int oflag , int pmode)
{
	USES_CONVERSION;
    HANDLE	osfh;                    /* OS handle of opened file */
    DWORD	fileaccess;               /* OS file access (requested) */
    DWORD	fileshare;                /* OS file sharing mode */
    DWORD	filecreate;               /* OS method of opening/creating */
    DWORD	fileattrib;               /* OS file attribute flags */
    
    /*
     * 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_WRITE;
                break;
        case _O_RDWR:           /* read and write access */
                fileaccess = GENERIC_READ | GENERIC_WRITE;
                break;
        default:                /* error, bad oflag */
                set_errno(EINVAL);
                return -1;
    }

    /*
     * decode sharing flags
     */
	fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE;

    /*
     * 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:
                // this can't happen ... all cases are covered
                set_errno(EINVAL);
                return -1;
	}

    /*
     * decode file attribute flags if _O_CREAT was specified
     */
    fileattrib = FILE_ATTRIBUTE_NORMAL;     /* default */

    if ( oflag & _O_CREAT ) 
	{
        /*
         * set up variable argument list stuff
         */
        if ( oflag & _O_RDONLY )
            fileattrib = FILE_ATTRIBUTE_READONLY;
    }

    /*
     * Set temporary file (delete-on-close) attribute if requested.
     */
    if ( oflag & _O_TEMPORARY ) 
	{
        fileattrib |= FILE_FLAG_DELETE_ON_CLOSE;
        fileaccess |= DELETE;
    }

    /*
     * Set temporary file (delay-flush-to-disk) attribute if requested.
     */
    if ( oflag & _O_SHORT_LIVED )
        fileattrib |= FILE_ATTRIBUTE_TEMPORARY;

    /*
     * Set sequential or random access attribute if requested.
     */
    if ( oflag & _O_SEQUENTIAL )
        fileattrib |= FILE_FLAG_SEQUENTIAL_SCAN;
    else if ( oflag & _O_RANDOM )
        fileattrib |= FILE_FLAG_RANDOM_ACCESS;

    /*
     * get an available handle.
     *
     * multi-thread note: the returned handle is locked!
     */
    /*
     * try to open/create the file
     */

    if ( (osfh = CreateFile( A2T(filename),
                             fileaccess,
                             fileshare,
                             NULL,
                             filecreate,
                             fileattrib,
                             NULL ))
         == INVALID_HANDLE_VALUE )
    {
        return -1;                      /* return error to caller */
    }
	return (int)osfh;
}

int _sopen(const char *filename, int oflag , int pmode, ...)
{ 
	return _open(filename, oflag , pmode); 
}

int	_chsize( int nHandle, long size )
{
	if ((DWORD)size!=SetFilePointer((HANDLE)nHandle,size,NULL,FILE_BEGIN)) return -1;
	return (SetEndOfFile((HANDLE)nHandle)) ? 0 : -1;
}


int _mkdir(const char *sDir)
{	
	USES_CONVERSION;
        PString folderName = sDir;

        if (folderName[folderName.GetLength() - 1] == PDIR_SEPARATOR) {
           folderName.Delete(folderName.GetLength() - 1, 1);
        }

        return (CreateDirectory(A2T(folderName),NULL) ? 0 : -1);
}

int  _rmdir(const char *sDir)
{	
	USES_CONVERSION;
        PString folderName = sDir;

        if (folderName[folderName.GetLength() - 1] == PDIR_SEPARATOR) {
            folderName.Delete(folderName.GetLength() - 1, 1);
        }

        return (RemoveDirectory(A2T(folderName)) ? 0 : -1);
}

int  _access(const char *sName, int mode)
{
	USES_CONVERSION;
	WIN32_FIND_DATA FindFileData;

        PString test(sName);
        if (test[test.GetLength() - 1] == '.' && test[test.GetLength() - 2] == PDIR_SEPARATOR)
            test.Delete(test.GetLength() - 2, 2);

        HANDLE file = FindFirstFile(A2T((const char*) test), &FindFileData);
	if (file == INVALID_HANDLE_VALUE ) return -1;
	FindClose(file);
	switch(mode)
	{
		//checking for the existance
		case 0: return 0;
		//checking for read permission
		case 4: return 0;
		//checking for write permission
		case 2: return (FindFileData.dwFileAttributes&FILE_ATTRIBUTE_READONLY) ? -1 : 0;
		//checking for read and write permission
		case 6: return (FindFileData.dwFileAttributes&FILE_ATTRIBUTE_READONLY) ? -1 : 0;
	}
	return -1;
}
int	remove(const char *name)
{
	USES_CONVERSION;
	return (DeleteFile(A2T(name)) ? 0 : -1);
}

int	_chmod( const char *filename, int pmode )
{
	USES_CONVERSION;
	TCHAR* pFileName=A2T(filename);
	DWORD attr = GetFileAttributes(pFileName);
	if (pmode&_S_IWRITE)
		attr|=FILE_ATTRIBUTE_READONLY;
	else
		attr&=~FILE_ATTRIBUTE_READONLY;
	
	return (SetFileAttributes(pFileName,attr) ? 0: -1);	
}

int	rename( const char *oldname, const char *newname )
{
	USES_CONVERSION;
	return (DeleteAndRenameFile( A2T(newname), A2T(oldname)) ? 0 : -1);
}

//used by regex.cxx
void printchar (char n)
{
	printf(" %d ",n);	
}

long strtol (const char *nptr,char **endptr,int ibase)
{
	USES_CONVERSION;
	TCHAR* tnptr = A2T(nptr);
	TCHAR* tendptr = NULL;

	long res= _tcstoul(tnptr,&tendptr,ibase);
	if (endptr)
	{
		if (tendptr) *endptr=const_cast<char*>(nptr+(tendptr-tnptr));
		else		 *endptr=NULL;
	}
	return res;
}

unsigned long strtoul (const char *nptr,char **endptr,int ibase)
{
	USES_CONVERSION;
	TCHAR* tnptr = A2T(nptr);
	TCHAR* tendptr = NULL;

	unsigned long res= _tcstoul(tnptr,&tendptr,ibase);
	if (endptr)
	{
		if (tendptr) *endptr=const_cast<char*>(nptr+(tendptr-tnptr));
		else		 *endptr=NULL;
	}
	return res;
}

double strtod( const char *nptr, char **endptr )
{
	USES_CONVERSION;
	TCHAR* tnptr = A2T(nptr);
	TCHAR* tendptr = NULL;

	double res= _tcstod(tnptr,&tendptr);
	if (endptr)
	{
		if (tendptr) *endptr=const_cast<char*>(nptr+(tendptr-tnptr));
		else		 *endptr=NULL;
	}
	return res;
}


#if _WIN32_WCE < 300
const char * __cdecl strrchr (const char * string,int ch )
{
        char *start = (char *)string;

        while (*string++)                       /* find end of string */
                ;
                                                /* search towards front */
        while (--string != start && *string != (char)ch)
                ;

        if (*string == (char)ch)                /* char found ? */
                return( (char *)string );

        return(NULL);
}
#endif

size_t strspn( const char *string, const char *strCharSet )
{
    const unsigned char *str = (const unsigned char*)string;
    const unsigned char *ctrl = (const unsigned char*)strCharSet;

    unsigned char map[32];
    int count;

    /* Clear out bit map */
    for (count=0; count<32; count++)
        map[count] = 0;

    /* Set bits in control map */
    while (*ctrl)
    {  
		map[*ctrl >> 3] |= (1 << (*ctrl & 7));
		ctrl++;
    }

    /* 1st char NOT in control map stops search */
    if (*str)
    {
        count=0;
        while (map[*str >> 3] & (1 << (*str & 7)))
        {
                count++;
                str++;
        }
        return(count);
	}
    return(0);
}

int stricmp( const char*string1, const char* string2 )
{
	USES_CONVERSION;
	return stricmp( (const unsigned short*) A2T(string1), (const char*) string2);
}

static void x64toa (unsigned __int64 val,char *buf,unsigned radix,int is_neg)
{
        char *p;                /* pointer to traverse string */
        char *firstdig;         /* pointer to first digit */
        char temp;              /* temp char */
        unsigned digval;        /* value of digit */

        p = buf;

        if ( is_neg )
        {
            *p++ = '-';         /* negative, so output '-' and negate */
            val = (unsigned __int64)(-(__int64)val);
        }

        firstdig = p;           /* save pointer to first digit */

        do {
            digval = (unsigned) (val % radix);
            val /= radix;       /* get next digit */

            /* convert to ascii and store */
            if (digval > 9)
                *p++ = (char) (digval - 10 + 'a');  /* a letter */
            else
                *p++ = (char) (digval + '0');       /* a digit */
        } while (val > 0);

        /* We now have the digit of the number in the buffer, but in reverse
           order.  Thus we reverse them now. */

        *p-- = '\0';            /* terminate string; p points to last digit */

        do {
            temp = *p;
            *p = *firstdig;
            *firstdig = temp;   /* swap *p and *firstdig */
            --p;
            ++firstdig;         /* advance to next two digits */
        } while (firstdig < p); /* repeat until halfway */
}

/* Actual functions just call conversion helper with neg flag set correctly,
   and return pointer to buffer. */

char * _i64toa (__int64 val,char *buf,int radix)
{
        x64toa((unsigned __int64)val, buf, radix, (radix == 10 && val < 0));
        return buf;
}

char * _ui64toa (unsigned __int64 val,char *buf,int radix)
{
        x64toa(val, buf, radix, 0);
        return buf;
}

//#if _WIN32_WCE < 300
__int64 _atoi64(const char *nptr)
{
        int c;              /* current char */
        __int64 total;      /* current total */
        int sign;           /* if '-', then negative, otherwise positive */

        /* skip whitespace */
        while ( isspace((int)(unsigned char)*nptr) )
            ++nptr;

        c = (int)(unsigned char)*nptr++;
        sign = c;           /* save sign indication */
        if (c == '-' || c == '+')
            c = (int)(unsigned char)*nptr++;    /* skip sign */

        total = 0;

        while (isdigit(c)) {
            total = 10 * total + (c - '0');     /* accumulate digit */
            c = (int)(unsigned char)*nptr++;    /* get next char */
        }

        if (sign == '-')
            return -total;
        else
            return total;   /* return result, negated if necessary */
}
//#endif

char * _mktemp (char *temp)
{
        char *string = temp;
        unsigned number;
        int letter = 'a';
        int xcount = 0;
        int olderrno;

        _ASSERTE(temp != NULL);
        _ASSERTE(*temp != '\0');

  	    number = (unsigned) GetCurrentProcess();
  
        while (*string)
                string++;

#ifndef _MBCS
        while (*--string == 'X')
#else  /* _MBCS */
        while ((string>temp) && (!__isdbcscode(temp,string-1))
                && (*--string == 'X'))
#endif  /* _MBCS */
        {
                xcount++;
                *string = (char)((number % 10) + '0');
                number /= 10;
        }

        if (*++string == '\0' || xcount != 6 )
                return(NULL);

        olderrno = errno;       /* save current errno */
        set_errno(0);              /* make sure errno isn't EACCESS */

        while ((_access(temp,0) == 0) || (errno == EACCES))
        /* while file exists */
        {
                set_errno(0);
                if (letter == 'z'+1) 
				{
                        set_errno(olderrno);
                        return(NULL);
                }

                *string = (char)letter++;
        }

        set_errno(olderrno);
        return(temp);
}

LONG RegOpenKeyEx( HKEY hKey, const char* lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult )
{ USES_CONVERSION; return 
	RegOpenKeyEx( hKey, A2T(lpSubKey), ulOptions, 
	samDesired, phkResult ); 
}

LONG RegCreateKeyEx( HKEY hKey, const char* lpSub, DWORD dwr, LPSTR lpcls, DWORD dwo, 
	REGSAM sam, LPSECURITY_ATTRIBUTES lpsa, PHKEY phk, LPDWORD lpdw )
{ USES_CONVERSION; return 
	RegCreateKeyEx( hKey, A2T(lpSub), 
	dwr, A2T(lpcls), dwo, sam, lpsa, phk, lpdw ); 
}

LONG RegEnumKey(HKEY hKey, DWORD dwIndex, LPTSTR ptcsName, DWORD cbName)
{	DWORD cb = cbName; 
	return RegEnumKeyEx( hKey, dwIndex, ptcsName, &cb, 0L, NULL, NULL, NULL ); 
}

LONG RegDeleteKey( HKEY hKey, const char* lpSubKey )
{ USES_CONVERSION; return RegDeleteKey( hKey, A2T(lpSubKey) ); }

LONG RegEnumValueCe( HKEY hKey, DWORD dwIndex, LPTSTR ptcsValueName, LPDWORD lpcbValueName, 
	LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData )
{ 
	return RegEnumValue( hKey, dwIndex, ptcsValueName, lpcbValueName, 
	lpReserved, lpType, lpData, lpcbData );
}

LONG RegQueryValueEx( HKEY hKey, char* lpValueName, 
	LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData )
{ USES_CONVERSION; return RegQueryValueEx( hKey, A2T(lpValueName), 
	lpReserved, lpType, lpData, lpcbData );
}

LONG RegSetValueEx( HKEY hKey, const char* lpValueName, DWORD Reserved, DWORD dwType, 
	const BYTE *lpData, DWORD cbData ) 
{ USES_CONVERSION; return RegSetValueEx( hKey, A2T(lpValueName), Reserved, dwType, 
	lpData, cbData ); 
}

LONG RegDeleteValue( HKEY hKey, const char* lpValueName )
{ USES_CONVERSION; return RegDeleteValue( hKey, A2T(lpValueName) ); }

UINT GetWindowsDirectory( char* lpBuffer, UINT uSize )
{ strncpy(lpBuffer, "\\Windows", uSize ); return uSize; }


DWORD GetPrivateProfileString(
  const char* lpAppName,        // points to section name
  const char*  lpKeyName,        // points to key name
  const char* lpDefault,        // points to default string
  char* lpReturnedString,  // points to destination buffer
  DWORD nSize,              // size of destination buffer
  const char*  )        // points to initialization filename
{ 	
	return -1L;
}

PBoolean WritePrivateProfileString(
  const char* lpAppName,  // pointer to section name
  const char* lpKeyName,  // pointer to key name
  const char* lpString,   // pointer to string to add
  const char* )  // pointer to initialization filename
{ 
	return PFalse;
}

