#ifdef SYSTEM_WIN32
#pragma warning(disable:4996)
#endif

#ifdef SYSTEM_WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif

#include "cmdlib.h"
#include "messages.h"
#include "hlassert.h"
#include "blockmem.h"
#include "log.h"

#ifdef SYSTEM_POSIX
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#endif

#define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/')

/*
 * ================
 * I_FloatTime
 * ================
 */

double          I_FloatTime()
{
#ifdef SYSTEM_WIN32
    FILETIME        ftime;
    double rval;

    GetSystemTimeAsFileTime(&ftime);

    rval = ftime.dwLowDateTime;
    rval += ((__int64)ftime.dwHighDateTime) << 32;

    return (rval / 10000000.0);
#endif

#ifdef SYSTEM_POSIX
    struct timeval  tp;
    struct timezone tzp;
    static int      secbase;

    gettimeofday(&tp, &tzp);

    if (!secbase)
    {
        secbase = tp.tv_sec;
        return tp.tv_usec / 1000000.0;
    }

    return (tp.tv_sec - secbase) + tp.tv_usec / 1000000.0;
#endif
}

#ifdef SYSTEM_POSIX
char*           strupr(char* string)
{
    int             i;
    int             len = strlen(string);

    for (i = 0; i < len; i++)
    {
        string[i] = toupper(string[i]);
    }
    return string;
}

char*           strlwr(char* string)
{
    int             i;
    int             len = strlen(string);

    for (i = 0; i < len; i++)
    {
        string[i] = tolower(string[i]);
    }
    return string;
}
#endif

// Case Insensitive substring matching
const char*     stristr(const char* const string, const char* const substring)
{
    char*           string_copy;
    char*           substring_copy;
    const char*     match;

    string_copy = _strdup(string);
    _strlwr(string_copy);

    substring_copy = _strdup(substring);
    _strlwr(substring_copy);

    match = strstr(string_copy, substring_copy);
    if (match)
    {
        match = (string + (match - string_copy));
    }

    free(string_copy);
    free(substring_copy);
    return match;
}

/*--------------------------------------------------------------------
// New implementation of FlipSlashes, DefaultExtension, StripFilename, 
// StripExtension, ExtractFilePath, ExtractFile, ExtractFileBase, etc.
----------------------------------------------------------------------*/
#ifdef ZHLT_NEW_FILE_FUNCTIONS

//Since all of these functions operate around either the extension 
//or the directory path, centralize getting both numbers here so we
//can just reference them everywhere else.  Use strrchr to give a
//speed boost while we're at it.
inline void getFilePositions(char* path, int* extension_position, int* directory_position)
{
	char* ptr = strrchr(path,'.');
	if(ptr == 0)
	{ *extension_position = -1; }
	else
	{ *extension_position = ptr - path; }

	ptr = max(strrchr(path,'/'),strrchr(path,'\\'));
	if(ptr == 0)
	{ *directory_position = -1; }
	else
	{ 
		*directory_position = ptr - path;
		if(*directory_position > *extension_position)
		{ *extension_position = -1; }
		
		//cover the case where we were passed a directory - get 2nd-to-last slash
		if(*directory_position == strlen(path) - 1)
		{
			do
			{
				--(*directory_position);
			}
			while(*directory_position > -1 && path[*directory_position] != '/' && path[*directory_position] != '\\');
		}
	}
}

char* FlipSlashes(char* string)
{
	char* ptr = string;
	if(SYSTEM_SLASH_CHAR == '\\')
	{
		while(ptr = strchr(ptr,'/'))
		{ *ptr = SYSTEM_SLASH_CHAR; }
	}
	else
	{
		while(ptr = strchr(ptr,'\\'))
		{ *ptr = SYSTEM_SLASH_CHAR; }
	}
	return string;
}

void DefaultExtension(char* path, const char* extension)
{
	int extension_pos, directory_pos;
	getFilePositions(path,&extension_pos,&directory_pos);
	if(extension_pos == -1)
	{ strcat(path,extension); }
}

void StripFilename(char* path)
{
	int extension_pos, directory_pos;
	getFilePositions(path,&extension_pos,&directory_pos);
	if(directory_pos == -1)
	{ path[0] = 0; }
	else
	{ path[directory_pos] = 0; }
}

void StripExtension(char* path)
{
	int extension_pos, directory_pos;
	getFilePositions(path,&extension_pos,&directory_pos);
	if(extension_pos != -1)
	{ path[extension_pos] = 0; }
}

void ExtractFilePath(char* const path, char* dest)
{
	int extension_pos, directory_pos;
	getFilePositions(path,&extension_pos,&directory_pos);
	if(directory_pos != -1)
	{
	    memcpy(dest,path,directory_pos+1); //include directory slash
	    dest[directory_pos+1] = 0;
	}
	else
	{ dest[0] = 0; }
}

void ExtractFile(char* const path, char* dest)
{
	int extension_pos, directory_pos;
	getFilePositions(path,&extension_pos,&directory_pos);

	int length = (int)strlen(path);

	if(directory_pos == -1)	{ directory_pos = 0; }
	else { length -= directory_pos + 1; }

    memcpy(dest,path+directory_pos+1,length); //exclude directory slash
    dest[length] = 0;
}

void ExtractFileBase(char* const path, char* dest)
{
	int extension_pos, directory_pos;
	getFilePositions(path,&extension_pos,&directory_pos);
	int length = extension_pos == -1 ? (int)strlen(path) : extension_pos;

	if(directory_pos == -1)	{ directory_pos = 0; }
	else { length -= directory_pos + 1; }

    memcpy(dest,path+directory_pos+1,length); //exclude directory slash
    dest[length] = 0;
}

void ExtractFileExtension(char* const path, char* dest)
{
	int extension_pos, directory_pos;
	getFilePositions(path,&extension_pos,&directory_pos);
	if(extension_pos != -1)
	{
		int length = (int)strlen(path) - extension_pos;
	    memcpy(dest,path+extension_pos,length); //include extension '.'
	    dest[length] = 0;
	}
	else
	{ dest[0] = 0; }
}
//-------------------------------------------------------------------
#else //old cmdlib functions

char*           FlipSlashes(char* string)
{
    while (*string)
    {
        if (PATHSEPARATOR(*string))
        {
            *string = SYSTEM_SLASH_CHAR;
        }
        string++;
    }
    return string;
}

void            DefaultExtension(char* path, const char* extension)
{
    char*           src;

    //
    // if path doesn't have a .EXT, append extension
    // (extension should include the .)
    //
    src = path + strlen(path) - 1;

    while (!PATHSEPARATOR(*src) && src != path)
    {
        if (*src == '.')
            return;                                        // it has an extension
        src--;
    }

    strcat(path, extension);
}

void            StripFilename(char* path)
{
    int             length;

    length = strlen(path) - 1;
    while (length > 0 && !PATHSEPARATOR(path[length]))
        length--;
    path[length] = 0;
}

void            StripExtension(char* path)
{
    int             length;

    length = strlen(path) - 1;
    while (length > 0 && path[length] != '.')
    {
        length--;
        if (PATHSEPARATOR(path[length]))
            return;                                        // no extension
    }
    if (length)
        path[length] = 0;
}

/*
 * ====================
 * Extract file parts
 * ====================
 */
void            ExtractFilePath(const char* const path, char* dest)
{
    hlassert (path != dest);

    const char*           src;

    src = path + strlen(path) - 1;

    //
    // back up until a \ or the start
    //
    while (src != path && !PATHSEPARATOR(*(src - 1)))
        src--;

    memcpy(dest, path, src - path);
    dest[src - path] = 0;
}

void            ExtractFile(const char* const path, char* dest)
{
    hlassert (path != dest);

    const char*           src;

    src = path + strlen(path) - 1;

    while (src != path && !PATHSEPARATOR(*(src - 1)))
        src--;

    while (*src)
    {
        *dest++ = *src++;
    }
    *dest = 0;
}

void            ExtractFileBase(const char* const path, char* dest)
{
    hlassert (path != dest);

    const char*           src;

    src = path + strlen(path) - 1;

    //
    // back up until a \ or the start
    //
    while (src != path && !PATHSEPARATOR(*(src - 1)))
        src--;

    while (*src && *src != '.')
    {
        *dest++ = *src++;
    }
    *dest = 0;
}

void            ExtractFileExtension(const char* const path, char* dest)
{
    hlassert (path != dest);

    const char*           src;

    src = path + strlen(path) - 1;

    //
    // back up until a . or the start
    //
    while (src != path && *(src - 1) != '.')
        src--;
    if (src == path)
    {
        *dest = 0;                                         // no extension
        return;
    }

    strcpy_s(dest, src);
}

#endif

/*
 * ============================================================================
 * 
 * BYTE ORDER FUNCTIONS
 * 
 * ============================================================================
 */

#ifdef WORDS_BIGENDIAN

short           LittleShort(const short l)
{
    byte            b1, b2;

    b1 = l & 255;
    b2 = (l >> 8) & 255;

    return (b1 << 8) + b2;
}

short           BigShort(const short l)
{
    return l;
}

int             LittleLong(const int l)
{
    byte            b1, b2, b3, b4;

    b1 = l & 255;
    b2 = (l >> 8) & 255;
    b3 = (l >> 16) & 255;
    b4 = (l >> 24) & 255;

    return ((int)b1 << 24) + ((int)b2 << 16) + ((int)b3 << 8) + b4;
}

int             BigLong(const int l)
{
    return l;
}

float           LittleFloat(const float l)
{
    union
    {
        byte            b[4];
        float           f;
    }
    in             , out;

    in.f = l;
    out.b[0] = in.b[3];
    out.b[1] = in.b[2];
    out.b[2] = in.b[1];
    out.b[3] = in.b[0];

    return out.f;
}

float           BigFloat(const float l)
{
    return l;
}

#else // Little endian (Intel, etc)

short           BigShort(const short l)
{
    byte            b1, b2;

    b1 = (byte) (l & 255);
    b2 = (byte) ((l >> 8) & 255);

    return (short)((b1 << 8) + b2);
}

short           LittleShort(const short l)
{
    return l;
}

int             BigLong(const int l)
{
    byte            b1, b2, b3, b4;

    b1 = (byte) (l & 255);
    b2 = (byte) ((l >> 8) & 255);
    b3 = (byte) ((l >> 16) & 255);
    b4 = (byte) ((l >> 24) & 255);

    return ((int)b1 << 24) + ((int)b2 << 16) + ((int)b3 << 8) + b4;
}

int             LittleLong(const int l)
{
    return l;
}

float           BigFloat(const float l)
{
    union
    {
        byte            b[4];
        float           f;
    }
    in             , out;

    in.f = l;
    out.b[0] = in.b[3];
    out.b[1] = in.b[2];
    out.b[2] = in.b[1];
    out.b[3] = in.b[0];

    return out.f;
}

float           LittleFloat(const float l)
{
    return l;
}

#endif

//=============================================================================

bool CDECL      safe_snprintf(char* const dest, const size_t count, const char* const args, ...)
{
    size_t          amt;
    va_list         argptr;

    hlassert(count > 0);

    va_start(argptr, args);
    amt = vsnprintf(dest, count, args, argptr);
    va_end(argptr);

    // truncated (bad!, snprintf doesn't null terminate the string when this happens)
    if (amt == count)
    {
        dest[count - 1] = 0;
        return false;
    }

    return true;
}

bool            safe_strncpy(char* const dest, const char* const src, const size_t count)
{
    return safe_snprintf(dest, count, "%s", src);
}

bool            safe_strncat(char* const dest, const char* const src, const size_t count)
{
    if (count)
    {
        strncat(dest, src, count);

        dest[count - 1] = 0;                               // Ensure it is null terminated
        return true;
    }
    else
    {
        Warning("safe_strncat passed empty count");
        return false;
    }
}

bool            TerminatedString(const char* buffer, const int size)
{
    int             x;

    for (x = 0; x < size; x++, buffer++)
    {
        if ((*buffer) == 0)
        {
            return true;
        }
    }
    return false;
}
