

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.0.0.170915_Beta4 (15 September 2017)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2017
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license.Â The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
#ifdef _WIN32
	// To avoid complaints about fopen and _open
	#define _CRT_SECURE_NO_WARNINGS
#endif

#include "Utils.h"
#include "ObjectIDs.h"
#include "HTML.h"

namespace cmlabs {

//////////////////////////////////////////////////////////////
//                        INIT/EXIT                         //
//////////////////////////////////////////////////////////////

uint32 CleanShutdownInstanceCount = 0;
CleanShutdown CleanShutdownInstance;

CleanShutdown::CleanShutdown() {
	CleanShutdownInstanceCount++;
}

CleanShutdown::~CleanShutdown() {
	CleanShutdownInstanceCount--;
	if (!CleanShutdownInstanceCount) {
		ClearUtilsMaps();
		delete(LogSystem::LogSingleton);
		LogSystem::LogSingleton = NULL;
	}
}

bool ClearUtilsMaps() {

	if (utils::SharedMemorySemaphoreMapMutex) {
		utils::SharedMemorySemaphoreMapMutex->enter();
		std::map<std::string, utils::Semaphore*>::iterator si;
		while (utils::SharedMemorySemaphoreMap->size()) {
			si = utils::SharedMemorySemaphoreMap->begin();
			delete(si->second);
			utils::SharedMemorySemaphoreMap->erase(si->first);
		}
		delete(utils::SharedMemorySemaphoreMap);
		utils::SharedMemorySemaphoreMap = NULL;
		utils::SharedMemorySemaphoreMapMutex->leave();
		delete(utils::SharedMemorySemaphoreMapMutex);
		utils::SharedMemorySemaphoreMapMutex = NULL;
	}
	
	if (utils::SharedMemoryEventMapMutex) {
		utils::SharedMemoryEventMapMutex->enter();
		std::map<std::string, utils::Event*>::iterator ei;
		while (utils::SharedMemoryEventMap->size()) {
			ei = utils::SharedMemoryEventMap->begin();
			delete(ei->second);
			utils::SharedMemoryEventMap->erase(ei->first);
		}
		delete(utils::SharedMemoryEventMap);
		utils::SharedMemoryEventMap = NULL;
		utils::SharedMemoryEventMapMutex->leave();
		delete(utils::SharedMemoryEventMapMutex);
		utils::SharedMemoryEventMapMutex = NULL;
	}

	if (utils::SharedMemoryMutexMapMutex) {
		utils::SharedMemoryMutexMapMutex->enter();
		std::map<std::string, utils::Mutex*>::iterator mi;
		while (utils::SharedMemoryMutexMap->size()) {
			mi = utils::SharedMemoryMutexMap->begin();
			delete(mi->second);
			utils::SharedMemoryMutexMap->erase(mi->first);
		}
		delete(utils::SharedMemoryMutexMap);
		utils::SharedMemoryMutexMap = NULL;
		utils::SharedMemoryMutexMapMutex->leave();
		delete(utils::SharedMemoryMutexMapMutex);
		utils::SharedMemoryMutexMapMutex = NULL;
	}

	delete(utils::DrumBeatMap);
	utils::DrumBeatMap = NULL;

	delete(utils::SharedMemoryFileHandleMap);
	utils::SharedMemoryFileHandleMap = NULL;

	delete(utils::Timer::timers);
	utils::Timer::timers = NULL;

	delete(utils::CommandLineInfo::CommandLineInfoSingleton);
	utils::CommandLineInfo::CommandLineInfoSingleton = NULL;

	return true;
}

utils::CommandLineInfo* utils::CommandLineInfo::CommandLineInfoSingleton = NULL;

//////////////////////////////////////////////////////////////
//                         Objects                          //
//////////////////////////////////////////////////////////////

std::string GetDataTypeName(uint32 datatype) {
	switch (datatype) {
	case CONSTCHARID:
		return "String";
	case TIMEID:
		return "Time";
	case INTID:
		return "Integer";
	case DOUBLEID:
		return "Float";
	case CHARDATAID:
		return "Binary";
	case DATAMESSAGEID:
		return "Message";
	case CONSTCHARINFOID:
		return "String info";
	case CHARDATAINFOID:
		return "Binary info";
	case DATAMESSAGEINFOID:
		return "Message info";
	default:
		return "Unknown type";
	}
}

uint32 GetDataTypeID(const char* typeName) {
	if ((stricmp(typeName, "string") == 0) || (stricmp(typeName, "text") == 0))
		return CONSTCHARID;
	else if (stricmp(typeName, "time") == 0)
		return CONSTCHARID;
	else if ((stricmp(typeName, "integer") == 0) || (stricmp(typeName, "int") == 0))
		return INTID;
	else if ((stricmp(typeName, "float") == 0) || (stricmp(typeName, "double") == 0))
		return DOUBLEID;
	else if (stricmp(typeName, "binary") == 0)
		return CHARDATAID;
	else if (stricmp(typeName, "message") == 0)
		return DATAMESSAGEID;
	else if (stricmp(typeName, "string info") == 0)
		return CONSTCHARINFOID;
	else if (stricmp(typeName, "binary info") == 0)
		return CHARDATAINFOID;
	else if (stricmp(typeName, "message info") == 0)
		return DATAMESSAGEINFOID;
	else
		return 0;
}

//////////////////////////////////////////////////////////////
//                         Logging                          //
//////////////////////////////////////////////////////////////

bool LogSystem::SetLogReceiver(LogReceiver* rec) {
	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}
	LogSingleton->logReceiverObj = rec;
	return true;
}

bool LogSystem::SetLogLevelDebug(uint8 level) {
	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}
	memset(LogSingleton->logLevelDebug, level, LOG_MAXCOUNT);
	return true;
}

bool LogSystem::SetLogLevelDebug(uint8 subject, uint8 level) {
	if (subject >= LOG_MAXCOUNT)
		return false;
	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}
	LogSingleton->logLevelDebug[subject] = level;
	return true;
}

bool LogSystem::SetLogLevelVerbose(uint8 level) {
	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}
	memset(LogSingleton->logLevelVerbose, level, LOG_MAXCOUNT);
	return true;
}

bool LogSystem::SetLogLevelVerbose(uint8 subject, uint8 level) {
	if (subject >= LOG_MAXCOUNT)
		return false;
	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}
	LogSingleton->logLevelVerbose[subject] = level;
	return true;
}


LogSystem::LogSystem() {
	logfile = NULL;
	printToStdOut = true;
}
LogSystem::~LogSystem() {
	if (logfile)
		delete [] logfile;
	logfile = NULL;
	printToStdOut = true;
}

const char* LogEntry::getText(uint32& len) {
	if (len = this->size - sizeof(LogEntry))
		return (char*)this+sizeof(LogEntry);
	else
		return NULL;
}

bool LogEntry::setText(char* text, uint32 len) {
	if (!text || !len)
		return false;
	int32 avail = (int32)size - sizeof(LogEntry) - 1;
	if (avail < (int32)len)
		return false;
	memcpy((char*)this+sizeof(LogEntry), text, len);
	*((char*)this+sizeof(LogEntry)+len) = 0;
	return true;
}

std::string LogEntry::toJSON() {
	uint32 len;
	const char* text = getText(len);
	return utils::StringFormat(
		"{ \"time\": %llu, \"level\": %u, \"source\": %u, \"subject\": %u, \"type\": %u, \"text\": \"%s\" }",
		time, level, source, subject, type, utils::EncodeJSON(text).c_str());
}

std::string LogEntry::toXML() {
	uint32 len;
	const char* text = getText(len);
	return utils::StringFormat(
		"<entry time=\"%llu\" level=\"%u\" source=\"%u\" subject=\"%u\" type=\"%u\" text=\"%s\" />\n",
		time, level, source, subject, type, html::EncodeHTML(text).c_str());
}


bool LogSystem::SetLogFileOutput(const char* logfile, bool printOut) {

	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}

	uint32 len;
	if (!logfile || !(len = (uint32)strlen(logfile))) {
		if (LogSingleton->logfile) {
			delete [] LogSingleton->logfile;
			LogSingleton->logfile = NULL;
		}
	}
	else {
		if (LogSingleton->logfile)
			delete [] LogSingleton->logfile;
		LogSingleton->logfile = new char[len+1];
		utils::strcpyavail(LogSingleton->logfile, logfile, len+1, true);
	}
	LogSingleton->printToStdOut = printOut;
	return true;
}

//#pragma data_seg(".shared")  // Begin the shared data segment.
LogSystem* LogSystem::LogSingleton = NULL;
//#pragma data_seg()          // End the shared data segment
//#pragma comment(linker, "/section:.shared,RWS")

bool LogSystem::LogSystemPrint(uint32 source, uint8 subject, uint8 level, const char *formatstring, ... ) {

	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}

	LogEntry* entry;
	char* str;
	uint32 len;
	bool res = true;

	if (LogSingleton->logLevelVerbose[subject] >= level) {
		va_list args;
		va_start(args, formatstring);
		str = utils::StringFormatVA(len, formatstring, args);
		va_end(args);
		if (!str || !len)
			res = false;
		else {
			if (LogSingleton->logReceiverObj) {
				entry = (LogEntry*) malloc(sizeof(LogEntry)+len+1);
				entry->size = sizeof(LogEntry)+len+1;
				entry->cid = LOGENTRYID;
				entry->time = GetTimeNow();
				entry->source = source;
				entry->subject = subject;
				entry->level = level;
				entry->type = LOGPRINT;
				entry->setText(str, len);
				free(str);
				return LogSingleton->logReceiverObj->logEntry(entry);
			}
			if (LogSingleton->logfile || LogSingleton->printToStdOut) {
				std::string strline = utils::StringFormat("%s %s\n", PrintTimeNowString().c_str(), str);
				if (LogSingleton->logfile && strlen(LogSingleton->logfile))
					utils::AppendToAFile(LogSingleton->logfile, strline.c_str(), (uint32)strline.length());
				if (LogSingleton->printToStdOut)
					std::cout << strline;
			}
		}
		free(str);
	}
	return res;
}

bool LogSystem::LogSystemDebug(uint32 source, uint8 subject, uint8 level, const char *formatstring, ... ) {

	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}

	LogEntry* entry;
	char* str;
	uint32 len;
	bool res = true;

	if (LogSingleton->logLevelDebug[subject] >= level) {
		va_list args;
		va_start(args, formatstring);
		str = utils::StringFormatVA(len, formatstring, args);
		va_end(args);
		if (!str || !len)
			res = false;
		else {
			if (LogSingleton->logReceiverObj) {
				entry = (LogEntry*) malloc(sizeof(LogEntry)+len+1);
				entry->size = sizeof(LogEntry)+len+1;
				entry->cid = LOGENTRYID;
				entry->time = GetTimeNow();
				entry->source = source;
				entry->subject = subject;
				entry->level = level;
				entry->type = LOGDEBUG;
				entry->setText(str, len);
				free(str);
				return LogSingleton->logReceiverObj->logEntry(entry);
			}
			else
				std::cout << PrintTimeNowString() << " [DEBUG] " << str << std::endl;
		}
		free(str);
	}
	return res;
}

	
namespace utils {

#if defined LINUX
	bool CalcTimeout(struct timespec &timeout, uint32 ms) {
		struct timeval now;
		if (gettimeofday(&now, NULL) != 0)
			return false;

		timeout.tv_sec = now.tv_sec + ms / 1000;
		long us = now.tv_usec + ((ms % 1000)*1000);
		if (us >= 1000000) {
			timeout.tv_sec++;
			us -= 1000000;
		}
		timeout.tv_nsec = us * 1000; // usec -> nsec
		return true;
	}

	uint64 GetTime() {
		struct timeval tv; 
		if ( gettimeofday(&tv, NULL))
			return 0; 
		return (tv.tv_usec + tv.tv_sec * 1000000LL);
	}
#endif

#ifdef WINDOWS
	VOID CALLBACK DrumBeatCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired) {
		DrumBeatInfo* info = (DrumBeatInfo*) lpParameter;
#else
	static void DrumBeatCallback(union sigval p) {
		DrumBeatInfo* info = (DrumBeatInfo*) p.sival_ptr;
#endif
		DrumBeatFunc func = info->func;
		if (!info->id || !info->createdTime || !info->started || !func)
			return;
		info->count++;
		if (!info->func(info->id, info->count))
			StopDrumBeat(info->id);
	}

bool CreateDrumBeat(uint32 id, uint32 interval, DrumBeatFunc func, bool autostart) {

	if (!DrumBeatMap)
		DrumBeatMap = new std::map<uint32, DrumBeatInfo>;
	DrumBeatInfo* info = &(*DrumBeatMap)[id];

	// check if it already exists
	if (info->id && info->createdTime)
		return false;

	// Windows threadpool size limit is 500, we probably shouldn't create more than that anyway
	// Could change the limit with WT_SET_MAX_THREADPOOL_THREAD
	if (DrumBeatMap->size() > 450)
		return false;

	info->id = id;
	info->createdTime = GetTimeNow();
	info->func = func;
	info->count = 0;
	info->interval = interval;

	#ifdef WINDOWS
	#else
		// struct sigaction sa;
		struct sigevent timer_event;

		//sigemptyset(&sa.sa_mask);
		//sa.sa_flags = SA_SIGINFO;   /* Real-Time signal */
		//sa.sa_sigaction = timer_signal_handler;
		//sigaction(SIGRTMIN, &sa, NULL);

		timer_event.sigev_notify = SIGEV_THREAD;
		timer_event.sigev_notify_attributes = NULL;
		timer_event.sigev_notify_function = DrumBeatCallback;
		timer_event.sigev_value.sival_ptr = (void *)info;
		int ret = timer_create(CLOCK_REALTIME, &timer_event, &info->handle);
		if (ret != 0) {
			LogPrint(0,LOG_SYSTEM,0,"Error creating timer: %d", ret);
		}
	#endif

	if (autostart)
		StartDrumBeat(id);
	return true;
}

bool StartDrumBeat(uint32 id) {
	if (!DrumBeatMap)
		return false;
	DrumBeatInfo* info = &(*DrumBeatMap)[id];
	if (!info->id || !info->createdTime)
		return false;

	if (info->started)
		return true;

	#ifdef WINDOWS
		// To compile an application that uses this function, define _WIN32_WINNT as 0x0500 or later
		CreateTimerQueueTimer(&info->handle, NULL, DrumBeatCallback, info, info->interval, info->interval, WT_EXECUTEINTIMERTHREAD);
		info->started = info->createdTime;
	#else
		struct itimerspec newtv;
		uint64 p = info->interval * 1000; // convert to nanosec
		newtv.it_interval.tv_sec = p / 1000000; 
		newtv.it_interval.tv_nsec = (p % 1000000)*1000; 
		newtv.it_value.tv_sec = p / 1000000; 
		newtv.it_value.tv_nsec = (p % 1000000)*1000; 
		
		int ret = timer_settime(info->handle, 0, &newtv, NULL);
		if (ret != 0) {
			LogPrint(0,LOG_SYSTEM,0,"Error arming timer: %d", ret);
		}
		info->started = info->createdTime;
	#endif
	return true;
}

bool StopDrumBeat(uint32 id) {
	if (!DrumBeatMap)
		return false;
	DrumBeatInfo* info = &(*DrumBeatMap)[id];
	if (!info->id || !info->createdTime)
		return false;

	if (!info->started)
		return true;

	#ifdef WINDOWS
		DeleteTimerQueueTimer(NULL, info->handle, NULL);
		info->started = 0;
		info->handle = NULL;
	#else
		struct itimerspec newtv;
		memset(&newtv, 0, sizeof(itimerspec));
		int ret = timer_settime(info->handle, 0, &newtv, NULL);
		if (ret != 0) {
			LogPrint(0,LOG_SYSTEM,0,"Error arming timer: %d", ret);
		}
		info->started = 0;
	#endif
	return true;
}

bool EndDrumBeat(uint32 id) {
	if (!DrumBeatMap)
		return false;
	DrumBeatInfo* info = &(*DrumBeatMap)[id];
	if (!info->id || !info->createdTime)
		return false;

	#ifdef WINDOWS
		if (info->started)
			StopDrumBeat(id);
	#else
		timer_delete(info->handle);
		info->started = 0;
		info->handle = 0;
	#endif

	info->createdTime = 0;
	info->func = NULL;
	info->count = 0;
	info->interval = 0;
	return true;
}


bool EnterMutex(const char* name, uint32 ms, bool autocreate) {
	if (!SharedMemoryMutexMap)
		SharedMemoryMutexMap = new std::map<std::string, Mutex*>;
	if (!SharedMemoryMutexMapMutex)
		SharedMemoryMutexMapMutex = new Mutex();

	Mutex* mutex;
	std::map<std::string, Mutex*>::iterator i = SharedMemoryMutexMap->find(name);
	std::map<std::string, Mutex*>::iterator e = SharedMemoryMutexMap->end();
	if (i == e) {
		SharedMemoryMutexMapMutex->enter();
		i = SharedMemoryMutexMap->find(name);
		if (i == e) {
			if (!autocreate) {
				SharedMemoryMutexMapMutex->leave();
				return false;
			}
			mutex = new Mutex(name);
			SharedMemoryMutexMap->insert(SharedMemoryMutexMapPair(name, mutex));
		}
		else {
			// try again
			if (i->second)
				mutex = i->second;
			else {
				if (!autocreate) {
					SharedMemoryMutexMapMutex->leave();
					return false;
				}
				mutex = new Mutex(name);
				SharedMemoryMutexMap->insert(SharedMemoryMutexMapPair(name, mutex));
			}
		}
		SharedMemoryMutexMapMutex->leave();
	}
	else
		mutex = i->second;
	return (mutex->enter(ms));	
}

bool LeaveMutex(const char* name) {
	if (!SharedMemoryMutexMap)
		SharedMemoryMutexMap = new std::map<std::string, Mutex*>;
	if (!SharedMemoryMutexMapMutex)
		SharedMemoryMutexMapMutex = new Mutex();

	std::map<std::string, Mutex*>::iterator i = SharedMemoryMutexMap->find(name);
	std::map<std::string, Mutex*>::iterator e = SharedMemoryMutexMap->end();
	if (i == e)
		return false;
	return (i->second->leave());
}

bool DestroyMutex(const char* name) {
	if (!SharedMemoryMutexMap)
		SharedMemoryMutexMap = new std::map<std::string, Mutex*>;
	if (!SharedMemoryMutexMapMutex)
		SharedMemoryMutexMapMutex = new Mutex();
	SharedMemoryMutexMapMutex->enter();

	std::map<std::string, Mutex*>::iterator i = SharedMemoryMutexMap->find(name);
	std::map<std::string, Mutex*>::iterator e = SharedMemoryMutexMap->end();
	if (i == e)
		return false;
	delete(i->second);
	SharedMemoryMutexMap->erase(i);
	SharedMemoryMutexMapMutex->leave();
	return true;
}








Semaphore* GetSemaphore(const char* name, bool autocreate) {
	if (!SharedMemorySemaphoreMap)
		SharedMemorySemaphoreMap = new std::map<std::string, Semaphore*>;
	if (!SharedMemorySemaphoreMapMutex)
		SharedMemorySemaphoreMapMutex = new Mutex();
	Semaphore* semaphore;
	std::map<std::string, Semaphore*>::iterator i = SharedMemorySemaphoreMap->find(name);
	std::map<std::string, Semaphore*>::iterator e = SharedMemorySemaphoreMap->end();
	if (i == e) {
		SharedMemorySemaphoreMapMutex->enter();
		i = SharedMemorySemaphoreMap->find(name);
		if (i == e) {
			if (!autocreate) {
				SharedMemorySemaphoreMapMutex->leave();
				return NULL;
			}
			semaphore = new Semaphore(name);
			SharedMemorySemaphoreMap->insert(SharedMemorySemaphoreMapPair(name, semaphore));
		}
		else {
			// try again
			if (i->second)
				semaphore = i->second;
			else {
				if (!autocreate) {
					SharedMemorySemaphoreMapMutex->leave();
					return NULL;
				}
				semaphore = new Semaphore(name);
				SharedMemorySemaphoreMap->insert(SharedMemorySemaphoreMapPair(name, semaphore));
			}
		}
		SharedMemorySemaphoreMapMutex->leave();
	}
	else
		semaphore = i->second;
	return semaphore;
}

bool WaitForSemaphore(const char* name, uint32 ms, bool autocreate) {
	if (!SharedMemorySemaphoreMap)
		SharedMemorySemaphoreMap = new std::map<std::string, Semaphore*>;
	if (!SharedMemorySemaphoreMapMutex)
		SharedMemorySemaphoreMapMutex = new Mutex();
	Semaphore* semaphore;
	std::map<std::string, Semaphore*>::iterator i = SharedMemorySemaphoreMap->find(name);
	std::map<std::string, Semaphore*>::iterator e = SharedMemorySemaphoreMap->end();
	if (i == e) {
		SharedMemorySemaphoreMapMutex->enter();
		i = SharedMemorySemaphoreMap->find(name);
		if (i == e) {
			if (!autocreate) {
				SharedMemorySemaphoreMapMutex->leave();
				return false;
			}
			semaphore = new Semaphore(name);
			SharedMemorySemaphoreMap->insert(SharedMemorySemaphoreMapPair(name, semaphore));
		}
		else {
			// try again
			if (i->second)
				semaphore = i->second;
			else {
				if (!autocreate) {
					SharedMemorySemaphoreMapMutex->leave();
					return false;
				}
				semaphore = new Semaphore(name);
				SharedMemorySemaphoreMap->insert(SharedMemorySemaphoreMapPair(name, semaphore));
			}
		}
		SharedMemorySemaphoreMapMutex->leave();
	}
	else
		semaphore = i->second;
	return (semaphore->wait(ms));
}

bool SignalSemaphore(const char* name) {
	if (!SharedMemorySemaphoreMap)
		SharedMemorySemaphoreMap = new std::map<std::string, Semaphore*>;
	if (!SharedMemorySemaphoreMapMutex)
		SharedMemorySemaphoreMapMutex = new Mutex();

	Semaphore* semaphore;
	std::map<std::string, Semaphore*>::iterator i = SharedMemorySemaphoreMap->find(name);
	std::map<std::string, Semaphore*>::iterator e = SharedMemorySemaphoreMap->end();
	if (i == e) {
		SharedMemorySemaphoreMapMutex->enter();
		i = SharedMemorySemaphoreMap->find(name);
		if (i == e) {
			semaphore = new Semaphore(name);
			SharedMemorySemaphoreMap->insert(SharedMemorySemaphoreMapPair(name, semaphore));
		}
		else {
			// try again
			if (i->second)
				semaphore = i->second;
			else {
				semaphore = new Semaphore(name);
				SharedMemorySemaphoreMap->insert(SharedMemorySemaphoreMapPair(name, semaphore));
			}
		}
		SharedMemorySemaphoreMapMutex->leave();
	}
	else
		semaphore = i->second;
	return (semaphore->signal());	
}

bool DestroySemaphore(const char* name) {
	if (!SharedMemorySemaphoreMap)
		SharedMemorySemaphoreMap = new std::map<std::string, Semaphore*>;
	if (!SharedMemorySemaphoreMapMutex)
		SharedMemorySemaphoreMapMutex = new Mutex();
	SharedMemorySemaphoreMapMutex->enter();

	std::map<std::string, Semaphore*>::iterator i = SharedMemorySemaphoreMap->find(name);
	std::map<std::string, Semaphore*>::iterator e = SharedMemorySemaphoreMap->end();
	if (i == e) {
		SharedMemorySemaphoreMapMutex->leave();
		return false;
	}
	delete(i->second);
	SharedMemorySemaphoreMap->erase(i);
	SharedMemorySemaphoreMapMutex->leave();
	return true;
}











bool WaitForNextEvent(const char* name, uint32 ms, bool autocreate) {
	if (!SharedMemoryEventMap)
		SharedMemoryEventMap = new std::map<std::string, Event*>;
	if (!SharedMemoryEventMapMutex)
		SharedMemoryEventMapMutex = new Mutex();
	Event* event;
	std::map<std::string, Event*>::iterator i = SharedMemoryEventMap->find(name);
	std::map<std::string, Event*>::iterator e = SharedMemoryEventMap->end();
	if (i == e) {
		SharedMemoryEventMapMutex->enter();
		i = SharedMemoryEventMap->find(name);
		if (i == e) {
			if (!autocreate) {
				SharedMemoryEventMapMutex->leave();
				return false;
			}
			event = new Event(name);
			SharedMemoryEventMap->insert(SharedMemoryEventMapPair(name, event));
		}
		else {
			// try again
			if (i->second)
				event = i->second;
			else {
				if (!autocreate) {
					SharedMemoryEventMapMutex->leave();
					return false;
				}
				event = new Event(name);
				SharedMemoryEventMap->insert(SharedMemoryEventMapPair(name, event));
			}
		}
		SharedMemoryEventMapMutex->leave();
	}
	else
		event = i->second;
	return (event->waitNext(ms));	
}

bool SignalEvent(const char* name) {
	if (!SharedMemoryEventMap)
		SharedMemoryEventMap = new std::map<std::string, Event*>;
	if (!SharedMemoryEventMapMutex)
		SharedMemoryEventMapMutex = new Mutex();
	std::map<std::string, Event*>::iterator i = SharedMemoryEventMap->find(name);
	std::map<std::string, Event*>::iterator e = SharedMemoryEventMap->end();
	if (i == e)
		return false;
	else
		return (i->second->signal());	
}

bool DestroyEvent(const char* name) {
	if (!SharedMemoryEventMap)
		SharedMemoryEventMap = new std::map<std::string, Event*>;
	if (!SharedMemoryEventMapMutex)
		SharedMemoryEventMapMutex = new Mutex();
	SharedMemoryEventMapMutex->enter();

	std::map<std::string, Event*>::iterator i = SharedMemoryEventMap->find(name);
	std::map<std::string, Event*>::iterator e = SharedMemoryEventMap->end();
	if (i == e)
		return false;
	delete(i->second);
	SharedMemoryEventMap->erase(i);
	SharedMemoryEventMapMutex->leave();
	return true;
}





bool SetSharedSystemInstance(uint32 inst) {
	SharedSystemInstance = inst;
	return true;
}

uint32 GetSharedSystemInstance() {
	return SharedSystemInstance;
}


Mutex::Mutex() {
	name = NULL;
	osid = 0;
	count = 0;
	total = 0;
	created = true;
	#ifdef WINDOWS
		mutex = ::CreateMutex(NULL, FALSE, NULL);
	#else
		mutex = new pthread_mutex_t;
		pthread_mutexattr_t attr;
		pthread_mutexattr_init(&attr);
		pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
		pthread_mutex_init(mutex, &attr);

		//semaphore = new sem_t;
		//if (sem_init(semaphore, 0, 1) != 0) {
		//	delete(semaphore);
		//	semaphore = NULL;
		//}

		pthread_mutexattr_destroy(&attr);
	#endif
}

Mutex::Mutex(const char* name, bool force) {
	this->name = new char[MAXSHMEMNAMELEN];
	osid = 0;
	count = 0;
	total = 0;
	created = true;
	#ifdef WINDOWS
		//sprintf(this->name, "Global\\Mutex_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		snprintf(this->name, MAXSHMEMNAMELEN, "%s\\Mutex_%s", WINSHMEMSPACE, name);
		mutex = ::CreateMutex(NULL, FALSE, this->name);
	#else
		mutex = NULL;
//		sprintf(this->name, "/Cond_%s", name);
//		semaphore = sem_open(name, O_CREAT, 0666, 1);

		snprintf(this->name, MAXSHMEMNAMELEN, "Mutex_%s", name);
		
		// test if shared memory exists
		char* data = NULL;
		
		if (!force)
			data = OpenSharedMemorySegment(this->name, MUTEXMEMSIZE);
		if (data) {
			mutex = (pthread_mutex_t*) data;
			created = false;
		}
		else {
			// create shared mutex
			if (data = CreateSharedMemorySegment(this->name, MUTEXMEMSIZE, force)) {
				// create mutex
				mutex = (pthread_mutex_t*) data;
				pthread_mutexattr_t attr;
				pthread_mutexattr_init(&attr);
				pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
				pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
				pthread_mutex_init(mutex, &attr);
				pthread_mutexattr_destroy(&attr);
			}
		}
	#endif
}

Mutex::~Mutex() {
	#ifdef WINDOWS
		if (mutex == NULL)
			return;
		CloseHandle(mutex);
		mutex = NULL;
		delete [] name;
	#else
		if (!name) {
			pthread_mutex_destroy(mutex);
		//	sem_destroy(semaphore);
		//	delete(semaphore);
		}
		else {
			if (created)
				pthread_mutex_destroy(mutex);
			CloseSharedMemorySegment((char*)mutex, MUTEXMEMSIZE);
			mutex = NULL;
		//	sem_close(semaphore);
			delete [] name;
		}
	#endif
}

bool Mutex::enter() {
	#ifdef WINDOWS
		DWORD reply;
		if ( (reply = WaitForSingleObject(mutex, INFINITE)) == WAIT_OBJECT_0) {
			utils::GetCurrentThreadOSID(osid);
//			if (count)
//				int n=0;
			count++;
			total++;
			return true;
		}
		else {
			if (reply != WAIT_ABANDONED) {
				if (name)
					LogPrint(0, LOG_SYSTEM, 0, "Lock of mutex failed: %s", name);
				else
					LogPrint(0, LOG_SYSTEM, 0, "Lock of unnamed mutex failed");
			}
			return false;
		}
	#else
		int error = pthread_mutex_lock(mutex);
		if (error == 0) {
			utils::GetCurrentThreadOSID(osid);
			count++;
			return true;
		}
		else {
			LogPrint(0, LOG_SYSTEM, 0, "Lock of mutex failed: %s (%d)", name ? name : "-", error);
			return false;
		}
	#endif
}

bool Mutex::enter(uint32 timeout, const char* errorMsg) {
//	printf("*** %s ***\n", errorMsg);
	#ifdef WINDOWS
		DWORD reply;
		if ((reply = WaitForSingleObject(mutex, timeout)) == WAIT_OBJECT_0) {
			utils::GetCurrentThreadOSID(osid);
			//if (count)
			//	printf("*** [%u]Mutex[%p](%s) locked more than once: %u\n", osid, mutex, errorMsg, count);
			//else
			//	printf("--- [%u]Mutex[%p](%s) locked\n", osid, mutex, errorMsg);
			count++;
			total++;
			return true;
		}
		if (errorMsg) {
			uint32 osid2;
			utils::GetCurrentThreadOSID(osid2);
			if (reply == WAIT_TIMEOUT)
				LogPrint(0,LOG_SYSTEM,0,"[%u]Mutex enter timeout[%p](%s) - currently held by %u, count %u, total %u",
				osid2, mutex, errorMsg, osid, count, total);
			else
				LogPrint(0,LOG_SYSTEM,0,"[%u]Mutex enter error[%p](%s): %u - currently held by %u, count %u, total %u",
				osid2, mutex, errorMsg, reply, osid, count, total);
		}
		return false;
	#else
		struct timespec ts;
		CalcTimeout(ts, timeout);
		int error = pthread_mutex_timedlock(mutex, &ts);
		if (error == 0) {
			utils::GetCurrentThreadOSID(osid);
			count++;
		//	if (count > 1)
		//		printf("Lock count now %u...\n", count);
		//	LogPrint(0,LOG_SYSTEM,0,"Mutex enter success: '%s' (%d) [%p] - I am %u", errorMsg, error, this, osid);
			return true;
		}
		else if (error == EINVAL) {
			LogPrint(0,LOG_SYSTEM,0,"Mutex enter error: '%s' (%d)", errorMsg ? errorMsg : "-", error);
			return false;
		}
//		if (errorMsg)
			LogPrint(0,LOG_SYSTEM,0,"Mutex enter timeout %u: '%s' (%d) [%p] - held by %u", timeout, errorMsg ? errorMsg : "-", error, this, osid);
		return false;
	
		//if (pthread_mutex_trylock(mutex) == 0)
		//	return true;

		//printf("[w-%s]", errorMsg);
		//fflush(stdout);
		//// Mutex is already locked, wait for the semaphore signal...
		//uint64 start = GetTimeNow();
		//struct timespec ts;
		//CalcTimeout(ts, timeout);

		//int res = 1;
		//do {
		//	if (sem_timedwait(semaphore, &ts) != 0)
		//		break;
		//	res = pthread_mutex_trylock(mutex);
		//} while ( !res && (GetTimeAgeMS(start) < (int32)timeout) );

		//if (res) {
		//	printf("s");
		//	fflush(stdout);
		//	return true;
		//}
		//if (errorMsg)
		//	LogPrint(0,LOG_SYSTEM,0,"Mutex enter timeout: %s", errorMsg);
		//return false;
	#endif
}

bool Mutex::leave() {
	#ifdef WINDOWS
		uint32 osid2 = osid;
		//utils::GetCurrentThreadOSID(osid2);
		if (count)
			count--;
		else
			return true;
		//if (osid != osid2)
		//	printf("*** [%u]Mutex[%p] unlocked by someone else: %u\n", osid, mutex, osid2);
		if (!count)
			osid = 0;
		if (ReleaseMutex(mutex) != 0)
			return true;
		else {
			// reinstate values
			count++;
			osid = osid2;
			return false;
		}
	#else
		if (pthread_mutex_unlock(mutex) != 0)
			return false;
		else {
			osid = 0;
			count--;
			return true;
		}

		// Signal semaphore
		// return (sem_post(semaphore) == 0);
	#endif
}

//bool Mutex::destroy() {
//	#ifdef WINDOWS
//	#else
//		pthread_mutex_destroy(mutex);
//		DestroySharedMemorySegment((char*)mutex, MUTEXMEMSIZE);
//		mutex = NULL;
//		sem_unlink(name);
//	#endif
//	return true;
//}













Semaphore::Semaphore(uint32 maxCount) {
	name = NULL;
	#ifdef WINDOWS
		semaphore = CreateSemaphore( 
			NULL,			// no security attributes
			0,				// initial count
			10,				// maximum count
			NULL);			// unnamed semaphore
	#else
		semaphore = new sem_t;
		if (sem_init(semaphore, 0, 1) != 0) {
			delete(semaphore);
			semaphore = NULL;
		}
	#endif
}

Semaphore::Semaphore(const char* name, uint32 maxCount) {
	this->name = new char[MAXSHMEMNAMELEN];
	#ifdef WINDOWS
		//sprintf(this->name, "Global\\Semaphore_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		snprintf(this->name, MAXSHMEMNAMELEN, "%s\\Semaphore_%s", WINSHMEMSPACE, name);
		// Creates or gets an existing Semaphore
		semaphore = CreateSemaphore( 
			NULL,			// no security attributes
			0,				// initial count
			maxCount,		// maximum count
			this->name);	// unnamed semaphore
	#else
		snprintf(this->name, MAXSHMEMNAMELEN, "/Semaphore_%s", name);
//		sprintf(this->name, "/Semaphore_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		semaphore = sem_open(this->name, O_CREAT, 0666, 1);
		if (semaphore == SEM_FAILED)
			semaphore = NULL;
	#endif
	// Rewrite the original name
	utils::strcpyavail(this->name, name, MAXKEYNAMELEN, true);
}

Semaphore::~Semaphore() {
	#ifdef WINDOWS
		if (semaphore) {
			CloseHandle(semaphore);
			semaphore = NULL;
		}
		delete [] name;
	#else
		// printf("Deleting semaphore '%s'...\n", name);
		if (!name) {
			sem_destroy(semaphore);
			delete(semaphore);
		}
		else {
			sem_close(semaphore);
			delete [] name;
		}
	#endif
}

bool Semaphore::wait() {
	#ifdef WINDOWS
		return (WaitForSingleObject(semaphore, INFINITE) == WAIT_OBJECT_0);
	#else
		int r = sem_wait(semaphore);
		return r == 0;
	#endif
}

bool Semaphore::wait(uint32 timeout) {
	#ifdef WINDOWS
		return (WaitForSingleObject(semaphore, timeout) == WAIT_OBJECT_0);
	#else
		struct timespec t;
		CalcTimeout(t, timeout);
		int r = sem_timedwait(semaphore, &t);
		return r == 0;
	#endif
}

bool Semaphore::signal() {
	#ifdef WINDOWS
		return (ReleaseSemaphore( 
				semaphore,	// handle to semaphore
				1,		// increase count by one
				NULL) != 0);	// not interested in previous count
	#else
		int r = sem_post(semaphore);
		return (r == 0);
	#endif
}

//bool Semaphore::destroy() {
//	#ifdef WINDOWS
//		return true;
//	#else
//		if (name)
//			sem_unlink(name);
//		return true;
//	#endif
//}













Event::Event() {
	name = NULL;
	#ifdef WINDOWS
		event = CreateEvent( 
			NULL,     // default security attributes
			TRUE,     // manual-reset event
			FALSE,    // initial state is nonsignaled
			NULL);	  // object name
	#else
		event = new pthread_cond_t;
		pthread_cond_init(event, NULL);

		mutex = new pthread_mutex_t;
		pthread_mutex_init(mutex, NULL);
	#endif
}

Event::Event(const char* name) {
	this->name = new char[MAXSHMEMNAMELEN];
	#ifdef WINDOWS
		//sprintf(this->name, "Global\\Event_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		snprintf(this->name, MAXSHMEMNAMELEN, "%s\\Event_%s", WINSHMEMSPACE, name);
		// Creates or gets an existing Event
		event = CreateEvent( 
			NULL,     // default security attributes
			TRUE,     // manual-reset event
			FALSE,    // initial state is nonsignaled
			this->name);	  // object name
	#else
		char* data = OpenSharedMemorySegment(this->name, EVENTMEMSIZE);
		if (data) {
			mutex = (pthread_mutex_t*) data;
			event = (pthread_cond_t*) (data + sizeof(pthread_mutex_t));
		}
		else {
			// create shared event
			if (data = CreateSharedMemorySegment(this->name, EVENTMEMSIZE, true)) {
				// create mutex
				mutex = (pthread_mutex_t*) data;
				pthread_mutexattr_t attr;
				pthread_mutexattr_init(&attr);
				pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
				pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
				pthread_mutex_init(mutex, &attr);
				pthread_mutexattr_destroy(&attr);

				event = (pthread_cond_t*) (data + sizeof(pthread_mutex_t));
				pthread_condattr_t cattr;
				pthread_condattr_init(&cattr);
				pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
				// pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
				pthread_cond_init(event, &cattr);
				pthread_mutexattr_destroy(&attr);
			}
		}
	#endif
}

Event::~Event() {
	#ifdef WINDOWS
		if (event) {
			CloseHandle(event);
			event = NULL;
		}
		delete [] name;
	#else
		if (!name) {
			pthread_cond_destroy(event);
			delete(event);
			pthread_mutex_destroy(mutex);
			delete(mutex);
		}
		else {
			pthread_cond_destroy(event);
			pthread_mutex_destroy(mutex);
			CloseSharedMemorySegment((char*)mutex, EVENTMEMSIZE);
			mutex = NULL;
			event = NULL;
			delete [] name;
		}
	#endif
}

bool Event::waitNext() {
	#ifdef WINDOWS
		return (WaitForSingleObject(event, INFINITE) == WAIT_OBJECT_0);
	#else
		pthread_mutex_lock(mutex);
		int r = pthread_cond_wait(event, mutex);
		pthread_mutex_unlock(mutex);
		return r == 0;
	#endif
}

bool Event::waitNext(uint32 timeout) {
	#ifdef WINDOWS
		return (WaitForSingleObject(event, timeout) == WAIT_OBJECT_0);
	#else
		pthread_mutex_lock(mutex);
		struct timespec t;
		CalcTimeout(t, timeout);
		int r = pthread_cond_timedwait(event, mutex, &t);
		pthread_mutex_unlock(mutex);
		return r == 0;
	#endif
}

bool Event::signal() {
	#ifdef WINDOWS
		if (!SetEvent(event))
			return false;
//		Sleep(1);
		ResetEvent(event);
		return true;
	#else
		pthread_mutex_lock(mutex);
		int r = pthread_cond_broadcast(event);
		pthread_mutex_unlock(mutex);
		return (r == 0);
	#endif
}

//bool Event::destroy() {
//	#ifdef WINDOWS
//		return true;
//	#else
//		if (name)
//			sem_unlink(name);
//		return true;
//	#endif
//}














std::map<uint32, Timer*>* Timer::timers = NULL;

Timer::Timer() {
	if (!timers)
		timers = new std::map<uint32, Timer*>;
	globalID = 0;
	while (timers->find(globalID) != timers->end())
		globalID++;
	(*timers)[globalID] = this;
}

Timer::~Timer() {
	if (!timers)
		return;
	timers->erase(globalID);
	mutex.enter();
	std::map<uint32, TimerSchedule*>::iterator it = schedules.begin();
	std::map<uint32, TimerSchedule*>::iterator itEnd = schedules.end();
	while (it != itEnd)
		removeTimer((it++)->first);

	// Now delete the remaining old schedules that were kept
	// in case a timer returned the same time as it was removed
	std::list<TimerSchedule*>::iterator it2 = oldSchedules.begin();
	std::list<TimerSchedule*>::iterator it2End = oldSchedules.end();
	while (it2 != it2End)
		delete(*it2++);
	oldSchedules.clear();

	mutex.leave();
}

bool Timer::addTimer(uint32 id, uint32 interval, uint64 start, uint64 end) {
	mutex.enter();
	if (schedules[id]) {
		mutex.leave();
		return false;
	}

	TimerSchedule* schedule = new TimerSchedule;
	schedule->globalID = globalID;
	schedule->id = id;
	schedule->handle = 0;
	schedule->start = start;
	schedule->end = end;
	schedule->interval = interval;
	schedules[id] = schedule;

	uint64 now = GetTimeNow();
	uint32 firstDelay = 0;
	if (start && (start > now))
		firstDelay = (uint32)(start - now)/1000;

	// Create and set timer
	#ifdef WINDOWS
		if (!CreateTimerQueueTimer(&schedule->handle, NULL, TimerCallback, schedule, firstDelay, interval, WT_EXECUTEINTIMERTHREAD)) {
			schedules[id] = NULL;
			delete(schedule);
			mutex.leave();
			return false;
		}
	#else
		struct sigaction sa;
		struct sigevent timer_event;

		sigemptyset(&sa.sa_mask);
		sa.sa_flags = SA_SIGINFO;   /* Real-Time signal */
		sa.sa_sigaction = TimerCallback;
		sigaction(SIGRTMIN, &sa, NULL);

		timer_event.sigev_notify = SIGEV_SIGNAL;
		timer_event.sigev_signo = SIGRTMIN;
		timer_event.sigev_value.sival_ptr = (void *)schedule;
		if (timer_create(CLOCK_REALTIME, &timer_event, &schedule->handle) != 0) {
			schedules[id] = NULL;
			delete(schedule);
			mutex.leave();
			return false;
		}

		struct itimerspec newtv;
		sigset_t allsigs;

		uint64 period = interval * 1000;
		newtv.it_value.tv_sec = firstDelay / 1000000; 
		newtv.it_value.tv_nsec = (firstDelay % 1000000)*1000; 
		newtv.it_interval.tv_sec = period / 1000000; 
		newtv.it_interval.tv_nsec = (period % 1000000)*1000; 
	  
		if (timer_settime(schedule->handle, 0, &newtv, NULL) != 0) {
			timer_delete(schedule->handle);
			schedules[id] = NULL;
			delete(schedule);
			mutex.leave();
			return false;
		}
		sigemptyset(&allsigs);
	#endif

	mutex.leave();
	return true;
}

bool Timer::removeTimer(uint32 id) {
	mutex.enter();

	TimerSchedule* schedule = schedules[id];
	if (!schedule) {
		mutex.leave();
		return true;
	}

	schedules[id] = NULL;

	#ifdef WINDOWS
		DeleteTimerQueueTimer(NULL, schedule->handle, NULL);
	#else
		timer_delete(schedule->handle);
	#endif

	oldSchedules.push_back(schedule);
	mutex.leave();
	return true;
}

bool Timer::triggerTimer(uint32 id) {
	mutex.enter();
	TimerSchedule* schedule = schedules[id];
	if (!schedule) {
		mutex.leave();
		return false;
	}

	TimerTrigger* trigger = new TimerTrigger;
	trigger->id = schedule->id;
	trigger->time = GetTimeNow();

	triggers.push(trigger);
	semaphore.signal();

	if ( schedule->end && ((int32)(schedule->end - trigger->time) < (int32)schedule->interval))
		removeTimer(schedule->id);

	mutex.leave();
	return true;
}

bool Timer::waitForTimer(uint32 timeout, uint32& id, uint64& time) {
	TimerTrigger* trigger;
	uint64 start = GetTimeNow();
	int64 timeleft;

	mutex.enter();
	while (!triggers.size()) {
		mutex.leave();
		if ( (timeleft = (int32)timeout - GetTimeAgeMS(start)) <= 0)
			return false;
		semaphore.wait((uint32)timeleft);
		mutex.enter();
	}

	trigger = triggers.front();
	triggers.pop();
	id = trigger->id;
	time = trigger->time;
	delete(trigger);
	mutex.leave();
	return true;
}

#ifdef WINDOWS
	void CALLBACK TimerCallback(PVOID arg, BOOLEAN TimerOrWaitFired) {
		if (!Timer::timers)
			return;
		TimerSchedule* schedule = (TimerSchedule*)arg;
		if (!schedule)
			return;
		Timer* timer = (*Timer::timers)[schedule->globalID];
		if (timer)
			timer->triggerTimer(schedule->id);
	}
#else
	void TimerCallback(int sig, siginfo_t *siginfo, void *context) {
		if (!Timer::timers)
			return;
		TimerSchedule* schedule = (TimerSchedule*) siginfo->si_value.sival_ptr;
		if (!schedule)
			return;
		Timer* timer = (*Timer::timers)[schedule->globalID];
		if (timer)
			timer->triggerTimer(schedule->id);
	}
#endif

bool UnitTest_Timer() {
	uint32 id;
	uint64 time, now = GetTimeNow();

	Timer* timer = new Timer();

	timer->addTimer(1, 1000, now+50000, now+10000000);
	timer->addTimer(2, 2000, now+55000, now+15000000);
	timer->addTimer(3, 3000, now+60000, now+20000000);

	for (uint32 n=0; n<60; n++) {
		if (timer->waitForTimer(500, id, time)) {
			now = GetTimeNow();
			LogPrint(0, LOG_SYSTEM, 0, "Timer %u triggered %.3f ms ago", id, (now-time)/1000.0);
		}
		else {
			LogPrint(0, LOG_SYSTEM, 0, "(Timeout)", id, PrintTimeString(time).c_str());
		}
	}

	delete(timer);
	return true;
}

bool UnitTest_Utils() {
	uint32 slotCount = 350;
	uint32 bitFieldSize = Calc32BitFieldSize(slotCount);
	char* bitField = new char[bitFieldSize];
	uint32 loc;

	// First set the full new bitfield to unoccupied
	Reset32BitField(bitField, bitFieldSize);
	//memset(bitField, 255, bitFieldSize);
//	printf("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str()); fflush(stdout);

	if (GetLastOccupiedBitLoc(bitField, bitFieldSize, loc)) {
		printf("Loc wrong: %u, expected: false\n", loc);
		return false;
	}

	if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 0))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 0))
		return false;

	if (!SetBit(10, BITOCCUPIED, bitField, bitFieldSize))
		return false;
//	printf("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str()); fflush(stdout);

	if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 0))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 0))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11))
		return false;
	if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 10)) {
		printf("Loc wrong: %u, expected: 10\n", loc);
		return false;
	}

	if (!SetBit(0, BITOCCUPIED, bitField, bitFieldSize))
		return false;
	if (!SetBitN(100, 50, BITOCCUPIED, bitField, bitFieldSize))
		return false;
//	printf("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str()); fflush(stdout);

	if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 1))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 11))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 100, loc) || (loc != 150))
		return false;
	if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 149)) {
		printf("Loc wrong: %u, expected: 249\n", loc);
		return false;
	}

	if (!SetBitN(200, 50, BITOCCUPIED, bitField, bitFieldSize))
		return false;
//	printf("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str()); fflush(stdout);

	if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 1))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 11))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 100, loc) || (loc != 250))
		return false;
	if (GetFirstFreeBitLocN(bitField, bitFieldSize, 200, loc))
		return false;
	if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 249)) {
		printf("Loc wrong: %u, expected: 249\n", loc);
		return false;
	}

	if (!SetBitN(210, 30, BITFREE, bitField, bitFieldSize))
		return false;
//	printf("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str()); fflush(stdout);

	if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 1))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 11))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 52, loc) || (loc != 11))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 100, loc) || (loc != 250))
		return false;
	if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 249)) {
		printf("Loc wrong: %u, expected: 249\n", loc);
		return false;
	}

	if (!SetBitN(50, 30, BITOCCUPIED, bitField, bitFieldSize))
		return false;
//	printf("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str()); fflush(stdout);

	if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 1))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 11))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 52, loc) || (loc != 250))
		return false;
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 100, loc) || (loc != 250))
		return false;
	if (GetFirstFreeBitLocN(bitField, bitFieldSize, 200, loc))
		return false;
	if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 249)) {
		printf("Loc wrong: %u, expected: 249\n", loc);
		return false;
	}

	delete[] bitField;
	printf("BitField operations tested successfully...\n");
	return true;
}








// Create named shared memory segment
char* CreateSharedMemorySegment(const char* name, uint64 size, bool force) {

	char* memname = new char[MAXSHMEMNAMELEN];
	#ifdef WINDOWS
		//sprintf(memname, "Global\\Shmem_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		snprintf(memname, MAXSHMEMNAMELEN, "%s\\Shmem_%s", WINSHMEMSPACE, name);
		HANDLE hMapFile;
		char* pBuf;

		hMapFile = CreateFileMapping(
			INVALID_HANDLE_VALUE,   // use paging file
			NULL,                   // default security 
			PAGE_READWRITE,         // read/write access
			(uint32)((size >> 32) & 0xffffffff),                      // maximum object size (high-order DWORD) 
			(uint32)(size & 0xffffffff),                   // maximum object size (low-order DWORD)  
			memname);                  // name of mapping object

		if (hMapFile == NULL) { 
			LogPrint(0,LOG_SYSTEM,0, "Could not create shared memory '%s': %s", memname, GetLastOSErrorMessage().c_str());
			delete [] memname;
			return NULL;
		}
		delete [] memname;
		pBuf = (char*) MapViewOfFile(hMapFile,   // handle to map object
			FILE_MAP_ALL_ACCESS, // read/write permission
			0, 0, 0);            // from beginning to end

		if (pBuf == NULL) { 
			int error = GetLastError(); 
			CloseHandle(hMapFile);
			return NULL;
		}

		if (!SharedMemoryFileHandleMap)
			SharedMemoryFileHandleMap = new SharedMemoryFileHandleMapType;
		// Save the handle so we can close it later
		(*SharedMemoryFileHandleMap)[pBuf] = hMapFile;

		return pBuf;
	#else
		//sprintf(memname, "/Shmem_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		snprintf(memname, MAXSHMEMNAMELEN, "/Shmem_%s", name);
		/* Create shared memory object and set its size */

		int fd = shm_open(memname, O_CREAT | O_RDWR, 0666);
		if (fd == -1) {
//			if (!force) {
				LogPrint(0,LOG_SYSTEM,0,"Couldn't create shared memory: '%s' (err: %d)...", memname, errno);
				delete [] memname;
				return NULL;
			//}
			//shm_unlink(memname);
			//fd = shm_open(memname, O_CREAT | O_EXCL, 0666);
			//if (fd == -1) {
			//	LogPrint(0,LOG_SYSTEM,0,"Couldn't create forced shared memory: '%s' (err: %d)...", memname, errno);
			//	delete [] memname;
			//	return NULL;
			//}
		}

		if (ftruncate(fd, size) == -1) {
			LogPrint(0,LOG_SYSTEM,0,"Couldn't truncate shared memory: '%s' size: %u (%d) (err: %d)...", memname, size, fd, errno);
			delete [] memname;
			close(fd);
			return NULL;
		}

		/* Map shared memory object */

		char* pBuf = (char*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
		if (pBuf == MAP_FAILED) {
			LogPrint(0,LOG_SYSTEM,0,"Couldn't map created shared memory: '%s' (err: %d)...", memname, errno);
			delete [] memname;
			close(fd);
			return NULL;
		}

		// On Linux you can close the handle, but need the name string to close the memory later
		if (!SharedMemoryFileHandleMap)
			SharedMemoryFileHandleMap = new SharedMemoryFileHandleMapType;
		(*SharedMemoryFileHandleMap)[pBuf] = memname;
		//printf("Created '%s' = %p\n", (*SharedMemoryFileHandleMap)[pBuf].c_str(), pBuf);
		close(fd);

		//printf("Created shared memory '%s' %p size %u...\n", memname, pBuf, size);

		delete [] memname;
		return pBuf;
	#endif
}

// Get named shared memory segment, size = 0 means autodetect size
char* OpenSharedMemorySegment(const char* name, uint64 size) {

	char* memname = new char[MAXSHMEMNAMELEN];
	#ifdef WINDOWS
		// sprintf(memname, "Global\\Shmem_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		snprintf(memname, MAXSHMEMNAMELEN, "%s\\Shmem_%s", WINSHMEMSPACE, name);
		HANDLE hMapFile;
		char* pBuf;

		hMapFile = OpenFileMapping(
			FILE_MAP_ALL_ACCESS,   // read/write access
			FALSE,                 // do not inherit the name
			memname);                 // name of mapping object 

		if (hMapFile == NULL) { 
		//	LogPrint(0,LOG_SYSTEM,0, "Could not open shared memory '%s': %s", memname, GetLastOSErrorMessage().c_str());
			delete [] memname;
			return NULL;
		} 

		pBuf = (char*) MapViewOfFile(hMapFile, // handle to map object
			FILE_MAP_ALL_ACCESS,  // read/write permission
			0, 0, 0);             // from beginning to end

		if (pBuf == NULL) { 
			LogPrint(0,LOG_SYSTEM,0, "Could not map shared memory '%s': %s", memname, GetLastOSErrorMessage().c_str());
			delete [] memname;
			GetLastError(); 
			CloseHandle(hMapFile);
			return NULL;
		}
		delete [] memname;

		// Save the handle so we can close it later
		if (!SharedMemoryFileHandleMap)
			SharedMemoryFileHandleMap = new SharedMemoryFileHandleMapType;
		(*SharedMemoryFileHandleMap)[pBuf] = hMapFile;

		return pBuf;
	#else
		//sprintf(memname, "/Shmem_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		snprintf(memname, MAXSHMEMNAMELEN, "/Shmem_%s", name);
		/* Open shared memory object and set its size */

		int fd = shm_open(memname, O_RDWR, 0666);

		if (fd == -1) {
			//LogPrint(0,LOG_SYSTEM,0, "Couldn't open shared memory: '%s' (err: %d)...\n", memname, errno);
			delete [] memname;
			return NULL;
		}

		//if (ftruncate(fd, size) == -1) {
		//	printf("Auch 5: '%s'...\n", memname);
		//	delete [] memname;
		//	close(fd);
		//	return NULL;
		//}

		/* Map shared memory object */

		char* pBuf;
		
		uint64 memSize = size;
		if (!size) {
			// auto-detect size from the first uint32 value
			pBuf = (char*)mmap(NULL, sizeof(uint64), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
			if (pBuf == MAP_FAILED) {
				LogPrint(0,LOG_SYSTEM,0,"Couldn't map opened shared memory: '%s' (err: %d)...", memname, errno);
				delete [] memname;
				close(fd);
				return NULL;
			}
			memSize = *((uint64*)pBuf);
			munmap(pBuf, sizeof(uint64));
		}
		pBuf = (char*)mmap(NULL, memSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
		if (pBuf == MAP_FAILED) {
		//	LogPrint(0,LOG_SYSTEM,0,"Couldn't map opened shared memory: '%s' (err: %d)...", memname, errno);
			delete [] memname;
			close(fd);
			return NULL;
		}

		// On Linux you can close the handle, but need the name string to close the memory later
		if (!SharedMemoryFileHandleMap)
			SharedMemoryFileHandleMap = new SharedMemoryFileHandleMapType;
		(*SharedMemoryFileHandleMap)[pBuf] = memname;
		//printf("Opened '%s' = %p\n", (*SharedMemoryFileHandleMap)[pBuf].c_str(), pBuf);
		close(fd);

		// printf("Opened shared memory '%s' size %u...\n", memname, memSize);
		delete [] memname;
		return pBuf;
	#endif
}

// Close named shared memory segment
bool CloseSharedMemorySegment(char* data, uint64 size) {
	if (!data || !size)
		return true;
	#ifdef WINDOWS
		UnmapViewOfFile(data);
		CloseHandle((*SharedMemoryFileHandleMap)[data]);
		SharedMemoryFileHandleMap->erase(data);
		return true;
	#else
		//printf("Closing '%s' = %p\n", (*SharedMemoryFileHandleMap)[data].c_str(), data);
		//SharedMemoryFileHandleMapType::iterator it = SharedMemoryFileHandleMap->begin(), itEnd = SharedMemoryFileHandleMap->end();
		//while (it != itEnd) {
		//	printf("   '%s' = %p\n", it->second.c_str(), it->first);
		//	it++;
		//}
		munmap(data, size);
		shm_unlink((*SharedMemoryFileHandleMap)[data].c_str());
		//printf("Closed shared memory '%s' %p size %u...\n", (*SharedMemoryFileHandleMap)[data].c_str(), data, size);
		SharedMemoryFileHandleMap->erase(data);
		return true;
	#endif
}

// Destroy named shared memory segment
//bool DestroySharedMemorySegment(char* data, uint32 size) {
//	#ifdef WINDOWS
//		UnmapViewOfFile(data);
//		CloseHandle(SharedMemoryFileHandleMap[data]);
//		SharedMemoryFileHandleMap[data] = NULL;
//		return true;
//	#else
//		munmap(data, size);
//		close(SharedMemoryFileHandleMap[data]);
//		SharedMemoryFileHandleMap[data] = NULL;
//		return (shm_unlink(data) == 0);
//	#endif
//}











uint32 Calc32BitFieldSize(uint32 bitsize) {
	return (uint32)ceil((double)bitsize / 32) * 4;
}

bool Reset32BitField(char* bitfield, uint32 bytesize) {
	memset(bitfield, 255, bytesize);
	return true;
}

// Get first zero bit in field of size bits, starting from data loc
bool GetFirstFreeBitLoc(const char* bitfield, uint32 bytesize, uint32& loc) {
	uint32* data = (uint32*)bitfield;
	uint32* src = (uint32*)data;
	int32 index;

	while ((*src == 0)) {
		src++;
		if (src - data > (int32)bytesize)
			return false;
	}
	#if defined	WINDOWS
		_BitScanForward((DWORD*)&index, *src);
	#elif defined LINUX
		index = ffsl(*src) - 1;
	#endif

	loc = ((uint32)(src-data)*32) + index;
	return true;
}

// Get first block of num zero bit in field of size bits, starting from data loc
bool GetFirstFreeBitLocN(const char* bitfield, uint32 bytesize, uint32 num, uint32& loc) {
	uint32* data = (uint32*)bitfield;
	uint32* src = (uint32*)data;
	int32 index;

	uint32 l;
	bit val;

	uint32 foundBlock = 0;
	while (true) {
		while ((*src == 0)) {
			src++;
			if (src - data > (int32)bytesize)
				return false;
		}
		#if defined	WINDOWS
			_BitScanForward((DWORD*)&index, *src);
		#elif defined LINUX
			index = ffsl(*src) - 1;
		#endif
		loc = ((uint32)(src - data) * 32) + index;
		l = loc + 1;
		foundBlock = 1;
		// check for num sized block until the end of the next int32 boundary
		if (!GetBit(l, bitfield, bytesize, val))
			return false;
		while (true) {
			l++;
			index++;
			if (val == BITFREE) {
				foundBlock++;
				if (foundBlock >= num)
					return true;
				if (index % 32 == 0) {
					while ((num - foundBlock > 31) && (*src == MAXVALUINT32)) {
						foundBlock += 32;
						l += 32;
						src++;
					}
					if ((num - foundBlock > 15) && (*(uint16*)src == MAXVALUINT16)) {
						foundBlock += 16;
						l += 16;
						index += 16;
						if ((num - foundBlock > 7) && (*((char*)src+2) == 255)) {
							foundBlock += 8;
							l += 8;
							index += 8;
						}
					}
					else if ((num - foundBlock > 7) && (*(char*)src == 255)) {
						foundBlock += 8;
						l += 8;
						index += 8;
					}
				}
				if (foundBlock >= num)
					return true;
			}
			else {
				foundBlock = 0;
				loc = l;
				// are we on a boundary
				if (index % 32 == 0)
					break;
			}
			if (!GetBit(l, bitfield, bytesize, val))
				return false;
		}
		// we are now on a boundary, jump back and use the bitscan search
		src += (uint32)(index / 32);
		index = 0;
		// if we get here, the block found wasn't big enough, continue
	}
	return true;
}

// Set the nth bit to value in field of size bits, starting from data loc
bool SetBit(uint32 loc, bit value, char* bitfield, uint32 bytesize) {
	if (ceil((double)loc/8) > bytesize)
		return false;
	uint32* data = (uint32*)bitfield;
	uint32* src = (uint32*)data;
	uint32 n = loc;
	while (n > 31) {
		n -= 32;
		src++;
	}

	#if defined	WINDOWS
		if (value != 0)
			_bittestandset((long*)src, n);
		else
			_bittestandreset((long*)src, n);
	#elif defined LINUX
		if (value != 0)
			*src |= 1<<n;
		else
			*src &= ~(1<<n);
			//*src &= ((1<<n)^0xFFFFFFFF);
	#elif defined OSX
	#endif
	return true;
}

// Set the block of num bits starting with nth bit to value in field of size bits, starting from data loc
bool SetBitN(uint32 loc, uint32 num, bit value, char* bitfield, uint32 bytesize) {
	if (ceil((double)loc / 8) > bytesize)
		return false;
	uint32* data = (uint32*)bitfield;
	uint32* src = (uint32*)data;
	uint32 n = loc;
	while (n > 31) {
		n -= 32;
		src++;
	}

	for (uint32 i = 0; i < num; i++) {
		if (!n) {
			while (num - i > 31) {
				*src = (value ? MAXVALUINT32 : 0);
				i += 32;
				src++;
			}
			if (num - i > 15) {
				*(uint16*)src = (value ? MAXVALUINT16 : 0);
				i += 16;
				n += 16;
				if (num - i > 7) {
					*((char*)src + 2) = (value ? 255 : 0);
					i += 8;
					n += 8;
				}
			}
			else if (num - i > 7) {
				*(char*)src = (value ? 255 : 0);
				i += 8;
				n += 8;
			}
			if (i == num)
				return true;
		}
		#if defined	WINDOWS
			if (value != 0)
				_bittestandset((long*)src, n);
			else
				_bittestandreset((long*)src, n);
		#elif defined LINUX
			if (value != 0)
				*src |= 1 << n;
			else
				*src &= ~(1 << n);
			//*src &= ((1<<n)^0xFFFFFFFF);
		#elif defined OSX
		#endif
		n++;
		if (n > 31) {
			n = 0;
			src++;
		}
	}
	return true;
}

// Get the nth bit in field of size bits, starting from data loc
bool GetBit(uint32 loc, const char* bitfield, uint32 bytesize, bit& val) {
	if (ceil((double)loc/8) > bytesize)
		return false;
	uint32* data = (uint32*)bitfield;
	uint32* src = (uint32*)data;
	uint32 n = loc;
	while (n > 31) {
		n -= 32;
		src++;
	}

	#if defined	WINDOWS
		val = (_bittest((long*)src, n) != 0);
	#elif defined LINUX
		uint32 mask = static_cast<uint32>( 1 << n ) ;
		val = (bit)(mask & *src);
	#elif defined OSX
	#endif
//	printf("GetBit returned %u...\n", val);
	return true;
}

// Get location of last bit in use, starting from data loc
bool GetLastOccupiedBitLoc(const char* bitfield, uint32 bytesize, uint32& loc) {
	uint32* data = (uint32*)bitfield;
	uint32* src = (uint32*)(bitfield + bytesize - 4);
	int32 index;

	loc = 0;
	while ((*src == 0xFFFFFFFF)) {
		if (src <= data)
			return false;
		src--;
	}

	#if defined	WINDOWS
		_BitScanReverse((DWORD*)&index, ~(*src));
	#elif defined LINUX
		index = 31 - __builtin_clz(~(*src));
	#elif defined OSX
	#endif
	loc = ((uint32)(src - data) * 32) + index;
	return true;
}


// Return string representing the bitfield
std::string GetBitFieldAsString(const char* bitfield, uint32 bytesize, uint32 size) {
	//char* str = new char[size+1];
	std::string str;
	
	uint32 i,j,n=0;
	for(i = 0; i < bytesize; i++) {
		for(j = 0; j < 8; j++) {
			if (++n <= size)
				str += (bitfield[i] & (1 << j)) ? '_' : '0';
			else
				str += '*';
		}
		str += ' ';
		//if (n >= size)
		//	return str;
	};
	return str;
}


// Print string representing the bitfield
std::string PrintBitFieldString(const char* bitfield, uint32 bytesize, uint32 size, const char* title) {
	std::string str = GetBitFieldAsString(bitfield, bytesize, size);
	uint32 strSize = (uint32)str.size();
	if (!strSize)
		return "";
	if (strSize < 73) {
		if (title != NULL)
			return StringFormat("%s [%u]: %s\n", title, size, str.c_str());
		else
			return StringFormat("%s\n", str.c_str());
	}
	else {
		std::string str2;
		if (title != NULL)
			str2 = StringFormat("%s [%u]:\n", title, size);
		uint32 loc = 0;
		while (loc < strSize-1) {
			str2 += str.substr(loc, 36) + "\n";
			loc += 36;
		}
		if (loc < strSize)
			str2 += str.substr(loc) + "\n";
		return str2;
	}
}




int32 AtomicIncrement32(int32 volatile &v) {
	#if defined	WINDOWS
		return	InterlockedIncrement((LONG*)&v);
	#elif defined LINUX
		__sync_add_and_fetch(&v, 1);
		return	v;
	#endif
};

int64 AtomicIncrement64(int64 volatile &v) {
	#if defined	WINDOWS
		return	InterlockedIncrement64((LONGLONG*)&v);
	#elif defined LINUX
		__sync_add_and_fetch(&v, 1);
		return	v;
	#endif
};

int32 AtomicDecrement32(int32 volatile &v) {
	#if defined	WINDOWS
		return	InterlockedDecrement((LONG*)&v);
	#elif defined LINUX
		__sync_add_and_fetch(&v, -1);
		return	v;
	#endif
};

int64 AtomicDecrement64(int64 volatile &v) {
	#if defined	WINDOWS
		return	InterlockedDecrement64((LONGLONG*)&v);
	#elif defined LINUX
		__sync_add_and_fetch(&v, -1);
		return	v;
	#endif
};

bool Sleep(uint32 ms) {
	#if defined	WINDOWS
		// we are actually being passed millisecond, so multiply up
		::Sleep((uint32)ms);
	#else
		usleep(ms*1000);
	#endif
	return true;
}

std::string PrintProgramTrace(uint32 startLine, uint32 endLine) {
	std::string tracePrint;
	std::list<std::string> trace = GetProgramTrace();
	
	std::list<std::string>::iterator i = trace.begin(), e = trace.end();

	uint32 line = 0;
	while (i != e) {
		if ((++line > startLine) && (!endLine || (line <= endLine)))
			tracePrint += " -" + (*i) + "\n";
		i++;
	}
	return tracePrint;
}

std::list<std::string> GetProgramTrace() {
	std::list<std::string> trace;
	#ifdef WINDOWS
		// this needs Dbghelp.h included and link with Dbghelp.lib
		//unsigned int   i;
		//void         * stack[ 100 ];
		//unsigned short frames;
		//SYMBOL_INFO  * symbol;
		//HANDLE         process;

		//process = GetCurrentProcess();

		//SymInitialize( process, NULL, TRUE );

		//frames               = CaptureStackBackTrace( 0, 100, stack, NULL );
		//symbol               = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 );
		//symbol->MaxNameLen   = 255;
		//symbol->SizeOfStruct = sizeof( SYMBOL_INFO );

		//for( i = 0; i < frames; i++ ) {
		//	SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol );
		//	trace.push_back(StringFormat("%i: %s   (0x%0X)", frames - i - 1, symbol->Name, symbol->Address));
		//}
		//free( symbol );
	#else
		#ifdef _DEBUG
			void *array[200];
			size_t size;
			char **strings;
			size_t i;
			size = backtrace(array, 200);
			strings = backtrace_symbols(array, size);
			for (i = 0; i < size; i++)
				trace.push_back(strings[i]);
			free (strings);
		#endif // _DEBUG
	#endif
	return trace;
}


bool CreateThread(THREAD_FUNCTION func, void* args, ThreadHandle& thread, uint32 &osID) {

	#if defined	WINDOWS
		DWORD dwThreadId;
		thread = ::CreateThread( 
			NULL,							// no security attributes 
			0,								// use default stack size  
			(LPTHREAD_START_ROUTINE) func,	// thread function 
			args,							// argument to thread function 
			0,								// use default creation flags 
			&dwThreadId);					// returns the thread identifier 

		if (thread != NULL) {
			osID = ::GetThreadId(thread);
			Sleep(0);
			return true;
		}
		else
			return false;
	#else
		pthread_attr_t attr;
		pthread_attr_init(&attr); /* initialize attr with default attributes */
		// pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
		pthread_attr_setschedpolicy(&attr, SCHED_RR);
		pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
		// pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);

		int res;
		if ((res=pthread_create(&thread, &attr, func, args)) != 0)
			return false;
		int oldstate;
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
		pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate);
		osID = (uint32) thread;
		// printf("create hThread: %u\n", thread);
		Sleep(0);
		return true;
	#endif
}

bool WaitForThreadToFinish(ThreadHandle hThread) {
	// Sadly, the Linux implementation of join doesn't allow a timeout, so to be compatible
	// we cannot allow the Windows to have this either, although it could (and should) have
	#if defined	WINDOWS
		int res = WaitForSingleObject(hThread, INFINITE);
		if(res != WAIT_OBJECT_0)
			return false;
		return (CloseHandle(hThread) != 0);
	#else
		if (pthread_join(hThread, NULL) != 0)
			return false;
	#endif
}

bool TerminateThread(ThreadHandle hThread) {
	if (!hThread)
		return true;
	#if defined	WINDOWS
		// printf("cancel hThread: %u\n", hThread);
		bool res = (::TerminateThread(hThread, 0) != 0);
		if (!res) {
			char* msg = new char[2048];
			int length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
				NULL, GetLastError(), 0, msg, sizeof(msg), NULL);
			msg[length] = '\0';
			LogPrint(0, LOG_SYSTEM, 0, "Error terminating thread: %s", msg);
			delete[] msg;
		}
		CloseHandle(hThread);
		return res;
	#else
		// printf("cancel hThread: %u\n", hThread);
		pthread_cancel(hThread);
		if (pthread_join(hThread, NULL) != 0)
			return false;
	#endif
}

bool PauseThread(ThreadHandle hThread) {
	#if defined	WINDOWS
		return (SuspendThread(hThread) >= 0);
	#else
		return (pthread_kill(hThread, SIGSTOP) == 0);
	#endif
	return false;
}

bool ContinueThread(ThreadHandle hThread) {
	#if defined	WINDOWS
		DWORD count;
		while ( (count = ResumeThread(hThread)) > 1);
		return (count == 1);
	#else
		return (pthread_kill(hThread, SIGCONT) == 0);
	#endif
	return false;
}

bool GetCurrentThreadUniqueID(uint32 &tid) {
	// This function returns an ID unique to the thread on a given OS
	// On Windows this is the Thread OS ID
	// On Linux we cannot read other thread OS IDs so it has to be the
	// thread handle converted to a 32bit uint.
	#if defined	WINDOWS
		tid = ::GetCurrentThreadId();
		return true;
	#else
		tid = (uint32) pthread_self();
		return true;
	#endif
}

bool GetCurrentThreadOSID(uint32 &tid) {
	#if defined	WINDOWS
		tid = ::GetCurrentThreadId();
		return true;
	#else
		int id = syscall(SYS_gettid);
		if (id <= 0)
			return false;
		tid = (uint32)id;
		return true;
	#endif
}

bool GetCurrentThread(ThreadHandle& thread) {
	#if defined	WINDOWS
		thread = ::GetCurrentThread();
		return true;
	#else
		thread = pthread_self();
		return true;
	#endif
}

bool IsThreadRunning(ThreadHandle hThread) {
	#if defined	WINDOWS
		DWORD code;
		if (!GetExitCodeThread(hThread, &code))
			return false;
		return (code == STILL_ACTIVE);
	#else
		// printf("*** IsThreadRunning: %llu\n", (uint64)hThread);
		return (pthread_tryjoin_np(hThread, NULL) != 0);
	#endif
}

uint32 GetThreadStatColAbility() {
	#if defined	WINDOWS
		return THREAD_STATS_AUTO;
	#else
		#if defined RUSAGE_THREAD
			return THREAD_STATS_ADHOC;
		#else
			return THREAD_STATS_OFF;
		#endif
	#endif
}

bool GetCPUTicks(ThreadHandle hThread, uint64& ticks) {
	ticks = 0;

	#if defined	WINDOWS
		if (!QueryThreadCycleTime(hThread, &ticks))
			return false;
		//FILETIME creationTime;
		//FILETIME exitTime;
		//FILETIME kernelTime;
		//FILETIME userTime;
		//if (!GetThreadTimes(hThread, &creationTime, &exitTime, &kernelTime, &userTime))
		//	return false;
		//userCPU = ((uint64)userTime.dwLowDateTime | (((uint64)userTime.dwHighDateTime) << 32)) / 10;
		//kernelCPU = ((uint64)kernelTime.dwLowDateTime | (((uint64)kernelTime.dwHighDateTime) << 32)) / 10;
		return true;
	#else
		clockid_t cid;
		if (pthread_getcpuclockid(hThread, &cid) != 0)
			return false;
		timespec ts;
		if (clock_gettime(cid, &ts) != -1 ) {
			ticks = ((uint64)ts.tv_sec * 1000000) + ts.tv_nsec;
			return true;
		}
		else
			return false;
		// For now, pthreads do not support getrusage from other threads
		// return false;
	#endif
	return true;
}

bool GetCPUTicks(uint64& ticks) {
	ticks = 0;

	#if defined	WINDOWS
		if (!QueryThreadCycleTime(::GetCurrentThread(), &ticks))
			return false;
		//FILETIME creationTime;
		//FILETIME exitTime;
		//FILETIME kernelTime;
		//FILETIME userTime;
		//if (!GetThreadTimes(::GetCurrentThread(), &creationTime, &exitTime, &kernelTime, &userTime))
		//	return false;
		//userCPU = ((uint64)userTime.dwLowDateTime | (((uint64)userTime.dwHighDateTime) << 32)) / 10;
		//kernelCPU = ((uint64)kernelTime.dwLowDateTime | (((uint64)kernelTime.dwHighDateTime) << 32)) / 10;
		return true;
	#else
		timespec ts;
		if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != -1 ) {
			ticks = ((uint64)ts.tv_sec * 1000000) + ts.tv_nsec;
			return true;
		}
		else
			return false;
		//#if defined RUSAGE_THREAD
		//	struct rusage us;
		//	if (getrusage(RUSAGE_THREAD, &us) == 0) { // RUSAGE_THREAD // accumulate_thread_rusage
		//		userCPU = (us.ru_utime.tv_sec * 1000000) + us.ru_utime.tv_usec;
		//		kernelCPU = (us.ru_stime.tv_sec * 1000000) + us.ru_stime.tv_usec;
		//	}
		//	else
		//		return false;
		//#else
		//	return false;
		//#endif
	#endif
}

bool GetProcessCPUTicks(uint32 osProcID, uint64& ticks) {
	ticks = 0;

	#if defined	WINDOWS
		HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, osProcID);
		if (!hProcess)
			return false;
		if (!QueryProcessCycleTime(hProcess, &ticks))
			return false;
		//FILETIME creationTime;
		//FILETIME exitTime;
		//FILETIME kernelTime;
		//FILETIME userTime;
		//if (!GetProcessTimes(::GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime))
		//	return false;
		//userCPU = ((uint64)userTime.dwLowDateTime | (((uint64)userTime.dwHighDateTime) << 32)) / 10;
		//kernelCPU = ((uint64)kernelTime.dwLowDateTime | (((uint64)kernelTime.dwHighDateTime) << 32)) / 10;
		return true;
	#else
		clockid_t clockid;
		if (clock_getcpuclockid(osProcID, &clockid) != 0)
			return false;
		timespec ts;
		if (clock_gettime(clockid, &ts) != -1 ) {
			ticks = ((uint64)ts.tv_sec * 1000000) + ts.tv_nsec;
			return true;
		}
		else
			return false;
		//#if defined RUSAGE_THREAD
		//	struct rusage us;
		//	if (getrusage(RUSAGE_SELF, &us) == 0) { // RUSAGE_THREAD // accumulate_thread_rusage
		//		userCPU = (us.ru_utime.tv_sec * 1000000) + us.ru_utime.tv_usec;
		//		kernelCPU = (us.ru_stime.tv_sec * 1000000) + us.ru_stime.tv_usec;
		//	}
		//	else
		//		return false;
		//#else
		//	return false;
		//#endif
	#endif
	return true;
}

bool GetProcessCPUTicks(uint64& ticks) {
	ticks = 0;

	#if defined	WINDOWS
		if (!QueryProcessCycleTime(::GetCurrentProcess(), &ticks))
			return false;
		//FILETIME creationTime;
		//FILETIME exitTime;
		//FILETIME kernelTime;
		//FILETIME userTime;
		//if (!GetProcessTimes(::GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime))
		//	return false;
		//userCPU = ((uint64)userTime.dwLowDateTime | (((uint64)userTime.dwHighDateTime) << 32)) / 10;
		//kernelCPU = ((uint64)kernelTime.dwLowDateTime | (((uint64)kernelTime.dwHighDateTime) << 32)) / 10;
		return true;
	#else
		timespec ts;
		if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) != -1 ) {
			ticks = ((uint64)ts.tv_sec * 1000000) + ts.tv_nsec;
			return true;
		}
		else
			return false;
		//#if defined RUSAGE_THREAD
		//	struct rusage us;
		//	if (getrusage(RUSAGE_SELF, &us) == 0) { // RUSAGE_THREAD // accumulate_thread_rusage
		//		userCPU = (us.ru_utime.tv_sec * 1000000) + us.ru_utime.tv_usec;
		//		kernelCPU = (us.ru_stime.tv_sec * 1000000) + us.ru_stime.tv_usec;
		//	}
		//	else
		//		return false;
		//#else
		//	return false;
		//#endif
	#endif
	return true;
}

bool GetProcAndOSCPUUsage(double& procPercentUse, double& osPercentUse, uint64 &prevOSTicks, uint64 &prevOSIdleTicks, uint64 &prevProcTicks) {
	#if defined	WINDOWS
		return GetProcAndOSCPUUsage(0, procPercentUse, osPercentUse, prevOSTicks, prevOSIdleTicks, prevProcTicks);
	#else
		return GetProcAndOSCPUUsage(getpid(), procPercentUse, osPercentUse, prevOSTicks, prevOSIdleTicks, prevProcTicks);
	#endif
	return true;
}

bool GetProcAndOSCPUUsage(uint32 osProcID, double& procPercentUse, double& osPercentUse, uint64 &prevOSTicks, uint64 &prevOSIdleTicks, uint64 &prevProcTicks) {
	procPercentUse = osPercentUse = 0;

	uint64 totalTicks, kernelTicks, userTicks, idleTicks, procTotalTicks, procUserTicks, procKernelTicks;

	#if defined	WINDOWS
		FILETIME idleTime, kernelTime, userTime;
		if (!GetSystemTimes(&idleTime, &kernelTime, &userTime))
			return false;

		idleTicks = FileTimeToUint64(idleTime);
		kernelTicks = FileTimeToUint64(kernelTime);
		userTicks = FileTimeToUint64(userTime);
		totalTicks = kernelTicks + userTicks;

		FILETIME procCreationTime, procExitTime, procKernelTime, procUserTime;

		if (!osProcID) {
			if (!GetProcessTimes(GetCurrentProcess(), &procCreationTime, &procExitTime, &procKernelTime, &procUserTime))
				return false;
		}
		else {
			HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, osProcID);
			if (!hProcess)
				return false;

			if (!GetProcessTimes(hProcess, &procCreationTime, &procExitTime, &procKernelTime, &procUserTime))
				return false;
		}

		procKernelTicks = FileTimeToUint64(procKernelTime);
		procUserTicks = FileTimeToUint64(procUserTime);
		procTotalTicks = procKernelTicks + procUserTicks;

		// printf("Proc User: %llu  Kernel: %llu\n", procUserTicks, procKernelTicks);

	#else
		std::string statLine, procStatLine;
		std::ifstream statFile ("/proc/stat");
		std::ifstream procStatFile (StringFormat("/proc/%d/stat", osProcID).c_str());
		if (statFile.is_open())
			getline (statFile, statLine);
		else
			return false;

		if (procStatFile.is_open())
			getline (procStatFile, procStatLine);
		else
			return false;

		if (!statLine.length() || !procStatLine.length())
			return false;

		//cpu  56344 20038 65629 5095979 55549 9083 10717 0 0 0
		uint64 fields[10];
		int retval = sscanf(statLine.c_str(), "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", 
							&fields[0], &fields[1], &fields[2], &fields[3], &fields[4],
							&fields[5], &fields[6], &fields[7], &fields[8], &fields[9]); 
		totalTicks = 0;
		for (int i=0; i<10; i++)
			totalTicks += fields[i];
		idleTicks = fields[3];

		// printf("stat: %llu idle: %llu\n", totalTicks, idleTicks);

		// 18238 (Psyclone) S 7893 18238 7893 34816 18238 1077960704 15233  0   2   0   3     2    0    0    20 0 42 0 2669130 1478500352 16465 18446744073709551615 4194304 5248240 140737088300880 140737088299744 241797488320 0 0 0 0 18446744073709551615 0 0 17 1 0 0 0 0 0 7346592 7358192 12959744 140737088309161 140737088309201 140737088309201 140737088311267 0
		// %*d      %*s    %*c %*d  %*d  %*d   %*d   %*d      %*u     %*u  %*u %*u %*u %llu %llu %llu %llu"
		uint64 utime_ticks, stime_ticks, cutime_ticks, cstime_ticks;
		retval = sscanf(procStatLine.c_str(), "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %llu %llu %llu %llu",
                &utime_ticks, &stime_ticks, &cutime_ticks, &cstime_ticks); 

		procTotalTicks = utime_ticks + stime_ticks + cutime_ticks + cstime_ticks;

		// printf("Proc User: %llu  Kernel: %llu\n", utime_ticks + cutime_ticks, stime_ticks + cstime_ticks);

		// Ref: http://stackoverflow.com/questions/1420426/calculating-cpu-usage-of-a-process-in-linux/1424556#1424556
	#endif

	uint64 totalTicksSinceLastTime = totalTicks - prevOSTicks;
	uint64 idleTicksSinceLastTime  = idleTicks - prevOSIdleTicks;
	uint64 procTicksSinceLastTime  = procTotalTicks - prevProcTicks;

	osPercentUse = 1.0f-((totalTicksSinceLastTime > 0) ? ((double)idleTicksSinceLastTime)/totalTicksSinceLastTime : 0);
	procPercentUse = ((totalTicksSinceLastTime > 0) ? ((double)procTicksSinceLastTime)/totalTicksSinceLastTime : 0);

//	printf("Idle: %llu   Total: %llu      %.2f%%\n",
//		idleTicksSinceLastTime, totalTicksSinceLastTime, percentUse*100);

	prevOSTicks = totalTicks;
	prevOSIdleTicks  = idleTicks;
	prevProcTicks = procTotalTicks;

	return true;
}

bool GetOSCPUUsage(double& percentUse) {
	percentUse = 0;

	static uint64 PreviousTotalTicks = 0;
	static uint64 PreviousIdleTicks = 0;

	uint64 totalTicks, kernelTicks, userTicks, idleTicks;

	#if defined	WINDOWS

		FILETIME idleTime, kernelTime, userTime;
		if (!GetSystemTimes(&idleTime, &kernelTime, &userTime))
			return false;

		idleTicks = FileTimeToUint64(idleTime);
		kernelTicks = FileTimeToUint64(kernelTime);
		userTicks = FileTimeToUint64(userTime);
		totalTicks = kernelTicks + userTicks;

	#else
		uint32 size;
		char* procData = utils::ReadAFile("/proc/stat", size);
		if (!procData || !size)
			return false;

		unsigned long long int fields[10];
		int retval = sscanf (procData, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", 
							&fields[0], 
							&fields[1], 
							&fields[2], 
							&fields[3], 
							&fields[4], 
							&fields[5], 
							&fields[6], 
							&fields[7], 
							&fields[8], 
							&fields[9]); 
		for (int i=0, totalTicks = 0; i<10; i++)
			totalTicks += fields[i];
		idleTicks = fields[3];
	#endif

	uint64 totalTicksSinceLastTime = totalTicks - PreviousTotalTicks;
	uint64 idleTicksSinceLastTime  = idleTicks - PreviousIdleTicks;

	percentUse = 1.0f-((totalTicksSinceLastTime > 0) ? ((double)idleTicksSinceLastTime)/totalTicksSinceLastTime : 0);

//	printf("Idle: %llu   Total: %llu      %.2f%%\n",
//		idleTicksSinceLastTime, totalTicksSinceLastTime, percentUse*100);

	PreviousTotalTicks = totalTicks;
	PreviousIdleTicks  = idleTicks;

	return true;
}

bool GetThreadPriority(ThreadHandle hThread, uint16 priority) {
	
	#if defined	WINDOWS
		uint16 prio = ::GetThreadPriority(hThread);
		priority = FromOSPriority(prio);
	#else
		struct sched_param sched;
		int policy;
		policy = sched_getscheduler(0);
		if(policy < 0)
			return false;
		
		if (pthread_getschedparam(hThread, &policy, &sched) != 0)
			return false;

		priority = FromOSPriority(sched.sched_priority);
	#endif
	return true;
}

bool SetThreadPriority(ThreadHandle hThread, uint16 priority) {
	#if defined	WINDOWS
		return (::SetThreadPriority(hThread, ToOSPriority(priority)) != 0);
	#else
		int policy;
		struct sched_param sched;
		memset(&sched, 0, sizeof(struct sched_param));

		policy = SCHED_FIFO;
		// policy = SCHED_RR;
		sched.sched_priority = ToOSPriority(priority);

		int res = pthread_setschedparam(hThread, policy, &sched);
		if (res != 0)
			return false;
	#endif
	return true;
}

int ToOSPriority(int pri) {
	int val;

	#if defined	WINDOWS
		if (pri == 0)
			val = THREAD_PRIORITY_NORMAL;
		else if (pri >= 90)
			val = THREAD_PRIORITY_TIME_CRITICAL;
		else if (pri >= 80)
			val = THREAD_PRIORITY_HIGHEST;
		else if (pri >= 70)
			val = THREAD_PRIORITY_ABOVE_NORMAL;
		else if (pri >= 60)
			val = THREAD_PRIORITY_NORMAL;
		else if (pri >= 50)
			val = THREAD_PRIORITY_BELOW_NORMAL;
		else if (pri >= 40)
			val = THREAD_PRIORITY_LOWEST;
		else
			val = THREAD_PRIORITY_IDLE;
	#else
		if (pri == 0)
			val = 40;
		else if (pri >= 90)
			val = 60;
		else if (pri >= 80)
			val = 55;
		else if (pri >= 70)
			val = 40;
		else if (pri >= 60)
			val = 35;
		else if (pri >= 50)
			val = 30;
		else if (pri >= 40)
			val = 25;
		else if (pri >= 30)
			val = 20;
		else if (pri >= 20)
			val = 15;
		else if (pri >= 10)
			val = 10;
		else
			val = 5;
	#endif

	return val;
}

int FromOSPriority(int pri) {

	#if defined	WINDOWS
	switch (pri) {
		case THREAD_PRIORITY_TIME_CRITICAL:
			return 80;
		case THREAD_PRIORITY_HIGHEST:
			return 60;
		case THREAD_PRIORITY_ABOVE_NORMAL:
			return 55;
		case THREAD_PRIORITY_NORMAL:
			return 50;
		case THREAD_PRIORITY_BELOW_NORMAL:
			return 40;
		case THREAD_PRIORITY_LOWEST:
			return 30;
		default:
			return 20;
	}
	#else
		return pri;
	#endif
}

bool SignalThread(ThreadHandle hThread, int32 signal) {
	#if defined	WINDOWS
		// ######################################
	#else
		// ######################################
	#endif
	return false;
}




//////////////////////////////////////////////////////////////
//                       Desktop                            //
//////////////////////////////////////////////////////////////

bool GetDesktopSize(uint32& width, uint32& height) {
	width = height = 0;
	#if defined	WINDOWS
		RECT desktop;
		// Get a handle to the desktop window
		const HWND hDesktop = GetDesktopWindow();
		// Get the size of screen to the variable desktop
		if (!GetWindowRect(hDesktop, &desktop))
			return false;
		// The top left corner will have coordinates (0,0)
		// and the bottom right corner will have coordinates
		// (horizontal, vertical)
		width = (uint32)desktop.right;
		height = (uint32)desktop.bottom;
		return true;
	#else
		return false;
	#endif
}

bool RenameConsoleWindow(const char* name, bool prepend) {
	#if defined	WINDOWS
		if (prepend) {
			uint32 size;
			char* oldName = new char[1024];
			if (!GetConsoleTitle(oldName, 1024)) {
				delete [] oldName;
				return false;
			}
			char* newName = StringFormat(size, "%s - %s", name, oldName);
			bool res = (SetConsoleTitle(name) != 0);
			delete [] newName;
			delete [] oldName;
			return res;
		}
		else
			return (SetConsoleTitle(name) != 0);
	#else
		return false;
	#endif
}

bool MoveConsoleWindow(int32 x, int32 y, int32 w, int32 h) {
	#if defined	WINDOWS
		char* consoleName = new char[1024];
		if (!GetConsoleTitle(consoleName, 1024)) {
			delete [] consoleName;
			return false;
		}
		HWND handle = FindWindow(NULL, consoleName);
		if (!handle) {
			delete [] consoleName;
			return false;
		}
		RECT wRect;
		if (!GetWindowRect(handle, &wRect)) {
			delete [] consoleName;
			return false;
		}

		int conX = (x<0) ? wRect.left : x;
		int conY = (y<0) ? wRect.top : y;
		int conW = (w<0) ? wRect.right - wRect.left : w;
		int conH = (h<0) ? wRect.bottom - wRect.top : h;

		bool res = (MoveWindow(handle, conX, conY, conW, conH, true) != 0);
		delete [] consoleName;
		return res;

	#else
		return false;
	#endif
}


//////////////////////////////////////////////////////////////
//                      Processes                           //
//////////////////////////////////////////////////////////////

uint32 GetLocalProcessID() {
	#if defined	WINDOWS
		return (uint32)GetCurrentProcessId();
	#else
		return (uint32)getpid();
	#endif
}


int RunOSCommand(const char* cmdline, const char* initdir, uint32 timeout, std::string& stdoutString, std::string& stderrString) {
	if (!cmdline || !strlen(cmdline))
		return -1;

	#if defined	WINDOWS
		HANDLE g_hChildStd_OUT_Rd = NULL;
		HANDLE g_hChildStd_OUT_Wr = NULL;
		HANDLE g_hChildStd_ERR_Rd = NULL;
		HANDLE g_hChildStd_ERR_Wr = NULL;

		SECURITY_ATTRIBUTES sa; 
		// Set the bInheritHandle flag so pipe handles are inherited. 
		sa.nLength = sizeof(SECURITY_ATTRIBUTES); 
		sa.bInheritHandle = TRUE; 
		sa.lpSecurityDescriptor = NULL; 
		// Create a pipe for the child process's STDERR. 
		if ( ! CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &sa, 0) ) {
			return -1;
		}
		// Ensure the read handle to the pipe for STDERR is not inherited.
		if ( ! SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0) ){
			return -1;
		}
		// Create a pipe for the child process's STDOUT. 
		if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0) ) {
			return -1;
		}
		// Ensure the read handle to the pipe for STDOUT is not inherited
		if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ){
			return -1;
		}
		PROCESS_INFORMATION piProcInfo; 
		STARTUPINFO siStartInfo;
		bool bSuccess = FALSE; 

		// Set up members of the PROCESS_INFORMATION structure. 
		ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

		// Set up members of the STARTUPINFO structure. 
		// This structure specifies the STDERR and STDOUT handles for redirection.
		ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
		siStartInfo.cb = sizeof(STARTUPINFO); 
		siStartInfo.hStdError = g_hChildStd_ERR_Wr;
		siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
		siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

		uint32 size = 0;
		char* cmd = utils::StringFormat(size, cmdline);

		// Create the child process. 
		bSuccess = (CreateProcess(NULL,
			cmd,     // command line 
			NULL,          // process security attributes 
			NULL,          // primary thread security attributes 
			TRUE,          // handles are inherited 
			0,             // creation flags 
			NULL,          // use parent's environment 
			initdir,          // use parent's current directory 
			&siStartInfo,  // STARTUPINFO pointer 
			&piProcInfo) != 0);  // receives PROCESS_INFORMATION
		CloseHandle(g_hChildStd_ERR_Wr);
		CloseHandle(g_hChildStd_OUT_Wr);
		// If an error occurs, exit the application. 
		if ( ! bSuccess ) {
			return -1;
		}
	
		uint64 startTime = GetTimeNow();

		DWORD dwRead; 
		char* chBuf = new char[PROCBUFSIZE];
		bSuccess = FALSE;
		for (;;) { 
			bSuccess=(ReadFile( g_hChildStd_OUT_Rd, chBuf, PROCBUFSIZE, &dwRead, NULL) != 0);
			if( ! bSuccess || dwRead == 0 ) break; 
			std::string s(chBuf, dwRead);
			stdoutString += s;
			if(GetTimeAgeMS(startTime) > (int32)timeout) break; 
		} 
		dwRead = 0;
		for (;;) { 
			bSuccess=(ReadFile( g_hChildStd_ERR_Rd, chBuf, PROCBUFSIZE, &dwRead, NULL) != 0);
			if( ! bSuccess || dwRead == 0 ) break; 
			std::string s(chBuf, dwRead);
			stderrString += s;
			if(GetTimeAgeMS(startTime) > (int32)timeout) break; 
		}
		delete [] chBuf;

		DWORD exitcode;
		if (GetExitCodeProcess(piProcInfo.hProcess, &exitcode) == STILL_ACTIVE) {
			TerminateProcess(piProcInfo.hProcess, 1);
		}
		CloseHandle(g_hChildStd_ERR_Rd);
		CloseHandle(g_hChildStd_OUT_Rd);
		CloseHandle(piProcInfo.hProcess);
		CloseHandle(piProcInfo.hThread);
		return exitcode; 
	#else
		fflush(stdin);
		fflush(stdout);
		FILE* runfile = popen(cmdline, "r");
		if (runfile == NULL)
			return -1;

		int exitcode = -1;
		int status;
		int res;
		int size = 4096;
		char* buffer = new char[size+1];
		
		res = fread(buffer, 1, size, runfile);
		if (res <= 0) {
			status = pclose(runfile);
			exitcode = WEXITSTATUS(status);
			delete [] buffer;
			return exitcode;
		}
		do {
			buffer[res] = 0;
			stdoutString += buffer;
		} while (res = fread(buffer, 1, size, runfile));

		status = pclose(runfile);
		exitcode = WEXITSTATUS(status);
		delete [] buffer;
		return exitcode;

	//
	//int size = 1024;
	//	char name[1024];
	//	if (!RunOSTextCommand("uname -n", name, size))
	//		return "";
	//	
	//
	//int argc = 0;
	//	char **argv=SplitCommandline(cmdline, argc);
	//	if (!argc || !argv)
	//		return -1;

	//	/* since pipes are unidirectional, we need two pipes.
	//	   one for data to flow from parent's stdout to child's
	//	   stdin and the other for child's stdout to flow to
	//	   parent's stdin */

	//	int* pipes = new int[NUM_PIPES][2];
	//	int outfd[2];
	//	int infd[2];

	//	// pipes for parent to write and read
	//	pipe((*pipes)[PARENT_READ_PIPE]);
	//	pipe((*pipes)[PARENT_WRITE_PIPE]);

	//	if(!fork()) {

	//		dup2(CHILD_READ_FD, STDIN_FILENO);
	//		dup2(CHILD_WRITE_FD, STDOUT_FILENO);

	//		/* Close fds not required by child. Also, we don't
	//			want the exec'ed program to know these existed */
	//		close(CHILD_READ_FD);
	//		close(CHILD_WRITE_FD);
	//		close(PARENT_READ_FD);
	//		close(PARENT_WRITE_FD);

	//		execv(argv[0], argv);
	//	} else {
	//		char buffer[100];
	//		int count;

	//		/* close fds not required by parent */       
	//		close(CHILD_READ_FD);
	//		close(CHILD_WRITE_FD);

	//		// Write to child’s stdin
	//		//write(PARENT_WRITE_FD, "2^32\n", 5);

	//		// Read from child’s stdout
	//		count = read(PARENT_READ_FD, buffer, sizeof(buffer)-1);
	//		if (count >= 0) {
	//			buffer[count] = 0;
	//			printf("%s", buffer);
	//		} else {
	//			printf("IO Error\n");
	//		}
	//		// ################
	//		// Not yet complete!!!
	//	}
	#endif

}


uint32 NewProcess(const char* cmdline, const char* initdir, const char* title, int16 x, int16 y, int16 w, int16 h) {

	if (!cmdline || !strlen(cmdline))
		return 0;

	#if defined	WINDOWS

		if (!ProcessInformationMap)
			ProcessInformationMap = new std::map<uint32, ProcessData*>;

		ProcessData* pData = new ProcessData;

		ZeroMemory(&(pData->procInfo), sizeof(pData->procInfo));
		ZeroMemory(&(pData->si), sizeof(pData->si));
		pData->si.cb = sizeof(pData->si);
		pData->si.wShowWindow = SW_SHOWNOACTIVATE; //SW_SHOW; // SW_HIDE;

		if ( (x>=0) && (y>=0) && (w>=0) && (h>=0) ) {
			pData->si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USEPOSITION | STARTF_USESIZE;
			pData->si.dwX = x;
			pData->si.dwY = y;
			pData->si.dwXSize = w;
			pData->si.dwYSize = h;
		}
		else {
			pData->si.dwFlags = STARTF_USESHOWWINDOW;
		}

		uint32 size = 0;
		char* titleCopy = NULL;
		if (title) {
			titleCopy = utils::StringFormat(size, title);
			pData->si.lpTitle = titleCopy;
		}

		char* cmd = utils::StringFormat(size, cmdline);
		//utils::strcpyavail(cmd, cmdline, (uint32)strlen(cmdline)+1, true);

		// DETACHED_PROCESS 
		int res = CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, initdir, &pData->si, &pData->procInfo);
		delete [] titleCopy;
		delete [] cmd;
		if (res == 0) {
			char* msg = new char[2048];
			int length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 
					   NULL, GetLastError(), 0, msg, sizeof(msg), NULL);
			msg[length] = '\0';
			LogPrint(0, LOG_SYSTEM, 0, "Error creating new process: %s\n%s", msg, cmd);
			delete [] msg;
			delete pData;
			return 0;
		}
		ProcessInformationMap->insert(Proc_Pair((uint32)pData->procInfo.dwProcessId, pData));
		return pData->procInfo.dwProcessId;
		
	#else

		//std::vector<std::string> args = utils::TextListSplit(cmdline, " ");
		//int argc = args.size();
		//// const char** argv = new const char*[argc+1];
		//char** argv = new char*[argc+1];
		//int len;
		//for (int n=0; n<argc; n++) {
		//	argv[n] = new char[args[n].size() + 1];
		//	utils::strcpyavail(argv[n], args[n].c_str(), args[n].size()+1, true);
		//}
		//argv[argc] = NULL;

		int argc = 0;
		char** argv = SplitCommandline(cmdline, argc);
		if (!argc)
			return -1;

		int pid = fork();

		if (pid < 0) {
			DeleteCommandline(argv, argc);
			return 0;
		}
		// We are the child process
		else if (pid == 0) {

			if ( initdir && (chdir(initdir) != 0) ) {
				LogPrint(0,LOG_SYSTEM,0,"Could not run process '%s' from starup dir '%s'\n", cmdline, initdir);
				exit(0);
			}

			execvp(argv[0],argv);
			// here we exit the program, if we continue the call failed...

			int res = errno;
			//  If program didn't take over the exec call failed.
			LogPrint(0,LOG_SYSTEM,0,"Could not create new process: %s (%d)!\n", cmdline, res);
			exit(0);

		}
		// we are in the calling process
		else {
			DeleteCommandline(argv, argc);
			return pid;
		}

	#endif
}

uint8 GetProcessStatus(uint32 proc, int &returncode) {
	returncode = 0;
	#if defined	WINDOWS
		DWORD exitcode;
		std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
		if (i != ProcessInformationMap->end()) {
			ProcessData* pData = i->second;
			if (GetExitCodeProcess(pData->procInfo.hProcess, &exitcode) == STILL_ACTIVE) {
				return PROC_RUNNING;
			}
			else {
				ProcessInformationMap->erase(i);
				CloseHandle(pData->procInfo.hProcess);
				CloseHandle(pData->procInfo.hThread);
				delete pData;
				returncode = exitcode;
				return PROC_TERMINATED;
			}
		}
		else {
			// We didn't create this process, look it up by id
			HANDLE hproc = OpenProcess(NULL, false, (DWORD)proc);
			if (!hproc)
				return PROC_ERROR;
			if (GetExitCodeProcess(hproc, &exitcode) == STILL_ACTIVE) {
				CloseHandle(hproc);
				return PROC_RUNNING;
			}
			else {
				CloseHandle(hproc);
				return PROC_TERMINATED;
			}
		}
	#else
		int status;
		int res = waitpid(proc, &status, WNOHANG);

		if (res == proc) {
		  //if (WIFEXITED(status)) {
			 //  printf("exited, status=%d\n", WEXITSTATUS(status));
		  // } else if (WIFSIGNALED(status)) {
			 //  printf("killed by signal %d\n", WTERMSIG(status));
		  // } else if (WIFSTOPPED(status)) {
			 //  printf("stopped by signal %d\n", WSTOPSIG(status));
		  // } else if (WIFCONTINUED(status)) {
			 //  printf("continued\n");
		  // }
			returncode = WEXITSTATUS(status);
			//printf("Return code: %d\n", returncode);
			return PROC_TERMINATED;
		}
		else if (res == 0) {
			return PROC_RUNNING;
		}
	#endif
}

bool EndProcess(uint32 proc) {
	#if defined	WINDOWS
		if (!ProcessInformationMap)
			return true;
		std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
		if (i != ProcessInformationMap->end()) {
			ProcessData* pData = i->second;
			TerminateProcess(pData->procInfo.hProcess, 1);
			CloseHandle(pData->procInfo.hProcess);
			CloseHandle(pData->procInfo.hThread);
			ProcessInformationMap->erase(i);
			delete pData;
			return true;
		}
		else {
			HANDLE hproc = OpenProcess(NULL, NULL, (DWORD)proc);
			if (!hproc)
				return true;
			if (TerminateProcess(hproc, 1) == 0) {
				CloseHandle(hproc);
				return false;
			}
			else {
				CloseHandle(hproc);
				return true;
			}
		}
	#else
		return (kill(proc, SIGKILL) == 0);
	#endif
}

uint8 WaitForProcess(uint32 proc, uint32 timeout, int &returncode) {
	#if defined	WINDOWS
		DWORD exitcode;
		std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
		if (i != ProcessInformationMap->end()) {
			ProcessData* pData = i->second;
			if (WaitForSingleObject(pData->procInfo.hProcess, timeout) == WAIT_OBJECT_0) {
				if (GetExitCodeProcess(pData->procInfo.hProcess, &exitcode) == STILL_ACTIVE) {
					return PROC_RUNNING;
				}
				else {
					returncode = exitcode;
					CloseHandle(pData->procInfo.hProcess);
					CloseHandle(pData->procInfo.hThread);
					ProcessInformationMap->erase(i);
					delete pData;
					return PROC_TERMINATED;
				}
			}
			return PROC_TIMEOUT;
		}
		else {
			HANDLE hproc = OpenProcess(NULL, NULL, (DWORD)proc);
			if (!hproc) {
				return PROC_ERROR;
			}
			if (WaitForSingleObject(hproc, timeout) == WAIT_OBJECT_0) {
				if (GetExitCodeProcess(hproc, &exitcode) == STILL_ACTIVE) {
					CloseHandle(hproc);
					return PROC_RUNNING;
				}
				else {
					returncode = exitcode;
					CloseHandle(hproc);
					return PROC_TERMINATED;
				}
			}
			else {
				CloseHandle(hproc);
				return PROC_RUNNING;
			}
		}
	#else
		uint64 start = GetTimeNow();	
		do {
			if (GetProcessStatus(proc, returncode) < PROC_RUNNING)
				return PROC_TERMINATED;
			utils::Sleep(10);
		} while (GetTimeAgeMS(start) < timeout);
		return PROC_RUNNING;

	#endif
}









//////////////////////////////////////////////////////////////
//                      DLL Libraries                       //
//////////////////////////////////////////////////////////////

bool SetCommandLine(int argc, char* argv[]) {

	if (!CommandLineInfo::CommandLineInfoSingleton)
		CommandLineInfo::CommandLineInfoSingleton = new CommandLineInfo();

	std::string key;
	const char* val;
	int32 n;
	CommandLineInfo::CommandLineInfoSingleton->CommandLineString = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec = argv[0];
	CommandLineInfo::CommandLineInfoSingleton->CommandLineItems.push_back(argv[0]);
	for (n=1; n<argc; n++) {
		CommandLineInfo::CommandLineInfoSingleton->CommandLineItems.push_back(argv[n]);
		key = argv[n];
		val = strchr(argv[n], '=');
		if (val)
			CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs[key.substr(0, val-argv[n])] = val+1;
		CommandLineInfo::CommandLineInfoSingleton->CommandLineString += ' ';
		CommandLineInfo::CommandLineInfoSingleton->CommandLineString += argv[n];
	}

	//printf("Full: '%s'\n", CommandLineString.c_str());
	//printf("Base: '%s'\n", CommandLineExec.c_str());

	//n = 0;
	//std::vector<std::string>::iterator it = CommandLineItems.begin(), itEnd = CommandLineItems.end();
	//while (it != itEnd) {
	//	printf("[%u] '%s'\n", n++, (*it).c_str());	
	//	it++;
	//}

	//n = 0;
	//std::map<std::string, std::string>::iterator mit = CommandLineArgs.begin(), mitEnd = CommandLineArgs.end();
	//while (mit != mitEnd) {
	//	printf("[%u] '%s' = '%s'\n", n++, mit->first.c_str(), mit->second.c_str());	
	//	mit++;
	//}

	//printf("Executable: '%s' '%s'\n", GetCommandLinePath().c_str(), GetCommandLineExecutableOnly().c_str());
	return true;
}

std::string GetCommandLine() {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return "";
	return CommandLineInfo::CommandLineInfoSingleton->CommandLineString;
}

std::string GetCommandLinePath() {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return "";
	std::string::size_type p1 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('/');
	std::string::size_type p2 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('\\');
	if (p1 == std::string::npos) {
		if (p2 == std::string::npos)
			return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec;
		else
			return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(0, p2+1);
	}
	else if (p2 == std::string::npos)
		return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(0, p1+1);
	else
		return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(0, (p1 > p2) ? p1+1 : p2+1);
}

std::string GetCommandLineExecutable() {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return "";
	return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec;
}

std::string GetCommandLineExecutableOnly() {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return "";
	std::string::size_type p1 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('/');
	std::string::size_type p2 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('\\');
	if (p1 == std::string::npos) {
		if (p2 == std::string::npos)
			return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec;
		else
			return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(p2+1);
	}
	else if (p2 == std::string::npos)
		return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(p1+1);
	else
		return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr((p1 > p2) ? p1+1 : p2+1);
}

uint32 GetCommandLineArgCount() {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return 0;
	return (uint32)CommandLineInfo::CommandLineInfoSingleton->CommandLineItems.size();
}

std::string GetCommandLineArg(uint16 n) {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return "";
	return CommandLineInfo::CommandLineInfoSingleton->CommandLineItems[n];
}

std::string GetCommandLineArg(const char* key) {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return "";
	return CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs[key];
}



Library::Library() {
	handle = NULL;
}
Library::~Library() {
	if (handle) {
		#ifdef WINDOWS
			FreeLibrary(handle);
		#else // WINDOWS
			// dlclose() bug: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=28625
			// __dso_handle unreferenced
			// if there are any globally desclared objects in the library!!!
			dlclose(handle);
		#endif // WINDOWS
	}
}

std::string Library::patchLibraryFilename(const char* filename, const char* path) {
	#ifdef WINDOWS
		return filename;
	#else
		std::string realLibName;
		if (utils::TextEndsWith(filename, ".so"))
			realLibName = filename;
		else
			realLibName = utils::StringFormat("%s.so", filename);

		int32 filenameStart;
		if ( (filenameStart = realLibName.find_last_of('/') == std::string::npos ))
			filenameStart = 0;
		else
			filenameStart++;

		if (!utils::TextStartsWith(filename + filenameStart, "lib", true))
			realLibName.insert(filenameStart, "lib");

		if (!filenameStart) {
			if (!path)
				realLibName.insert(0, "./");
			else
				realLibName.insert(0, path);
		}

		return realLibName;
	#endif
}

bool Library::load(const char* filename) {
	if (!filename)
		return false;
	std::string errorText;
	//printf("Trying to find library: '%s'...\n", filename);

	#ifdef WINDOWS
		LPVOID lpMsgBuf; //Message Buffer
		#ifdef _DEBUG
			handle = LoadLibrary(utils::StringFormat("%sDebug", filename).c_str());
			if (handle == NULL)
		#endif
			handle = LoadLibrary(filename);
		if (handle == NULL) {
			FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
				NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
				(LPTSTR) &lpMsgBuf, 0, NULL);
			LogPrint(0, LOG_SYSTEM, 0, "Could not find or load library '%s': %s", filename, lpMsgBuf);
			LocalFree(lpMsgBuf);
			return false;
		}
	#else // WINDOWS
		handle = 0;
		const char* dlErrorText;
		std::string realLibName = patchLibraryFilename(filename);
		//printf("Trying patched library: '%s'...\n", realLibName.c_str());
	
		// clear errors
		dlerror();
		#ifdef _DEBUG
			std::string debugName = realLibName;
			utils::StringSingleReplace(debugName, ".so", "Debug.so", true);
			//printf("Trying library: '%s'...\n", debugName.c_str());
			handle = dlopen(debugName.c_str(), RTLD_NOW | RTLD_GLOBAL);
			if (!handle) {
				dlErrorText = dlerror();
				if (dlErrorText && strlen(dlErrorText) && !strstr(dlErrorText, "No such file"))
					errorText = dlErrorText;
			}
		#endif

		if (!handle && !errorText.size()) {
			handle = dlopen(realLibName.c_str(), RTLD_NOW | RTLD_GLOBAL);
			if (!handle) {
				dlErrorText = dlerror();
				if (dlErrorText && strlen(dlErrorText) && !strstr(dlErrorText, "No such file"))
					errorText = dlErrorText;
			}
		}

		if (!handle && !errorText.size() && !strchr(filename, '/')) {
			realLibName = patchLibraryFilename(filename, utils::GetCommandLinePath().c_str());
			//printf("Trying library: '%s'...\n", realLibName.c_str());
			handle = dlopen(realLibName.c_str(), RTLD_NOW | RTLD_GLOBAL);
			if (!handle) {
				dlErrorText = dlerror();
				if (dlErrorText && strlen(dlErrorText) && !strstr(dlErrorText, "No such file"))
					errorText = dlErrorText;
			}
		}
			
		if (handle == NULL) {
			if (errorText.size())
				LogPrint(0, LOG_SYSTEM, 0, "Could not load library file '%s': %s", realLibName.c_str(), errorText.c_str());
			else
				LogPrint(0, LOG_SYSTEM, 0, "Could not find library file '%s'", filename, errorText.c_str());
			return false;
		}
	#endif // WINDOWS
	return true;
}

LibraryFunction Library::getFunction(const char* funcName) {
	if (handle == NULL)
		return NULL;
	if (!strlen(funcName))
		return NULL;

	LibraryFunction func;

	#ifdef WINDOWS
		func = (LibraryFunction)GetProcAddress(handle, funcName);
		if (func == NULL) {
			LPVOID lpMsgBuf; //Message Buffer
			//Generate Error Message from GetLastError
			FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
				NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
				(LPTSTR) &lpMsgBuf, 0, NULL);
			LogPrint(0, LOG_SYSTEM, 0, "Could not find library function: %s: %s", funcName, lpMsgBuf);
			// Free the buffer.
			LocalFree(lpMsgBuf);
			return NULL;
		}
	#else // WINDOWS
		const char* errmsg;
		dlerror();
		func = (LibraryFunction)dlsym(handle, funcName);
		errmsg = dlerror();
		if (errmsg != NULL) {
			LogPrint(0, LOG_SYSTEM, 0, "Could not find library function: %s: %s", (char*) funcName, (char*) errmsg);
			return NULL;
		}
	#endif // WINDOWS
	return func;
}

Library* OpenLibrary(const char* libName) {
	Library* lib = new Library();
	if (!lib->load(libName)) {
		delete(lib);
		return NULL;
	}
	return lib;
}


//////////////////////////////////////////////////////////////
//                        OS System                         //
//////////////////////////////////////////////////////////////

char OSLocalHostName[1024] = {0};
char OSArchitectureName[1024] = {0};
char OSName[1024] = {0};

const char* GetComputerName() {
	//char name[1024];
	char* name = new char[1024];
	if (strlen(OSLocalHostName) == 0) {
		#ifdef WINDOWS

			DWORD size = 1024;
			#ifdef POCKETPC
				delete [] name;
				return "PocketPC";
			#else
				if (::GetComputerName(name, &size) == 0) {
					delete [] name;
					return "";
				}
				else
					utils::strcpyavail(OSLocalHostName, name, 1024, true);
			#endif

			//if (gethostname(name, 1024) == 0)
			//	strcpy(OSLocalHostName, name);
			//else {
			//	int er = WSAGetLastError();
			//	if (er == WSANOTINITIALISED) {
			//		WSADATA info;
			//		if (WSAStartup(MAKEWORD(1,1), &info) == 0) {
			//			// Now we can retry the socket() function
			//			if (gethostname(hname, 1000) == 0)
			//				strcpy(OSLocalHostName, name);
			//		}
			//	}
			//}
		#else
			std::string outString, errString;
			if (!RunOSCommand("uname -n", NULL, 1000, outString, errString)) {
				//if (!RunOSTextCommand("uname -n", name, size))
				delete [] name;
				return "";
			}
			else
				utils::strcpyavail(OSLocalHostName, outString.c_str(), 1024, true);
		#endif // WINDOWS
	}
	delete [] name;
	return OSLocalHostName;
}

uint64 GetProcessMemoryUsage() {

	#if defined	WINDOWS
		HANDLE hProcess = GetCurrentProcess();
		PROCESS_MEMORY_COUNTERS pmc;
		if (hProcess == NULL)
			return 0;
		if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) != 0)
			return (uint64)pmc.WorkingSetSize;
		else
			return 0;
	#else
		/* Linux ---------------------------------------------------- */
		long rss = 0L;
		FILE* fp = NULL;
		if ( (fp = fopen( "/proc/self/statm", "r" )) == NULL )
			return (uint64)0L;      /* Can't open? */
		if ( fscanf( fp, "%*s%ld", &rss ) != 1 ) {
			fclose( fp );
			return (uint64)0L;      /* Can't read? */
		}
		fclose( fp );
		return (uint64)rss * (uint64)sysconf( _SC_PAGESIZE);
	#endif
}

uint64 GetPeakProcessMemoryUsage() {

	#if defined	WINDOWS
		HANDLE hProcess = GetCurrentProcess();
		PROCESS_MEMORY_COUNTERS pmc;
		if (hProcess == NULL)
			return 0;
		if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) != 0)
			return (uint64)pmc.PeakWorkingSetSize;
		else
			return 0;
	#else
		// Get Peak Memory Usage
		struct rusage usage;
		getrusage( RUSAGE_SELF, &usage );
		return ((uint64)usage.ru_maxrss) * 1024L;


		///* AIX and Solaris ------------------------------------------ */
		//struct psinfo psinfo;
		//int fd = -1;
		//if ( (fd = open( "/proc/self/psinfo", O_RDONLY )) == -1 )
		//	return (size_t)0L;      /* Can't open? */
		//if ( read( fd, &psinfo, sizeof(psinfo) ) != sizeof(psinfo) )
		//{
		//	close( fd );
		//	return (size_t)0L;      /* Can't read? */
		//}
		//close( fd );
		//return (size_t)(psinfo.pr_rssize * 1024L);

	#endif
}


uint16 GetCPUCount() {
	#if defined	WINDOWS
		SYSTEM_INFO siSysInfo;
		GetSystemInfo(&siSysInfo); 
		return (uint16) siSysInfo.dwNumberOfProcessors;
	#else
		// ######################################
	#endif
	return 0;
}

uint16 GetCPUArchitecture() {
	#if defined	WINDOWS
		SYSTEM_INFO siSysInfo;
		GetSystemInfo(&siSysInfo);
		switch(siSysInfo.wProcessorArchitecture) {
			case PROCESSOR_ARCHITECTURE_AMD64:
				return OSCPU_AMD64;
			case PROCESSOR_ARCHITECTURE_IA64:
				return OSCPU_IA64;
			case PROCESSOR_ARCHITECTURE_INTEL:
				return OSCPU_X86;
			default:
				return OSCPU_UNKNOWN;
		}
	#else
		// ######################################
	#endif
	return 0;
}

uint64 GetCPUSpeed() {
	#if defined	WINDOWS
		//HKEY hKey;
		//LONG lRet;

		// Test for SP6 versus SP6a.
		//JString key = "HARDWARE\\DESCRIPTION\\SYSTEM\\CentralProcessor\\0";
		//lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, &hKey );
		//if( lRet == ERROR_SUCCESS ) {
		//	DWORD len = sizeof(int);
		//	BYTE data[sizeof(int)];
		//	key = "~MHz";
		//	lRet = RegQueryValueEx(hKey, key, NULL, NULL, data, &len);
		//	if (lRet == ERROR_SUCCESS) {
		//		RegCloseKey(hKey);
		//		return (uint64) (data[3] << 24) + (data[2] << 16) + (data[1] << 8) + (data[0] << 0);
		//	}
		//	else {
		//		RegCloseKey(hKey);
		//		return 0;
		//	}
		//}
	#else
		// ######################################
	#endif
	return 0;
}

uint64 GetSystemMemorySize() {
	#if defined	WINDOWS
		MEMORYSTATUS memstat;
		GlobalMemoryStatus(&memstat);
		return (uint64)memstat.dwTotalPhys;
	#else
		struct sysinfo info;
		if (sysinfo(&info) == 0)
			return info.totalram;
		else
			return 0;
	#endif
	return 0;
}

bool GetSystemMemoryUsage(uint64 &totalRAM, uint64 &freeRAM) {
	#if defined	WINDOWS
		MEMORYSTATUSEX memstat;
		memset(&memstat, 0, sizeof(MEMORYSTATUSEX));
		memstat.dwLength = sizeof(MEMORYSTATUSEX);
		if (!GlobalMemoryStatusEx(&memstat)) {
			int a = GetLastError();
			return false;
		}
		totalRAM = (uint64)memstat.ullTotalPhys;
		freeRAM = (uint64)memstat.ullAvailPhys;
		return true;
	#else
		struct sysinfo info;
		if (sysinfo(&info) == 0) {
			totalRAM = info.totalram;
			freeRAM = info.freeram;
			return true;
		}
		else
			return false;
	#endif
}

const char* GetSystemArchitecture() {
	if (strlen(OSArchitectureName) == 0) {
		#if defined	WINDOWS
			SYSTEM_INFO siSysInfo;
			GetSystemInfo(&siSysInfo); 

			switch(siSysInfo.wProcessorArchitecture) {
				case PROCESSOR_ARCHITECTURE_UNKNOWN:
					utils::strcpyavail(OSArchitectureName, "i386", 1024, true);
				case PROCESSOR_ARCHITECTURE_INTEL:
					switch(siSysInfo.wProcessorLevel) {
						case 3:
							utils::strcpyavail(OSArchitectureName, "i386", 1024, true);
							break;
						case 4:
							utils::strcpyavail(OSArchitectureName, "i486", 1024, true);
							break;
						case 5:
							utils::strcpyavail(OSArchitectureName, "i586", 1024, true);
							break;
						case 6:
							utils::strcpyavail(OSArchitectureName, "i686", 1024, true);
							break;
						case 7:
							utils::strcpyavail(OSArchitectureName, "i786", 1024, true);
							break;
						case 8:
							utils::strcpyavail(OSArchitectureName, "i886", 1024, true);
							break;
						case 9:
							utils::strcpyavail(OSArchitectureName, "i986", 1024, true);
							break;
						default:
							utils::strcpyavail(OSArchitectureName, "i686", 1024, true);
							break;
					}
				case PROCESSOR_ARCHITECTURE_MIPS:
					utils::strcpyavail(OSArchitectureName, "mips", 1024, true);
					break;
				case PROCESSOR_ARCHITECTURE_ALPHA:
					utils::strcpyavail(OSArchitectureName, "alpha", 1024, true);
					break;
				case PROCESSOR_ARCHITECTURE_PPC:
					utils::strcpyavail(OSArchitectureName, "ppc", 1024, true);
					break;
				case PROCESSOR_ARCHITECTURE_IA64:
					utils::strcpyavail(OSArchitectureName, "ia64", 1024, true);
					break;
				case PROCESSOR_ARCHITECTURE_AMD64:
					utils::strcpyavail(OSArchitectureName, "amd64", 1024, true);
					break;
				default:
					break;
			}
		#else
		// ######################################
		#endif
	}
	return OSArchitectureName;
}

const char* GetSystemOSName() {
	if (strlen(OSName) == 0) {
		#if defined	WINDOWS
			#ifdef POCKETPC
				utils::strcpyavail(OSName, "Microsoft PocketPC", 1024, true);
			#else
				switch(GetCPUArchitecture()) {
					case OSCPU_AMD64:
						utils::strcpyavail(OSName, "Microsoft Windows X64", 1024, true);
						break;
					case OSCPU_IA64:
						utils::strcpyavail(OSName, "Microsoft Windows IA64", 1024, true);
						break;
					case OSCPU_X86:
						utils::strcpyavail(OSName, "Microsoft Windows X86", 1024, true);
						break;
					default:
						utils::strcpyavail(OSName, "Microsoft Windows", 1024, true);
						break;
				}
			#endif
		#else
		// ######################################
		#endif
	}
	return OSName;
}

bool GetSystemOSVersion(uint16& major, uint16& minor, uint16& build, char* text, uint16 textSize) {
	major = 0;
	minor = 0;
	build = 0;
	text[0] = 0;
	#if defined	WINDOWS
		#ifdef POCKETPC

			OSVERSIONINFO osvi;
			BOOL bOsVersionInfoEx;

			// Try calling GetVersionEx using the OSVERSIONINFOEX structure.
			// If that fails, try using the OSVERSIONINFO structure.

			ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
			osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

			if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
			{
				osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
				if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) 
					return false;
			}

			major = osvi.dwMajorVersion;
			minor = osvi.dwMinorVersion;
			if (strlen((char*)osvi.szCSDVersion) < textSize)
				utils::strcpyavail(text, (char*)osvi.szCSDVersion, textSize, true);
		#else

			OSVERSIONINFOEX osvi;
			BOOL bOsVersionInfoEx;

			// Try calling GetVersionEx using the OSVERSIONINFOEX structure.
			// If that fails, try using the OSVERSIONINFO structure.

			ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
			osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

			if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
			{
				osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
				if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) 
					return false;
			}

			switch (osvi.dwPlatformId)
			{
				// Test for the Windows NT product family.
				case VER_PLATFORM_WIN32_NT:

				// Display service pack (if any) and build number.

					if( osvi.dwMajorVersion == 4 && 
						lstrcmpi( osvi.szCSDVersion, "Service Pack 6" ) == 0 )
					{
						HKEY hKey;
						LONG lRet;

						// Test for SP6 versus SP6a.
						lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
							"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009",
							0, KEY_QUERY_VALUE, &hKey );
						if( lRet == ERROR_SUCCESS ) {
							if (textSize > 16)
								utils::strcpyavail(text, "Service Pack 6a", textSize, true);
							build = osvi.dwBuildNumber & 0xFFFF;
						}
						else // Windows NT 4.0 prior to SP6a
						{
							major = (uint16)osvi.dwMajorVersion;
							minor = (uint16)osvi.dwMinorVersion;
							build = (uint16)osvi.dwBuildNumber & 0xFFFF;
							if (strlen((char*)osvi.szCSDVersion) < textSize)
								utils::strcpyavail(text, (char*)osvi.szCSDVersion, textSize, true);
						}
						RegCloseKey( hKey );
					}
					else // Windows NT 3.51 and earlier or Windows 2000 and later
					{
						major = (uint16)osvi.dwMajorVersion;
						minor = (uint16)osvi.dwMinorVersion;
						build = (uint16)osvi.dwBuildNumber & 0xFFFF;

						if (strlen((char*)osvi.szCSDVersion) < textSize)
							utils::strcpyavail(text, (char*)osvi.szCSDVersion, textSize, true);
					}
					break;
			}

		#endif
	#else
		// ######################################
	#endif
	return true;
}

//bool RunOSTextCommand(const char* cmd, char** result, uint32 size) {
//
//	#ifdef WINDOWS
//		// Use RunOSCommand instead
//		return false;
//	#else
//		fflush(stdin);
//		fflush(stdout);
//		FILE* runfile = popen(cmd, "r");
//		if (runfile == NULL)
//			return false;
//
//		int res;
//		int size = 4096;
//		char buffer[size+1];
//		std::string output;
//		
//		res = fread(buffer, 1, size, runfile);
//		if (res <= 0) {
//			pclose(runfile);
//			return false;
//		}
//		do {
//			buffer[res] = 0;
//			output += buffer;
//		} while (res = fread(buffer, 1, size, runfile));
//
//		pclose(runfile);
//		delete [] buffer;
//		*result = new char[output.length()+1];
//		memcpy(*result, output.c_str(), output.length());
//		result[output.length()] = 0;
//		return true;
//	#endif
//}

//bool GetSystemName(uint32 id, const char* title, char* name, uint32 size) {
//	if ( (name == NULL) || (size < 64) )
//		return false;
//	memset(name, 0, 64);
//	uint32 pos = 0;
//	#ifdef WINDOWS
//		strcpy_s(name, size, "Global\\");
//		strcpy_s(name+(pos = (uint32)strlen(name)), size-pos, title);
//	//	_itoa_s(id, name+(pos = (uint32)strlen(name)), size-pos, 16);
//	#else
//		strncpy(name, "/", size);
//		pos = (uint32)strlen(name);
//		strncpy(name+pos, title, size-pos);
//	#endif
//	Int2Ascii((int32)id, name+(pos = (uint32)strlen(name)), size-pos, 16);
//	return true;
//}

char* Int2Ascii(int64 value, char* result, uint16 size, uint8 base) {
	if (base < 2 || base > 36) {
		result[0] = 0;
		return result;
	}

	char* ptr = result, *ptr1 = result, tmp_char;
	int64 tmp_value;

	do {
		tmp_value = value;
		value /= base;
		*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
	} while ( value );

	// Apply negative sign
	if (tmp_value < 0)
		*ptr++ = '-';
	*ptr-- = '\0';
	while(ptr1 < ptr) {
		tmp_char = *ptr;
		*ptr--= *ptr1;
		*ptr1++ = tmp_char;
	}
	return result;
}

char* Uint2Ascii(uint64 value, char* result, uint16 size, uint8 base) {
	if (base < 2 || base > 36) {
		result[0] = 0;
		return result;
	}

	char* ptr = result, *ptr1 = result, tmp_char;
	int64 tmp_value;

	do {
		tmp_value = value;
		value /= base;
		*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
	} while ( value );

	*ptr-- = '\0';
	while(ptr1 < ptr) {
		tmp_char = *ptr;
		*ptr--= *ptr1;
		*ptr1++ = tmp_char;
	}
	return result;
}

unsigned char* Ascii2UTF16LE(const char* ascii, uint32 len, uint32& size) {
	size = (len * 2) + 4;
	unsigned char* result = new unsigned char[size];
	result[0] = 0xff;
	result[1] = 0xfe;
	uint32 i = 2;
	const char* src = ascii;
	for (uint32 n = 0; n < len; n++) {
		result[i++] = *src;
		result[i++] = 0;
		src++;
	}
	result[i++] = 0;
	result[i++] = 0;
	size = (len * 2) + 2;
	return result;
}


bool GetSocketError(int error, char* errorString, uint16 errorStringMaxSize, bool* isRecoverable) {

	if (errorStringMaxSize < 128)
		return false;

	#ifdef WINDOWS

		if (error == WSANOTINITIALISED) {
			utils::strcpyavail(errorString, "Cannot initialize WinSock!", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAENETDOWN) {
			utils::strcpyavail(errorString, "The network subsystem or the associated service provider has failed", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEAFNOSUPPORT) {
			utils::strcpyavail(errorString, "The specified address family is not supported", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEINPROGRESS) {
			utils::strcpyavail(errorString, "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function", errorStringMaxSize, true);
			*isRecoverable = true;
		}
		else if (error == WSAEMFILE) {
			utils::strcpyavail(errorString, "No more socket descriptors are available", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAENOBUFS) {
			utils::strcpyavail(errorString, "No buffer space is available. The socket cannot be created", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEPROTONOSUPPORT) {
			utils::strcpyavail(errorString, "The specified protocol is not supported", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEPROTOTYPE) {
			utils::strcpyavail(errorString, "The specified protocol is the wrong type for this socket", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAESOCKTNOSUPPORT) {
			utils::strcpyavail(errorString, "The specified socket type is not supported in this address family", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEADDRINUSE) {
			utils::strcpyavail(errorString, "The socket's local address is already in use and the socket was not marked to allow address reuse with SO_REUSEADDR. This error usually occurs during execution of the bind function, but could be delayed until this function if the bind was to a partially wildcard address (involving ADDR_ANY) and if a specific address needs to be committed at the time of this function", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEINVAL) {
			utils::strcpyavail(errorString, "The socket has not been bound with bind", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEISCONN) {
			utils::strcpyavail(errorString, "The socket is already connected", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAENOTSOCK) {
			utils::strcpyavail(errorString, "The descriptor is not a socket", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEOPNOTSUPP) {
			utils::strcpyavail(errorString, "The referenced socket is not of a type that supports the listen operation", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEADDRNOTAVAIL) {
			utils::strcpyavail(errorString, "The specified address is not a valid address for this machine", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEFAULT) {
			utils::strcpyavail(errorString, "The name or namelen parameter is not a valid part of the user address space", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEMFILE) {
			utils::strcpyavail(errorString, "The queue is nonempty upon entry to accept and there are no descriptors available", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEWOULDBLOCK) {
			utils::strcpyavail(errorString, "The socket is marked as nonblocking and no connections are present to be accepted", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAETIMEDOUT) {
			utils::strcpyavail(errorString, "Attempt to connect timed out without establishing a connection", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAENETUNREACH) {
			utils::strcpyavail(errorString, "The network cannot be reached from this host at this time", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEISCONN) {
			utils::strcpyavail(errorString, "The socket is already connected (connection-oriented sockets only)", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAECONNREFUSED) {
			utils::strcpyavail(errorString, "The attempt to connect was forcefully rejected", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEAFNOSUPPORT) {
			utils::strcpyavail(errorString, "Addresses in the specified family cannot be used with this socket", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEADDRNOTAVAIL) {
			utils::strcpyavail(errorString, "The remote address is not a valid address (such as ADDR_ANY)", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEALREADY) {
			utils::strcpyavail(errorString, "A nonblocking connect call is in progress on the specified socket", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAECONNRESET) {
			utils::strcpyavail(errorString, "Connection was reset", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAECONNABORTED) {
			utils::strcpyavail(errorString, "Software caused connection abort", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else {
			snprintf(errorString, errorStringMaxSize, "TCP error with no description: %d", error);
			*isRecoverable = false;
		}

		return true;
	#else
		if (error == 100) {
			utils::strcpyavail(errorString, "Cannot initialise socket", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else {
			utils::strcpyavail(errorString, strerror(error), errorStringMaxSize, true);
			*isRecoverable = false;
		}

		return true;
	#endif
}


int GetLastOSErrorNumber() {
	#ifdef WINDOWS
		int err = WSAGetLastError();
		WSASetLastError(0);
		return err;
	#else
		return errno;
	#endif
}

std::string GetLastOSErrorMessage() {
	#ifdef WINDOWS
		LPVOID lpMsgBuf; //Message Buffer
		DWORD error = GetLastError();
		uint32 len = FormatMessage(
			FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
			NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf, 0, NULL);
		std::string str = (char*)lpMsgBuf;
		LocalFree(lpMsgBuf);
		return str;
	#else
		return utils::StringFormat("Error number: %u", errno);
	#endif
}

bool WaitForSocketWriteability(SOCKET s, int32 timeout) {

	int maxfd = 0;

	// If socket is not valid return true so the next read will report the error
	if (s == INVALID_SOCKET) return true;

	struct timeval tv;
	tv.tv_sec = 0;
	tv.tv_usec = 0;

	fd_set wds;
	// create a list of sockets to check for activity
	FD_ZERO(&wds);
	// specify mySocket
	FD_SET(s, &wds);

	#ifdef WINDOWS
	#else
		maxfd = s + 1;
	#endif

	if (timeout > 0) {
		ldiv_t d = ldiv(timeout*1000, 1000000);
		tv.tv_sec = d.quot;
		tv.tv_usec = d.rem;
	}

	// Check for readability
	return( select(maxfd, NULL, &wds, NULL, &tv) > 0);
}

bool WaitForSocketReadability(SOCKET s, int32 timeout) {

	int maxfd = 0;

	// If socket is not valid return true so the next read will report the error
	if (s == INVALID_SOCKET) return true;

	struct timeval tv;
	tv.tv_sec = 0;
	tv.tv_usec = 0;

	fd_set rdds;
	// create a list of sockets to check for activity
	FD_ZERO(&rdds);
	// specify mySocket
	FD_SET(s, &rdds);

	#ifdef WINDOWS
	#else
		maxfd = s + 1;
	#endif

	if (timeout > 0) {
		ldiv_t d = ldiv(timeout*1000, 1000000);
		tv.tv_sec = d.quot;
		tv.tv_usec = d.rem;
	}

	// Check for readability
//	uint64 t2 = GetTimeNow();
	int ret = select(maxfd, &rdds, NULL, NULL, &tv);
	//if (GetTimeAge(t2) > 1000) {
//		LogPrint(0,LOG_NETWORK,0,"************** WaitForSocketReadability(%d) took %s ******************", s, PrintTimeDifString(GetTimeAge(t2)).c_str());
	//}
	return(ret > 0);
}

bool SetSocketNonBlockingMode(SOCKET s) {
	#if defined(WINDOWS)
		unsigned long parm = 1; // 1 = Non-blocking, 0 = Blocking
		ioctlsocket(s, FIONBIO, &parm);
	#else
		long parm = fcntl(s, F_GETFL);
		parm |= O_NONBLOCK;
		fcntl(s, F_SETFL, parm);
	#endif
	return true;
}

bool SetSocketBlockingMode(SOCKET s) {
	#if defined(WINDOWS)
		unsigned long parm = 0; // 1 = Non-blocking, 0 = Blocking
		ioctlsocket(s, FIONBIO, &parm);
	#else
		long parm = fcntl(s, F_GETFL);
		parm &= ~O_NONBLOCK;
		fcntl(s, F_SETFL, parm);
	#endif
	return true;
}

bool LookupIPAddress(const char* name, uint32& address) {
	CHECKNETWORKINIT
	if ( (name == NULL) || (strlen(name) == 0) )
		return false;

	struct hostent* hent;

	#ifdef WINDOWS
		hent = gethostbyname(name);
		if (hent == NULL)
			return false;
		else {
			memcpy(&address, hent->h_addr_list[0], 4);
			return true;
		}
	#else
		struct addrinfo hints, *res;
		int error;
		const char *cause = NULL;

		memset(&hints, 0, sizeof(hints));
		// hints.ai_flags = AI_NUMERICHOST; // AI_PASSIVE
		hints.ai_family = PF_UNSPEC;
		hints.ai_socktype = SOCK_STREAM;
		error = getaddrinfo(name, NULL, &hints, &res);
		if (!error) {
			memcpy(&address, res->ai_addr->sa_data+2, 4);
			freeaddrinfo(res);
			return true;
		}
		else
			return false;
	#endif // WINDOWS
}

#ifdef WINDOWS
	bool IsNetworkInitialised = false;
	bool CheckNetworkInit() {
		if (IsNetworkInitialised) return true;
		WSADATA info;
		if (WSAStartup(MAKEWORD(1,1), &info) != 0) {
			LogPrint(0, LOG_NETWORK, 0, "Could not start Windows Networking...");
			return false;
		}
		IsNetworkInitialised = true;
		return true;
	}
#endif

bool LookupHostname(uint32 address, char* name, uint32 maxSize) {
	CHECKNETWORKINIT
	struct hostent *pHost;

	unsigned char* addrChars = (unsigned char*)&address;
	//char addrString[20];
	char* addrString = new char[20];
	snprintf(addrString, 20, "%u.%u.%u.%u", addrChars[0], addrChars[1], addrChars[2], addrChars[3]);

	#ifdef WINDOWS
		pHost = gethostbyname(addrString);
		delete [] addrString;
		if (pHost == NULL) {
			return false;
		}
		if (strlen(pHost->h_name) > maxSize-1) {
			return false;
		}
		utils::strcpyavail(name, pHost->h_name, maxSize, true);
		return true;
	#else // WINDOWS

		#ifdef Darwin
			pHost = gethostbyname(addrString);
			delete [] addrString;
			if (pHost == NULL)
				return false;
			else {
				if (strlen(pHost->h_name) > maxSize-1)
					return false;
				utils::strcpyavail(name, pHost->h_name, maxSize, true);
				return true;
			}
		#else // Darwin

			pHost = new struct hostent;
			char bf[2000];
			int er;
			gethostbyname_r(addrString, pHost, bf, 2000, &pHost, &er);
			delete [] addrString;
			if (er != 0) {
				delete pHost;
				return false;
			}
			else {
				if (strlen(pHost->h_name) > maxSize-1)
					return false;
				utils::strcpyavail(name, pHost->h_name, maxSize, true);
				delete pHost;
				return true;
			}

		#endif // Darwin
	#endif // WINDOWS
}

std::string GetLocalHostnameString() {
	char* name = new char[256];
	if (!GetLocalHostname(name, 255)) {
		delete [] name;
		return "";
	}
	
	std::string sname = name;
	delete [] name;
	return sname;
}

bool GetLocalHostname(char* name, uint32 maxSize) {
	CHECKNETWORKINIT

	if (gethostname(name, maxSize) == 0)
		return true;
	else
		return false;

	//JString ipaddress = GetLocalIPAddress();
	//JString hostname = ipaddress;
	//struct hostent* hent;
	//if ( (ipaddress.equals("127.0.0.1")) || (ipaddress.equals("localhost")) ) {
	//	localhostName = "127.0.0.1";
	//	return localhostName;
	//}

	//#ifdef WINDOWS

	//	hent = gethostbyname((char*) ipaddress);
	//	if (hent != NULL)
	//		localhostName = JString(hent->h_name);
	//	else {
	//		int err = WSAGetLastError();
	//		if (DEBUGLEVEL(KITCHENSINK)) {
	//			printf("getLocalHostname(): gethostbyaddr error %d...\n", err);
	//		}
	//		localhostName = ipaddress;
	//		return ipaddress;
	//	}

	//#else // WINDOWS

	//	struct addrinfo hints, *res;
	//	int error;
	//	const char *cause = NULL;

	//	memset(&hints, 0, sizeof(hints));
	//	hints.ai_flags = AI_NUMERICHOST;
	//	hints.ai_family = PF_UNSPEC;
	//	hints.ai_socktype = SOCK_STREAM;
	//	error = getaddrinfo(ipaddress, NULL, &hints, &res);
	//	if (error != 0) {
	//		localhostName = ipaddress;
	//		return ipaddress;
	//	}
	//	else {
	//		if (res->ai_canonname != NULL) {
	//			localhostName = res->ai_canonname;
	//		}
	//		else
	//			localhostName = ipaddress;
	//	}
	//	freeaddrinfo(res);

	//#endif // WINDOWS

	//return localhostName;

}

bool GetLocalMACAddress(uint64& address) {
	address = 0;
	uint32 c;
	NetworkInterfaces* interfaces = GetLocalInterfaces(c);
	for (uint32 i=0; i<c; i++) {
		if ( (interfaces[i].address != 0) && (interfaces[i].address != LOCALHOSTIP) )
			address = interfaces[i].mac;
	}
	delete [] interfaces;
	return (address != 0);
}

uint64* GetLocalMACAddresses(uint32& count) {
	uint32 c;
	NetworkInterfaces* interfaces = GetLocalInterfaces(c);
	if (!c)
		return NULL;
	uint64* addresses = new uint64[c];
	uint32 p=0;
	for (uint32 i=0; i<c; i++) {
		if ( (interfaces[i].address != 0) && (interfaces[i].address != LOCALHOSTIP) )
			addresses[p++] = interfaces[i].mac;
	}
	count = p;
	return addresses;
}

bool IsLocalIPAddress(const char* addr) {
	uint32 address;
	if (!LookupIPAddress(addr, address))
		return false;
	return IsLocalIPAddress(address);
}

bool IsLocalIPAddress(uint32& address) {
	CHECKNETWORKINIT
	uint32 count;
	uint32* addresses = GetLocalIPAddresses(count);
	if ((addresses == NULL) || (count == 0)) {
		delete [] addresses;
		return false;
	}

	unsigned int n;
	for (n=0; n<count; n++) {
		if (addresses[n] == address) {
			delete [] addresses;
			return true;
		}
	}
	delete [] addresses;
	return false;
}

bool GetLocalIPAddress(uint32& address) {
	CHECKNETWORKINIT
	uint32 count;
	uint32* addresses = GetLocalIPAddresses(count);
	if ((addresses == NULL) || (count == 0)) {
		delete [] addresses;
		return false;
	}
	// First find first non-localhost address
	unsigned int n;
	bool localhostPresent = false;
	for (n=0; n<count; n++) {
		if (addresses[n] != LOCALHOSTIP) {
			if (addresses[n] != 0) {
				address = addresses[n];
				delete [] addresses;
				return true;
			}
		}
		else
			localhostPresent = true;
	}
	delete [] addresses;
	// OK, then accept localhost, if present
	if (localhostPresent) {
		address = LOCALHOSTIP;
		return true;
	}
	else
		return false;
}

uint32* GetLocalIPAddresses(uint32& count) {
	CHECKNETWORKINIT

	uint32 maxSize = 128;
	uint32* addresses = new uint32[maxSize];

	addresses[0] = LOCALHOSTIP;
	count = 1;

	char* szHostName = new char[255];

	bool canAskHost = true;
	if( gethostname(szHostName, 255) != 0 ) {
		#ifdef WINDOWS
			int err = GetLastOSErrorNumber();
			if (err == WSANOTINITIALISED) {
				WSADATA info;
				if (WSAStartup(MAKEWORD(1,1), &info) != 0) {
					delete [] szHostName;
					return addresses;
				}
				else {
					if( gethostname(szHostName, 255) != 0 )
						canAskHost = false;
				}
			}
			else
				canAskHost = false;
		#else // WINDOWS
			canAskHost = false;
		#endif // WINDOWS
	}

	struct hostent * pHost;
	uint32 addr, j, i;
	bool exists;

	if (canAskHost) {
		// Get host addresses

		#ifdef WINDOWS
			pHost = gethostbyname(szHostName);
		#else // WINDOWS

			#ifdef Darwin
				pHost = gethostbyname(szHostName);
				//if (pHost == NULL)
				//	return addresses;
			#else // Darwin

				pHost = new struct hostent;
				char* bf = new char[2000];
				int er;
				int res = gethostbyname_r(szHostName, pHost, bf, 2000, &pHost, &er);
				// LogPrint(0, LOG_NETWORK, 0, "gethostbyname_r '%s' [%d] [%d]...\n", szHostName, res, er);
				if ((er != 0) && (res == 0)) {
					delete pHost;
					pHost = NULL;
					// return addresses;
				}
				delete [] bf;
			#endif // Darwin
		#endif // WINDOWS

		for( i = 0; pHost!= NULL && pHost->h_addr_list[i]!= NULL; i++ )
		{
			exists = false;
			memcpy(&addr, ((unsigned char*)pHost->h_addr_list[i]), sizeof(uint32));
			if ( (addr != 0) && (addr != LOCALHOSTIP) ) {
				for (j=0; j<count; j++) {
					if (addresses[j] == addr) {
						exists = true;
						break;
					}
				}
				if (!exists)
					addresses[count++] = addr;
			}
		}

		#ifdef WINDOWS
		#else // WINDOWS
			#ifdef Darwin
				endhostent();
			#else
				delete pHost;
			#endif // Darwin
		#endif // WINDOWS
	}

	uint32 c;
	NetworkInterfaces* interfaces = GetLocalInterfaces(c);
	for (i=0; i<c; i++) {
		exists = false;
		if ( (interfaces[i].address != 0) && (interfaces[i].address != LOCALHOSTIP) ) {
			for (j=0; j<count; j++) {
				if (addresses[j] == interfaces[i].address) {
					exists = true;
					break;
				}
			}
			if (!exists)
				addresses[count++] = interfaces[i].address;
		}
	}
	delete [] interfaces;
	delete [] szHostName;
	return addresses;
}

NetworkInterfaces* GetLocalInterfaces(uint32& count) {
	CHECKNETWORKINIT

	uint32 maxSize = 128;
	uint32 addr;
	NetworkInterfaces* interfaces = new NetworkInterfaces[maxSize];
	count = 0;

	#ifdef WINDOWS

		DWORD dwSize = 0;
		DWORD dwRetVal = 0;

		unsigned int i = 0;

		// Set the flags to pass to GetAdaptersAddresses
		ULONG flags = GAA_FLAG_INCLUDE_PREFIX;

		// default to unspecified address family (both)
		ULONG family = AF_INET;

		LPVOID lpMsgBuf = NULL;

		PIP_ADAPTER_ADDRESSES pAddresses = NULL;
		ULONG outBufLen = 0;
		ULONG Iterations = 0;

		PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
		PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
		PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;
		PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;
		IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL;
		IP_ADAPTER_PREFIX *pPrefix = NULL;

		// Allocate a 15 KB buffer to start with.
		outBufLen = 15000;

		do {

			pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen);
			if (pAddresses == NULL)
				return interfaces;

			dwRetVal =
				GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);

			if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
				FREE(pAddresses);
				pAddresses = NULL;
			}
			else
				break;

			Iterations++;

		} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < 10));


		if (dwRetVal == NO_ERROR) {
			// If successful, output some information from the data we received
			pCurrAddresses = pAddresses;
			while (pCurrAddresses) {
				if ( (pCurrAddresses->OperStatus == IfOperStatusUp) && (pCurrAddresses->IfIndex != 0)) {

					// First check the IP address
					pUnicast = pCurrAddresses->FirstUnicastAddress;
					if (pUnicast != NULL) {
						for (i = 0; pUnicast != NULL; i++) {
							addr = *(uint32*)(((unsigned char*) ((SOCKADDR*)(pUnicast->Address.lpSockaddr))->sa_data) + 2);
							if ( (addr != 0) && (addr != LOCALHOSTIP) ) {
								interfaces[count].address = addr;
								interfaces[count].mac = 0;
								if (pCurrAddresses->PhysicalAddressLength == 6)
									memcpy(&(interfaces[count].mac), &(pCurrAddresses->PhysicalAddress), 6);
								WideCharToMultiByte( CP_ACP, 0, pCurrAddresses->Description, -1, interfaces[count].name, MAXKEYNAMELEN, NULL, NULL );
								WideCharToMultiByte( CP_ACP, 0, pCurrAddresses->FriendlyName, -1, interfaces[count].friendlyName, MAXKEYNAMELEN, NULL, NULL );
								count++;
							}
							pUnicast = pUnicast->Next;
						}
					}
				}

				pCurrAddresses = pCurrAddresses->Next;
			}
		}
		if (pAddresses)
			FREE(pAddresses);

		return interfaces;

	#else
		int sock = 0;
		struct ifreq ifreq;
		struct sockaddr_in *saptr = NULL;
		struct if_nameindex *iflist = NULL, *listsave = NULL;

		//need a socket for ioctl()
		if( (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			return interfaces;
		}

		//returns pointer to dynamically allocated list of structs
		iflist = listsave = if_nameindex();

		if (iflist == NULL) {
			close(sock);
			return interfaces;
		}

		//walk thru the array returned and query for each
		//interface's address
		for(iflist; iflist->if_index != 0; iflist++) {
			//copy in the interface name to look up address of
			strncpy(ifreq.ifr_name, iflist->if_name, IF_NAMESIZE);
			//get the address for this interface
			if(ioctl(sock, SIOCGIFADDR, &ifreq) != 0) {
				// ignore;
				continue;
			}
			//print out the address
			saptr = (struct sockaddr_in *)&ifreq.ifr_addr;
			//unsigned char* ip = (unsigned char*)&(saptr->sin_addr.s_addr);
			//printf("**** %u.%u.%u.%u ***\n", ip[0], ip[1], ip[2], ip[3]);
			memcpy(&(interfaces[count].address), &(saptr->sin_addr.s_addr), sizeof(uint32));
			utils::strcpyavail(interfaces[count++].name, ifreq.ifr_name, MAXKEYNAMELEN, true);
			// get the MAC address
			interfaces[count].mac = 0;
			ioctl(sock, SIOCGIFHWADDR, &ifreq);
			memcpy(&(interfaces[count].mac), &(ifreq.ifr_hwaddr.sa_data), 6);
		}
		//free the dynamic memory kernel allocated for us
		if_freenameindex(listsave);
		close(sock); 

	#endif

	return interfaces;
}

bool GetNextAvailableLocalPort(uint16 lastPort, uint16 &nextPort) {
	CHECKNETWORKINIT

	SOCKET socket;
	struct	sockaddr_in	addr;
	addr.sin_family= AF_INET;
	addr.sin_addr.s_addr=INADDR_ANY;
	bool result = false;

	nextPort = lastPort + 1;
	while (nextPort < lastPort + 1000) {
		if((socket=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET)
			return false;
  
		#ifdef WINDOWS
			// Set the exclusive address option, preventing other software binding to
			// non-INADDR_ANY (i.e. interface addresses such as localhost directly)
			int one = 1;
			setsockopt(socket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &one, sizeof(one));
		#else
			/*
				This socket option tells the kernel that even if this port is busy (in
				the TIME_WAIT state), go ahead and reuse it anyway.  If it is busy,
				but with another state, you will still get an address already in use
				error.  It is useful if your server has been shut down, and then
				restarted right away while sockets are still active on its port.  You
				should be aware that if any unexpected data comes in, it may confuse
				your server, but while this is possible, it is not likely.
			*/
			int one = 1;
			setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&one,sizeof(one));
		#endif

		addr.sin_port=htons(nextPort);

		result = (bind(socket,(SOCKADDR*)&addr,sizeof(struct sockaddr_in))!=SOCKET_ERROR);
		shutdown(socket, SD_BOTH);
		closesocket(socket);

		if (result)
			return true;

		nextPort++;
	}
	return false;

}

bool UtilsTest() {
	printf("Testing Utils...\n\n");

	char hostname[1024];
	if (!GetLocalHostname(hostname, 1023)) {
		printf("Error GetLocalHostname\n");
		return false;
	}
	printf("Local Hostname: '%s'\n\n", hostname);

	uint32 addr;
	unsigned char* addrChars = (unsigned char*) &addr;
	if (!GetLocalIPAddress(addr)) {
		printf("Error GetLocalIPAddress\n");
		return false;
	}
	printf("Local IP Address: %u.%u.%u.%u\n\n", addrChars[0], addrChars[1], addrChars[2], addrChars[3]);

	uint32 count;
	uint32* addresses = GetLocalIPAddresses(count);
	if (addresses == NULL) {
		printf("Error GetLocalIPAddresses\n");
		return false;
	}
	uint32 n;
	for (n=0; n<count; n++) {
		addrChars = (unsigned char*) &addresses[n];
		printf("Local IP Address[%u]: %u.%u.%u.%u (%u,%u)\n\n", n, addrChars[0], addrChars[1], addrChars[2], addrChars[3], addresses[n], LOCALHOSTIP);
	}
	delete [] addresses;

	NetworkInterfaces* interfaces = GetLocalInterfaces(count);
	for (n=0; n<count; n++) {
		addrChars = (unsigned char*) &(interfaces[n]);
		printf("Local Interface[%u] %s: %u.%u.%u.%u (%llX)\n\n", n, interfaces[n].name,
			addrChars[0], addrChars[1], addrChars[2], addrChars[3], interfaces[n].mac);
	}
	delete [] interfaces;

	return true;
}

void PrintBinary(void* p, uint32 size, bool asInt, const char* title) {
	if (title != NULL)
		printf("--- %s %u ---\n", title, size);
	unsigned char c;
	for (uint32 n=0; n<size; n++) {
		c = *(((unsigned char*)p)+n);
		if (asInt)
			printf("[%u] ", (unsigned int)c);
		else
			printf("[%c] ", c);
		if ( (n > 0) && ((n+1)%10 == 0) )
			printf("\n");
	}
	printf("\n");
}

const char* stristr(const char *str, const char *substr, uint32 len) {
	if (!str || !substr) return NULL;
	const char *a, *b;
	uint32 n=0, m, sslen = (uint32)strlen(substr);
	for(;*str;*str++,n++) {
		a = str;
		b = substr;
		m = n+sslen;
		while((*a++ | 32) == (*b++ | 32)) {
			if(!*b)
				return str;
			if (len && (++m > len))
				return NULL;
		}
	}
	return NULL;
}

uint32 strcpyavail(char* dst, const char* src, uint32 maxlen, bool copyAvailable) {
	if ( !dst || ! src || !maxlen )
		return 0;
	uint32 len = (uint32)strlen(src);
	if (len > maxlen-1) {
		if (!copyAvailable)
			return 0;
		else {
			memcpy(dst, src, maxlen-1);
			dst[maxlen] = 0;
			return maxlen-1;
		}
	}
	else
		memcpy(dst, src, len+1);
	return len;
}

bool GetNextLineEnd(const char *str, uint32 size, uint32& len, uint32& crSize) {
	if (!str || !size)
		return false;

	for (len = 0; len < size; len++) {
		// Checking for CR and CRLF
		if (str[len] == 13) {
			crSize = ( (len < size-1) && (str[len+1] == 10 ) ) ? 2 : 1;
			return true;
		}
		// Checking for LF only without CR
		if (str[len] == 10) {
			crSize = 1;
			return true;
		}
	}
	return false;
}

std::string TextCapitalise(const char* text) {
	if (!text)
		return "";
	std::string str = text;
	if (!str.length())
		return "";
	std::string::iterator i = str.begin(), e = str.end();
	*i = ::toupper(*i);
	i++;
	if (i != e)
		std::transform(i, e, i, ::tolower);
	return str;
}

std::string TextUppercase(const char* text) {
	std::string str = text;
	std::transform(str.begin(), str.end(), str.begin(), ::toupper);
	return str;
}

std::string TextLowercase(const char* text) {
	std::string str = text;
	std::transform(str.begin(), str.end(), str.begin(), ::tolower);
	return str;
}

std::string TextIndent(const char* text, const char* indent) {
	std::vector<std::string> lines = utils::TextListSplitLines(text, true, false);
	std::vector<std::string>::iterator i = lines.begin(), e = lines.end();
	std::string output;
	while (i != e) {
		output += utils::StringFormat("%s%s\n", indent, (*i).c_str());
		i++;
	}
	return output;
}

std::string TextUnindent(const char* text) {
	uint32 len = (uint32)strlen(text);
	if (!len) return "";
	if (text[0] > 32)
		return text;

	// Find initial spacing char sequence
	uint32 s = 0, p = 0;
	// Find initial line endings
	while ( ((text[s] == '\n') || (text[s] == '\r')) && (s < len))
		s++;
	p = s;
	while ((text[p] <= 32) && (p < len))
		p++;
	std::string indentString = std::string(text+s, p-s);
	std::string str = text;
	StringSingleReplace(str, indentString, "", false);
	return str;
}


std::string TextTrimQuotes(const char* text) {
	if (text) {
		uint32 begin = 0, end = (uint32)strlen(text);
		if (begin == end)
			return "";
		while ((begin < end) && (text[begin] <= 32) || (text[begin] == '\"') || (text[begin] == '\''))
			begin++;
		while ((end > begin) && (text[end-1] <= 32) || (text[end-1] == '\"') || (text[end-1] == '\''))
			end--;
		return std::string(text+begin, end-begin);
	}
	else
		return "";
}


std::string TextTrim(const char* text) {
	if (text) {
		uint32 begin = 0, end = (uint32)strlen(text);
		if (begin == end)
			return "";
		while ((text[begin] <= 32) && (begin < end))
			begin++;
		while ((end > begin) && (text[end-1] <= 32))
			end--;
		return std::string(text+begin, end-begin);
	}
	else
		return "";
}

std::multimap<std::string, std::string> TextMultiMapSplit(const char* text, const char* outersplit, const char* innersplit) {
	std::multimap<std::string, std::string> result;
	typedef std::pair <std::string, std::string> Map_String_Pair;

	if (!text || !outersplit || !innersplit)
		return result;

	const char* pstr = text, *lstr = text;
	const char* src = text;

	while (pstr = strstr(src, outersplit)) {
		if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
			// result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
			result.insert(Map_String_Pair(TextTrim(std::string(src, lstr-src).c_str()), TextTrim(std::string(lstr+1, pstr-lstr-1).c_str())));
		}
		src = pstr + 1;
	}
	pstr = src + strlen(src);
	if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
		// result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
		result.insert(Map_String_Pair(TextTrim(std::string(src, lstr-src).c_str()), TextTrim(std::string(lstr+1, pstr-lstr-1).c_str())));
	}

	return result;
}


std::map<std::string, std::string> TextMapSplit(const char* text, const char* outersplit, const char* innersplit) {
	std::map<std::string, std::string> result;
	typedef std::pair <std::string, std::string> Map_String_Pair;

	if (!text || !outersplit || !innersplit)
		return result;

	const char* pstr = text, *lstr = text;
	const char* src = text;

	while (pstr = strstr(src, outersplit)) {
		if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
			// result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
			result.insert(Map_String_Pair(TextTrim(std::string(src, lstr - src).c_str()), TextTrim(std::string(lstr + 1, pstr - lstr - 1).c_str())));
		}
		src = pstr + 1;
	}
	pstr = src + strlen(src);
	if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
		// result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
		result.insert(Map_String_Pair(TextTrim(std::string(src, lstr - src).c_str()), TextTrim(std::string(lstr + 1, pstr - lstr - 1).c_str())));
	}

	return result;
}

char** SplitCommandline(const char* cmdline, int& argc) {
	std::vector<std::string> args = TextCommandlineSplit(cmdline);
	uint32 size = (uint32)args.size();
	if (!size)
		return NULL;

	char** argv = new char*[size+1];
	uint32 c = 0;
	std::vector<std::string>::iterator i = args.begin(), e = args.end();
	while (i != e) {
		argv[c] = new char[(*i).length()+1];
		strcpy(argv[c], (*i).c_str());
		c++;
		i++;
	}
	argv[c] = NULL;
	argc = (int)c;
	return argv;
}

wchar_t** SplitCommandlineW(const char* cmdline, int& argc) {
	std::vector<std::string> args = TextCommandlineSplit(cmdline);
	uint32 size = (uint32)args.size();
	if (!size)
		return NULL;

	wchar_t** argv = new wchar_t*[size+1];
	uint32 c = 0;
	std::vector<std::string>::iterator i = args.begin(), e = args.end();
	while (i != e) {
		argv[c] = new wchar_t[(*i).length()+1];
		mbstowcs(argv[c], (*i).c_str(), (*i).length() + 1);
		c++;
		i++;
	}
	argv[c] = NULL;
	argc = (int)c;
	return argv;
}

bool DeleteCommandline(char** argv, int argc) {
	for (uint32 n=0; n<(uint32)argc; n++)
		delete [] argv[n];
	delete [] argv;
	return true;
}

std::vector<std::string> TextCommandlineSplit(const char* cmdline) {

	bool insideQuotes = false;
	bool lastWasWhiteSpace = false;
	std::vector<std::string> vect;
	std::string arg, val, str;
	unsigned char ch;

	uint32 len = (uint32)strlen(cmdline);

	bool keepQuotes = false;

	for (uint32 n=0; n<len; n++) {
		ch = cmdline[n];
		if (ch <= 32) {
			if (!insideQuotes) {
				if (!lastWasWhiteSpace) {
					// An arg is ended, parse it...
					vect.push_back(str);
					str = "";
				}
				else {
					// Ignore this whitespace then...
				}
				lastWasWhiteSpace = true;
			}
			else {
				str += ch;
			}
		}
		else if (ch == '"') {
			insideQuotes = !insideQuotes;
			if (keepQuotes)
				str += ch;
			lastWasWhiteSpace = false;
		}
		else {
			str += ch;
			lastWasWhiteSpace = false;
		}
	}

	if (str.length() > 0) {
		vect.push_back(str);
		str = "";
	}

	return vect;
}

std::vector<std::string> TextListSplitLines(const char* text, bool keepEmpty, bool autoTrim) {
	if (!text || !strlen(text))
		return std::vector<std::string>();

	if (strstr(text, "\r\n"))
		return TextListSplit(text, "\r\n", keepEmpty, autoTrim);
	else if (strstr(text, "\n\r"))
		return TextListSplit(text, "\n\r", keepEmpty, autoTrim);
	else if (strstr(text, "\n"))
		return TextListSplit(text, "\n", keepEmpty, autoTrim);
	else if (strstr(text, "\r"))
		return TextListSplit(text, "\n", keepEmpty, autoTrim);

	std::vector<std::string> result;
	result.push_back(text);
	return result;
}

std::vector<std::string> TextListBreakLines(const char* text, uint32 maxLineLength) {
	if (!text || !strlen(text))
		return std::vector<std::string>();

	std::string str;
	std::vector<std::string> result;
	uint32 s = 0, n = 0, len = strlen(text);
	int p = -1;
	while (n < len) {
		if (text[n] == 13) {
			if (n == s)
				result.push_back("");
			else {
				str = std::string(text + s, n - s);
				result.push_back(TextTrim(str.c_str()));
			}
			if ((n < len - 1) && (text[n + 1] == 10))
				s = n + 2;
			else
				s = n + 1;
			p = -1;
		}
		else if (text[n] == 10) {
			if (n == s)
				result.push_back("");
			else {
				str = std::string(text + s, n - s);
				result.push_back(TextTrim(str.c_str()));
			}
			if ((n < len - 1) && (text[n + 1] == 13))
				s = n + 2;
			else
				s = n + 1;
			p = -1;
		}
		else if ((n-s < maxLineLength) && (text[n] == 32)) {
			p = n - s;
		}
		else if (n - s >= maxLineLength) {
			if (p == 0) {
				result.push_back("");
				s += p + 1;
				p = -1;
				n = s - 1;
			}
			else if (p > 0) {
				str = std::string(text + s, p);
				result.push_back(TextTrim(str.c_str()));
				s += p + 1;
				p = -1;
				n = s - 1;
			}
			else {
				str = std::string(text + s, maxLineLength);
				result.push_back(TextTrim(str.c_str()));
				s += maxLineLength;
				p = -1;
				n = s;
			}
		}
		n++;
	}
	if (s < n) {
		str = std::string(text + s, n - s);
		result.push_back(TextTrim(str.c_str()));
	}

	return result;
}

std::string TextListBreakLines(const char* text, uint32 maxLineLength, const char* breakstr) {
	std::vector<std::string> lines = TextListBreakLines(text, maxLineLength);
	if (!breakstr)
		return TextJoin(lines, "\n");
	else
		return TextJoin(lines, breakstr);
}

std::string TextJoin(std::vector<std::string> &list, const char* split, uint32 start, uint32 count) {
	std::ostringstream o;
	std::vector<std::string>::iterator i = list.begin(), e = list.end();
	uint32 l = 0, c = 0;
	while (i != e) {
		if (!count || ((l >= start) && (c < count))) {
			if (c++) o << split;
			o << *i;
		}
		l++;
		i++;
	}
	return o.str();
}

std::string TextJoin(std::list<std::string> &list, const char* split, uint32 start, uint32 count) {
	std::ostringstream o;
	std::list<std::string>::iterator i = list.begin(), e = list.end();
	uint32 l = 0, c = 0;
	while (i != e) {
		if (!count || ((l >= start) && (c < count))) {
			if (c++) o << split;
			o << *i;
		}
		l++;
		i++;
	}
	return o.str();
}

std::string TextJoin(std::map<std::string, std::string> &map, const char* innersplit, const char* outersplit, bool keyquotes, bool valquotes) {
	std::ostringstream o;
	std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
	uint32 c = 0;
	while (i != e) {
		if (c++) o << outersplit;
		if (keyquotes && valquotes)
			o << "\"" << i->first << "\"" << innersplit << "\"" << i->second << "\"";
		else if (valquotes)
			o << i->first << innersplit << "\"" << i->second << "\"";
		else
			o << i->first << innersplit << i->second;
		i++;
	}
	return o.str();
}

std::string TextJoinJSON(std::map<std::string, std::string> &map) {
	std::ostringstream o;
	std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
	uint32 c = 0;
	o << "{ ";
	while (i != e) {
		if (c++)
			o << ", \"" << utils::EncodeJSON(i->first) << "\": \"" << utils::EncodeJSON(i->second) << "\"";
		else
			o << " \"" << utils::EncodeJSON(i->first) << "\": \"" << utils::EncodeJSON(i->second) << "\"";
		i++;
	}
	o << " }";
	return o.str();
}

std::string TextJoinXML(std::map<std::string, std::string> &map, const char* innernodeName, const char* outernodeName) {
	if (!innernodeName || !strlen(innernodeName))
		return "";
	std::ostringstream o;
	std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
	if (outernodeName && strlen(outernodeName))
		o << "<" << outernodeName << ">\n";
	while (i != e) {
		o << "<" << innernodeName << " " << html::EncodeHTML(i->first) << "=\"" << html::EncodeHTML(i->second) << "\" />\n";
		i++;
	}
	if (outernodeName && strlen(outernodeName))
		o << "</" << outernodeName << ">\n";
	return o.str();
}



std::vector<std::string> TextListSplit(const char* text, const char* split, bool keepEmpty, bool autoTrim) {
	std::vector<std::string> result;
	if (!text || !split)
		return result;

	uint32 splitLen = (uint32)strlen(split);
	const char* pstr = text;
	const char* src = text;

	while (pstr = strstr(src, split)) {
		if (pstr - src || keepEmpty) {
			if (autoTrim)
				result.push_back(TextTrim(std::string(src, pstr - src).c_str()));
			else
				result.push_back(std::string(src, pstr - src).c_str());
		}
		src = pstr + splitLen;
	}
	pstr = src + strlen(src);
	if (pstr - src) {
		if (autoTrim)
			result.push_back(TextTrim(std::string(src, pstr - src).c_str()));
		else
			result.push_back(std::string(src, pstr - src).c_str());
	}
	return result;
}

std::string TextVectorConcat(std::vector<std::string> vect, const char* sep, bool allowEmpty) {
	std::string result;
	std::vector<std::string>::iterator i, e;
	for (i=vect.begin(), e=vect.end(); i!=e; i++) {
		if (allowEmpty || (*i).size()) {
			if (result.size())
				result += sep;
			result += *i;
		}
	}
	return result;
}

char* StringFormat(uint32& size, const char *format, ...) {
	if (strlen(format) == 0) {
		size = 0;
		return 0;
	}

	va_list args;
	va_start(args, format);
	char* res = StringFormatVA(size, format, args);
	va_end(args);
	return res;
}


char* StringFormatVA(uint32& size, const char *format, va_list orig_args) {
	char* str = NULL;

	#ifdef WINDOWS
		int len = _vscprintf(format, orig_args) + 10;
		str = (char*)malloc(len+1);
		#ifdef CYGWIN
			len = vsnprintf(str, len, format, orig_args);
		#else
			len = _vsnprintf_s(str, len, len+1, format, orig_args);
		#endif

		if (len)
			size = (uint32)strlen(str);
		else {
			size = 0;
			free((char*)str);
			str = NULL;
		}
	#else // WINDOWS
		int len = vasprintf(&str, format, orig_args);
		if(len >= 0)
			size = strlen(str);
		else
			size = 0;
	#endif //HAVE_VASPRINTF

	return str;
}

bool StringFormatInto(char* dst, uint32 maxsize, const char *format, ...) {
	if (!dst || !maxsize)
		return false;
	if (strlen(format) == 0) {
		dst[0] = 0;
		return true;
	}

	uint32 len = 0;
	va_list args;
	va_start(args, format);
	char* str = StringFormatVA(len, format, args);
	va_end(args);
	if (str && len) {
		if (len > maxsize) {
			free(str);
			return false;
		}
		else {
			utils::strcpyavail(dst, str, maxsize, true);
			free(str);
			return true;
		}
	}
	else {
		free(str);
		return false;
	}
}

std::string StringFormat(const char *format, ...) {
	if (strlen(format) == 0)
		return "";

	std::string res;
	uint32 len = 0;
	va_list args;
	va_start(args, format);
	char* str = StringFormatVA(len, format, args);
	va_end(args);
	if (str && len)
		res = str;
	else
		res = "";
	free(str);
	return res;
}

uint32 StringSingleReplace(std::string& text, std::string key, std::string value, bool onlyFirst) {
	if (!text.size() || !key.size())
		return 0;

	uint32 c = 0;
	size_t p = 0;
	uint32 keylen = (uint32)key.size();

	while ( (p = text.find(key, p)) != std::string::npos) {
		text.replace(p, keylen, value);
		p++;
		c++;
		if (onlyFirst)
			break;
	}
	return c;
}

uint32 StringMultiReplace(std::string& text, std::map<std::string, std::string>& map, bool onlyFirst) {
	if (!text.size() || !map.size())
		return 0;

	uint32 c = 0;

	std::map<std::string, std::string>::iterator it, itEnd;
	for (it = map.begin(), itEnd = map.end(); it != itEnd; ++it)
		c += StringSingleReplace(text, it->first, it->second, onlyFirst);
	
	return c;
}


// Example use
	//uint32 len;
	//char* file = utils::ReadAFile("test.html", len);
	//std::string sfile = file;

	//std::list<std::map<std::string, std::string> > list;

	//char tmp[64];
	//std::map<std::string, std::string> map;
	//for (uint32 n=0; n<10; n++) {
	//	map["%1%"] = utils::Int2Ascii(n, tmp, 64, 10);
	//	map["%2%"] = "Hello2";
	//	map["%3%"] = "Hello3";
	//	map["%4%"] = "Hello4";
	//	map["%5%"] = "Hello5";
	//	list.push_back(map);
	//}

	//utils::ScriptReplace(sfile, "test", list);

uint32 StringScriptReplace(std::string& text, std::string name, std::list<std::map<std::string, std::string> >& list) {
	if (!text.size() || !name.size())
		return 0;

	uint32 c = 0;

	char* start = new char[name.size() + 50];
	snprintf(start, name.size()+50, "<!-- start:%s -->", name.c_str());
	char* end = new char[name.size() + 50];
	snprintf(end, name.size()+50, "<!-- end:%s -->", name.c_str());

	// Find the section boundaries
	size_t a1 = text.find(start);
	if (a1 == std::string::npos) {
		delete [] start;
		delete [] end;
		return 0;
	}
	size_t b1 = text.find(end);
	if (b1 == std::string::npos) {
		delete [] start;
		delete [] end;
		return 0;
	}

	uint32 a2 = (uint32)(a1 + strlen(start));
	uint32 b2 = (uint32)(b1 + strlen(end));

	// Copy it
	std::string section = text.substr(a2, b1-a2);
	std::string tmp;
	std::string result;

	std::list<std::map<std::string, std::string> >::iterator it, itEnd;
	for (it = list.begin(), itEnd = list.end(); it != itEnd; ++it) {
		tmp = section;
		c += StringMultiReplace(tmp, *it, false);
		result += tmp;
	}
	
	text.replace(a1, b2-a1, result);
	delete [] start;
	delete [] end;
	return c;
}



unsigned char Hex2Char(const char* str) {
	unsigned char c = (unsigned char) strtol(str, NULL, 16);
	if (c == 0)
		return ' ';
	else
		return c;
}

unsigned char Dec2Char(const char* str) {
	unsigned char c = (unsigned char) strtol(str, NULL, 10);
	if (c == 0)
		return ' ';
	else
		return c;
}

#define poly 0xEDB88320
/* Some compilers need
   #define poly 0xEDB88320uL
 */

/* On entry, addr=>start of data
			 num = length of data
			 crc = incoming CRC     */
int CRC32(const char *addr, uint32 length, int32 crc) {
	uint32 i;
	for (; length>0; length--) {			/* Step through bytes in memory */
		crc = crc ^ *addr++;				/* Fetch byte from memory, XOR into CRC */
		for (i=0; i<8; i++) {				/* Prepare to rotate 8 bits */
			if (crc & 1)					/* b0 is set... */
				crc = (crc >> 1) ^ poly;	/* rotate and XOR with ZIP polynomic */
			else							/* b0 is clear... */
				crc >>= 1;					/* just rotate */
											/* Some compilers need:
											crc &= 0xFFFFFFFF;
											*/
		}									/* Loop for 8 bits */
	}										/* Loop until num=0 */
	return(crc);							/* Return updated CRC */
}


std::string ReadAFileString(std::string filename) {
	uint32 length;
	char* data = ReadAFile(filename.c_str(), length, false);
	if (!data || !length)
		return "";
	std::string dataString = data;
	delete [] data;
	return dataString;
}

char* ReadAFile(const char* dir, const char* filename, uint32& length, bool binary) {
	if (!dir || !filename || strlen(filename) == 0)
		return NULL;

	char* fullname = new char[strlen(dir) + strlen(filename) + 5];
	snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
	char* res = ReadAFile(fullname, length, binary);
	delete [] fullname;
	return res;
}

char* ReadAFile(const char* filename, uint32& length, bool binary) {
	if (!filename || strlen(filename) == 0)
		return NULL;

	length = 0;

	FILE* file;
	if (binary)
		file = fopen(filename, "rb");
	else
		file = fopen(filename, "r");
	if (file == NULL) {
		//LogPrint(0,0,0,"Couldn't open file %s", filename);
		return NULL;
	}

	fseek(file, 0, SEEK_END);
	length = ftell(file);
	fseek(file, 0, SEEK_SET);

	if (length <= 0) {
		fclose(file);
		//LogPrint(0,0,0,"File %s has no size", filename);
		return NULL;
	}

	char* data = new char[length+1];

	int res = (int)fread(data, 1, length, file);

	if ((res <= 0) || (res != length)) {
		int error = ferror(file);
		int eof = feof(file);
		if (eof == 0) {
			delete [] data;
			fclose(file);
			LogPrint(0,0,0,"File %s has no data", filename);
			return NULL;
		}
	}

	fclose(file);

	length = (uint32)res;
	data[length] = 0;
	return data;
}

bool WriteAFile(const char* dir, const char* filename, const char* data, uint32 length, bool binary) {
	if (!dir || !filename || strlen(filename) == 0)
		return NULL;

	char* fullname = new char[strlen(dir) + strlen(filename) + 5];
	snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
	bool res = WriteAFile(fullname, data, length, binary);
	delete [] fullname;
	return res;
}

bool WriteAFile(const char* filename, const char* data, uint32 length, bool binary) {
	if (!filename || strlen(filename) == 0)
		return false;

	FILE* file;
	if (binary)	
		file = fopen(filename, "wb");
	else
		file = fopen(filename, "w");
	if (file == NULL)
		return false;

	int res = (int)fwrite(data, 1, length, file);
	fclose(file);
	return (res == length);
}

bool AppendToAFile(const char* dir, const char* filename, const char* data, uint32 length, bool binary) {
	if (!dir || !filename || strlen(filename) == 0)
		return NULL;

	char* fullname = new char[strlen(dir) + strlen(filename) + 5];
	snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
	bool res = AppendToAFile(fullname, data, length, binary);
	delete [] fullname;
	return res;
}

bool AppendToAFile(const char* filename, const char* data, uint32 length, bool binary) {
	if (!filename || strlen(filename) == 0)
		return false;

	FILE* file;
	if (binary)	
		file = fopen(filename, "ab");
	else
		file = fopen(filename, "a");
	if (file == NULL)
		return false;

	int res = (int)fwrite(data, 1, length, file);
	fclose(file);
	return (res == length);
}

bool DeleteAFile(const char* dir, const char* filename, bool force) {
	if (!dir || !filename || strlen(filename) == 0)
		return NULL;

	char* fullname = new char[strlen(dir) + strlen(filename) + 5];
	snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
	bool res = DeleteADir(fullname, force);
	delete [] fullname;
	return res;
}

bool DeleteAFile(const char* filename, bool force) {
	return DeleteADir(filename, force);
}

bool ChangeAFileAttr(const char* filename, bool read, bool write) {
	if (!filename || strlen(filename) == 0)
		return false;
	#ifdef WINDOWS
		if (!write)
			return (SetFileAttributes(filename, FILE_ATTRIBUTE_READONLY) != 0);
		else
			return (SetFileAttributes(filename, FILE_ATTRIBUTE_NORMAL) != 0);
	#else
		if (read && write)
			return (chmod(filename,S_IREAD | S_IWRITE) != 0);
		else if (read)
			return (chmod(filename,S_IREAD) == 0);
		else if (write)
			return (chmod(filename,S_IWRITE) == 0);
		else
			return (chmod(filename,0) == 0);
	#endif
}

bool MoveAFile(const char* oldfilename, const char* newfilename, bool force) {
	#ifdef WINDOWS
		bool result;
		if (force)
			result = (MoveFileEx(oldfilename, newfilename, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0);
		else
			result = (MoveFileEx(oldfilename, newfilename, MOVEFILE_COPY_ALLOWED) != 0);
		if (result)
			return true;
		LogPrint(0,LOG_SYSTEM,0,"MoveAFile '%s' to '%s' failed with: %s", oldfilename, newfilename, GetLastOSErrorMessage().c_str());
		return false;
	#else
		if (rename(oldfilename, newfilename) == 0)
			return true;
		int error = errno;
		if (force) {
			FileDetails info = GetFileDetails(newfilename);
			if (info.doesExist) {
				delete(newfilename);
				if (rename(oldfilename, newfilename) == 0)
					return true;
			}
		}
		if (error == EXDEV) {
			if (CopyAFile(newfilename, oldfilename)) {
				DeleteAFile(oldfilename, true);
				return true;
			}
		}
		return false;
	#endif
}

bool MoveAFile(const char* olddir, const char* oldfilename, const char* newdir, const char* newfilename, bool force) {
	if (!newdir || !newfilename || !strlen(newfilename) ||
		!olddir || !oldfilename || !strlen(oldfilename) )
		return NULL;

	char* oldname = new char[strlen(olddir) + strlen(oldfilename) + 5];
	snprintf(oldname, strlen(olddir) + strlen(oldfilename) + 5, "%s/%s", olddir, oldfilename);
	char* newname = new char[strlen(newdir) + strlen(newfilename) + 5];
	snprintf(newname, strlen(newdir) + strlen(newfilename) + 5, "%s/%s", newdir, newfilename);
	bool res = MoveAFile(newname, oldname, force);
	delete [] oldname;
	delete [] newname;
	return res;
}

bool CopyAFile(const char* oldfilename, const char* newfilename, bool force) {
	#ifdef WINDOWS
		if (force)
			return (CopyFile(oldfilename, newfilename, FALSE) != 0);
		else
			return (CopyFile(oldfilename, newfilename, TRUE) != 0);
	#else
		FileDetails target = GetFileDetails(newfilename);
		if (target.doesExist && !force)
			return false;
		if (target.isDirectory)
			return false;
		uint32 size;
		char* data = ReadAFile(oldfilename, size, true);
		if (!data || !size) {
			delete [] data;
			return false;
		}

		if ((target.doesExist) && !DeleteAFile(newfilename, true)) {
			delete [] data;
			return false;
		}

		if (!WriteAFile(newfilename, data, size, true)) {
			delete [] data;
			return false;
		}
		delete [] data;
		return true;
	#endif
}

bool CopyAFile(const char* olddir, const char* oldfilename, const char* newdir, const char* newfilename, bool force) {
	if (!newdir || !newfilename || !strlen(newfilename) ||
		!olddir || !oldfilename || !strlen(oldfilename) )
		return NULL;

	char* oldname = new char[strlen(olddir) + strlen(oldfilename) + 5];
	snprintf(oldname, strlen(olddir) + strlen(oldfilename) + 5, "%s/%s", olddir, oldfilename);
	char* newname = new char[strlen(newdir) + strlen(newfilename) + 5];
	snprintf(newname, strlen(newdir) + strlen(newfilename) + 5, "%s/%s", newdir, newfilename);
	bool res = CopyAFile(newname, oldname, force);
	delete [] oldname;
	delete [] newname;
	return res;
}

bool CreateADir(const char* dirname) {
	if (!dirname || strlen(dirname) == 0)
		return false;

	FileDetails info = GetFileDetails(dirname);

	if (info.doesExist) {
		if (info.isDirectory)
			return true;
		else
			return false;
	}

	#ifdef WINDOWS
		if (CreateDirectory(dirname, NULL) != 0)
			return true;
		else
			return false;
	#else
		if (mkdir(dirname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
			return true;
		else
			return false;
	#endif
}

bool DeleteADir(const char* dirname, bool force) {
	if (!dirname || strlen(dirname) == 0)
		return false;

	FileDetails info = GetFileDetails(dirname);

	if (!info.doesExist)
		return true;

	// If it is a file
	if (!info.isDirectory) {
		#ifdef WINDOWS
			if (DeleteFile(dirname) != 0)
				return true;
			else if (!force)
				return false;
			else {
				ChangeAFileAttr(dirname, true, true);
				if (DeleteFile(dirname) != 0)
					return true;
				else
					return false;
			}
		#else
			if (unlink(dirname) == 0)
				return true;
			else if (!force)
				return false;
			else {
				ChangeAFileAttr(dirname, true, true);
				if (unlink(dirname) == 0)
					return true;
				else
					return false;
			}
		#endif
	}

	// Now we know it is a dir
	#ifdef WINDOWS
		if (RemoveDirectory(dirname) != 0)
			return true;
		else if (!force)
			return false;
		else {
			DeleteFilesInADir(dirname, true);
			if (RemoveDirectory(dirname) != 0)
				return true;
			else
				return false;
		}
	#else
		if (rmdir(dirname) == 0)
			return true;
		else if (!force)
			return false;
		else {
			DeleteFilesInADir(dirname, true);
			if (rmdir(dirname) == 0)
				return true;
			else
				return false;
		}
	#endif

}

bool DeleteFilesInADir(const char* dirname, bool force) {
	if (!dirname || strlen(dirname) == 0)
		return false;

	uint32 count;
	char* files = utils::GetFileList(dirname, NULL, count, true);
	if (!files)
		return true;

	const char* name;
	for (uint32 n = 0; n<count; n++) {
		name = files + (n*(MAXFILENAMELEN+1));
		if (!DeleteADir(name, force)) {
			delete[] files;
			return false;
		}
	}

	delete [] files;
	return true;
}





uint32 TextReplaceCharsInPlace(char* str, uint32 size, char find, char replace) {
	uint32 c = 0;
	for (uint32 n=0; n<size; n++) {
		if (str[n] == find) {
			str[n] = replace;
			c++;
		}
	}
	return c;
}

const char* laststrstr(const char* str1, const char* str2) {
	const char* strp;
	int len1, len2;
 
	len2 = (int)strlen(str2);
	if(len2==0)
		return (char*)str1;
 
	len1 = (int)strlen(str1);
	if (len1 == len2)
		return (strcmp(str1, str2) == 0) ? str1 : 0;
	else if (len1 - len2 < 0)
		return 0;
	
	strp = (char*)(str1 + len1 - len2);
	while(strp != str1) {
		if(*strp == *str2) {
			if(strncmp(strp,str2,len2)==0)
				return strp;
		}
		strp--;
	}
	return 0;
}

const char* laststristr(const char* str1, const char* str2) {
	const char* strp;
	int len1, len2;
 
	len2 = (int)strlen(str2);
	if(len2==0)
		return (char*)str1;
 
	len1 = (int)strlen(str1);
	if (len1 == len2)
		return (stricmp(str1, str2) == 0) ? str1 : 0;
	else if (len1 - len2 < 0)
		return 0;
	
	strp = (char*)(str1 + len1 - len2);
	while(strp != str1) {
		if(*strp == *str2) {
			if(strnicmp(strp,str2,len2)==0)
				return strp;
		}
		strp--;
	}
	return 0;
}

bool TextEndsWith(const char* str, const char* end, bool caseSensitive) {
	if (!caseSensitive)
		return (laststristr(str, end) == str + strlen(str) - strlen(end));
	else
		return (laststrstr(str, end) == str + strlen(str) - strlen(end));
}

bool TextStartsWith(const char* str, const char* start, bool caseSensitive) {
	if (!caseSensitive)
		return (stristr(str, start) == str);
	else
		return (strstr(str, start) == str);
}


std::string GetFilePath(const char* filename) {
	std::string name = filename;
	std::string::size_type p1 = name.find_last_of('/');
	std::string::size_type p2 = name.find_last_of('\\');
	if (p1 == std::string::npos) {
		if (p2 == std::string::npos)
			return "";
		else
			return name.substr(0, p2 + 1);
	}
	else if (p2 == std::string::npos)
		return name.substr(0, p1 + 1);
	else
		return name.substr(0, (p1 > p2) ? p1 + 1 : p2 + 1);
}

const char* GetFileBasename(const char* filename) {
	std::string name = filename;
	std::string::size_type p1 = name.find_last_of('/');
	std::string::size_type p2 = name.find_last_of('\\');
	if (p1 == std::string::npos) {
		if (p2 == std::string::npos)
			return filename;
		else
			return filename + p2 + 1;
	}
	else if (p2 == std::string::npos)
		return filename + p1 + 1;
	else
		return (p1 > p2) ? filename + p1 + 1 : filename + p2 + 1;
}

char* GetFileList(const char* dirname, const char* ext, uint32& count, bool fullpath, uint32 maxNameLen) {
	DIR* dir = opendir(dirname);
	if (dir == NULL)
		return NULL;

	uint32 extlen = (ext == NULL) ? 0 : (uint32)strlen(ext);
	struct dirent* direntry = NULL;

	uint32 tempMaxCount = 50;
	uint32 len = (maxNameLen+1)*tempMaxCount;
	char* result = new char[len];
	char* dst = result;
	char* tmp;
	uint32 dirnamelen = (uint32)strlen(dirname);
	bool dirslash = (*(dirname + dirnamelen - 1) == '/') || (*(dirname + dirnamelen - 1) == '\\');

	count = 0;
	direntry = readdir(dir);
	while (direntry) {
		if (direntry->d_name) {
			if ( !strcmp(direntry->d_name, ".") || !strcmp(direntry->d_name, "..") ) {
				direntry = readdir(dir);
				continue;
			}
			if (ext && extlen && !TextEndsWith(direntry->d_name, ext)) {
				direntry = readdir(dir);
				continue;
			}
			if (fullpath) {
				utils::strcpyavail(dst, dirname, maxNameLen, true);
				if (!dirslash) {
					memset(dst+dirnamelen, '/', 1);
					utils::strcpyavail(dst+dirnamelen+1, direntry->d_name, maxNameLen, true);
				}
				else
					utils::strcpyavail(dst+dirnamelen, direntry->d_name, maxNameLen, true);
			}
			else
				utils::strcpyavail(dst, direntry->d_name, maxNameLen, true);
			dst+= maxNameLen+1;
			count++;
			if (count >= tempMaxCount) {
				tempMaxCount *= 2;
				tmp = new char[len*2];
				memcpy(tmp, result, len);
				delete [] result;
				result = tmp;
				dst = result + len;
				len *= 2;
			}
		}
		direntry = readdir(dir);
	}
	closedir(dir);
	return result;
}

bool DoesADirExist(const char* dirname) {
	FileDetails details = GetFileDetails(dirname);
	return (details.doesExist && details.isDirectory);
}

bool DoesAFileExist(const char* filename) {
	FileDetails details = GetFileDetails(filename);
	return (details.doesExist && !details.isDirectory);
}

FileDetails GetFileDetails(const char* filename) {
	struct stat statbuf;
	int fd, result;

	FileDetails info;

	info.doesExist = false;
	info.isDirectory = false;
	info.isReadable = false;
	info.isWritable = false;
	info.isExecutable = false;
	info.size = 0;
	info.lastAccessTime = 0;
	info.lastModifyTime = 0;
	info.creationTime = 0;

	// If file cannot be opened
	if ((fd = _open(filename, O_RDONLY)) ==  -1) {
		// On Windows, this might be a dir
		DIR* dir = opendir(filename);
		if (dir == NULL)
			return info;
		struct dirent* direntry = readdir(dir);

		info.doesExist = true;
		info.isDirectory = true;
		info.lastAccessTime = info.lastModifyTime = info.creationTime = GetTimeNow();
		info.isReadable = true;
		info.isWritable = true;
		info.isExecutable = true;

		closedir(dir);
		return info;
	}

	// Get data associated with "fd":
	result = fstat(fd, &statbuf);

	// Check if statistics are valid:
	if( result != 0 ) {
		_close(fd);
		return info;
	}

	_close(fd);
	info.doesExist = true;

	info.size = statbuf.st_size;
	info.isDirectory = ((statbuf.st_mode & S_IFDIR) != 0);
	info.lastAccessTime = FTime2PsyTime(statbuf.st_atime);
	info.lastModifyTime = FTime2PsyTime(statbuf.st_ctime);
	info.creationTime = FTime2PsyTime(statbuf.st_ctime);

	#ifdef WINDOWS
		info.isReadable = ((statbuf.st_mode & _S_IREAD) != 0);
		info.isWritable = ((statbuf.st_mode & _S_IWRITE) != 0);
		info.isExecutable = ((statbuf.st_mode & _S_IEXEC) != 0);
	#else
		info.isReadable = ((statbuf.st_mode & S_IRUSR) != 0);
		info.isWritable = ((statbuf.st_mode & S_IWUSR) != 0);
		info.isExecutable = ((statbuf.st_mode & S_IXUSR) != 0);
	#endif

	return info;

}

char* TextSubstringCopy(const char* ascii, uint32 start, uint32 end) {
	uint32 len;
	char* copy;
	if (!ascii || (start >= end) || (!(len = (uint32)strlen(ascii))) ) {
		copy = new char[1];
		copy[0] = 0;
	}
	else {
		if (end > len - 1)
			end = len - 1;
		copy = new char[end - start + 2];
		memcpy(copy, ascii + start,
			end - start + 1);
		copy[end - start + 1] = 0;
	}
	return copy;
}


bool IsTextNumeric(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	uint32 len = (uint32)strlen(ascii);
	uint32 stop = end ? end+1 : len;
	if (stop > len) stop = len;
	for (uint32 n=start; n<stop; n++) {
        if (( ascii[n] < '0' || ascii[n] > '9') && ascii[n] != '.')
            return false;
	}
	return true;
}

int64 Ascii2Int64(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
	#ifdef WINDOWS
		return _strtoi64(ascii + start, NULL, 10);
	#else
		return strtoll(ascii + start, NULL, 10);
	#endif
	char* copy = TextSubstringCopy(ascii, start, end);
	#ifdef WINDOWS
		int64 val = _strtoi64(copy, NULL, 10);
	#else
		int64 val = strtoll(copy, NULL, 10);
	#endif
	delete[] copy;
	return val;
}

uint64 Ascii2Uint64(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
	#ifdef WINDOWS
		return _strtoui64(ascii + start, NULL, 10);
	#else
		return strtoull(ascii + start, NULL, 10);
	#endif
	char* copy = TextSubstringCopy(ascii, start, end);
	#ifdef WINDOWS
		uint64 val = _strtoui64(copy, NULL, 10);
	#else
		uint64 val = strtoull(copy, NULL, 10);
	#endif
	delete[] copy;
	return val;
}

uint64 AsciiHex2Uint64(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
	#ifdef WINDOWS
		return _strtoui64(ascii + start, NULL, 16);
	#else
		return strtoull(ascii + start, NULL, 16);
	#endif
	char* copy = TextSubstringCopy(ascii, start, end);
	#ifdef WINDOWS
		uint64 val = _strtoui64(copy, NULL, 16);
	#else
		uint64 val = strtoull(copy, NULL, 16);
	#endif
	delete[] copy;
	return val;
}

int32 Ascii2Int32(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
		return (int32)strtol(ascii + start, NULL, 10);
	char* copy = TextSubstringCopy(ascii, start, end);
	int32 val = (int32)strtol(copy, NULL, 10);
	delete[] copy;
	return val;
}

uint32 Ascii2Uint32(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
		return (uint32)strtoul(ascii + start, NULL, 10);
	char* copy = TextSubstringCopy(ascii, start, end);
	uint32 val = (uint32)strtoul(copy, NULL, 10);
	delete[] copy;
	return val;
}

uint32 AsciiHex2Uint32(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
		return (uint32)strtoul(ascii + start, NULL, 16);
	char* copy = TextSubstringCopy(ascii, start, end);
	uint32 val = (uint32)strtoul(copy, NULL, 16);
	delete[] copy;
	return val;
}

float64 Ascii2Float64(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
		return strtod(ascii + start, NULL);
	char* copy = TextSubstringCopy(ascii, start, end);
	float64 val = strtod(copy, NULL);
	delete[] copy;
	return val;
}


std::string DecodeJSON(std::string str) {
	bool isEscaped = false;
	char* tmp = new char[5];
	tmp[4] = 0;
	std::ostringstream o;
	std::string::iterator c;
    for (c = str.begin(); c != str.end(); c++) {
		if (isEscaped) {
			switch (*c) {
			case '"': o << "\""; break;
			case '\\': o << "\\"; break;
			case 'b': o << "\b"; break;
			case 'f': o << "\f"; break;
			case 'n': o << "\n"; break;
			case 'r': o << "\r"; break;
			case 't': o << "\t"; break;
			case 'u':
				c++;
				tmp[0] = (uint8)(*c++);
				tmp[1] = (uint8)(*c++);
				tmp[2] = (uint8)(*c++);
				tmp[3] = (uint8)(*c);
				o << (char) strtol(tmp, NULL, 16);
				break;
			default: o << *c;
			}
			isEscaped = false;
		}
		else {
			switch (*c) {
			case '\\': isEscaped = true; break;
			default: o << *c;
			}
		}
    }
	delete[] tmp;
    return o.str();
}

std::string EncodeJSON(std::string str) {
    std::ostringstream o;
	std::string::iterator c;
	for (c = str.begin(); c != str.end(); c++) {
        switch (*c) {
        case '"': o << "\\\""; break;
        case '\\': o << "\\\\"; break;
        case '\b': o << "\\b"; break;
        case '\f': o << "\\f"; break;
        case '\n': o << "\\n"; break;
        case '\r': o << "\\r"; break;
        case '\t': o << "\\t"; break;
        default:
            if ('\x00' <= *c && *c <= '\x1f') {
                o << "\\u"
                  << std::hex << std::setw(4) << std::setfill('0') << (int)*c;
            } else {
                o << *c;
            }
        }
    }
    return o.str();

	//if (str.size() == 0)
	//	return str;
	//std::string result = str;
	//char c;
	//char* tmp = (char*)malloc(20);

	//for (uint32 n=0; n<result.size(); n++) {
	//	switch(c = result[n]) {
	//		case '\n':
	//			result.replace(n, 1, "\\n");
	//			n+=1;
	//			break;
	//		case '\r':
	//			result.replace(n, 1, "\\r");
	//			n+=1;
	//			break;
	//		case '\t':
	//			result.replace(n, 1, "\\t");
	//			n+=2;
	//			break;
	//		case '"':
	//			result.replace(n, 1, "\\\"");
	//			n+=2;
	//			break;
	//		case '\\':
	//			result.replace(n, 1, "\\\\");
	//			n+=2;
	//			break;
	//		case '\b':
	//			result.replace(n, 1, "\\b");
	//			n+=2;
	//			break;
	//		case '\f':
	//			result.replace(n, 1, "\\f");
	//			n+=2;
	//			break;
	//		default:
	//			if ('\x00' <= c && c <= '\x1f') {
	//				snprintf(tmp, 20, "\\u%.4x;", (int) c & 0xFF);
	//				result.replace(n, 1, tmp);
	//				n+=(uint32)strlen(tmp)-1;
	//			}
	//			break;
	//	}
	//}

	//free((char*)tmp);

	//return result;
}


// Seeds the rand() function
bool SeedRandomValues(uint32 seedvalue) {
	srand( seedvalue ? seedvalue : (uint32)GetTimeNow());
	return true;
}

// Returns a random value between 0.0 and 1.0
double RandomValue() {
	double d = 0;
	double t = (1.0/((RAND_MAX + 1.0)*(RAND_MAX + 1.0)*(RAND_MAX + 1.0)));
	do {
		d = (rand () * ((RAND_MAX + 1.0) * (RAND_MAX + 1.0))
			+ rand () * (RAND_MAX + 1.0)
			+ rand ()) * t;
	} while (d >= 1); /* Round off */
	return d;
}

// Returns a random value between 0.0 and max
double RandomValue(double max) {
	return RandomValue() * max;
}

// Returns a random value between from and to
double RandomValue(double from, double to) {
	if (to < from)
		return (RandomValue()*(from-to))+to;
	else
		return (RandomValue()*(to-from))+from;
}

// Returns a random value between from and to, rounded to nearest interval
double RandomValue(double from, double to, double interval) {
	if (to < from)
		return RandomValue(to, from, interval);
	double r = RandomValue(0, to-from);
	uint64 n = (uint64)(r/interval);
	double l = r - (n*interval);
	if (l >= (0.5*interval))
		return from + (n*interval);
	else
		return from + ((n+1)*interval);
}

int64 RandomInt(int64 from, int64 to) {
	return (int64)roundl(RandomValue((double)from, (double)to));
}


//std::string BytifySize(int32 val) {
//	return BytifySize((double)val);
//}

std::string BytifySize(double val) {
	
	double kb = 1024;
	double mb = kb*1024;
	double gb = mb*1024;
	double tb = gb*1024;

	char *tmp = new char[64];

	if (val < 512)
		snprintf(tmp, 64, "%.2f B", val);
	else if (val < 512*kb) {
		val = val / kb;
		snprintf(tmp, 64, "%.2f KB", val);
	}
	else if (val < 512*mb) {
		val = val / mb;
		snprintf(tmp, 64, "%.2f MB", val);
	}
	else if (val < 512*gb) {
		val = val / gb;
		snprintf(tmp, 64, "%.2f GB", val);
	}
	else {
		val = val / tb;
		snprintf(tmp, 64, "%.2f TB", val);
	}

	std::string result = tmp;
	delete [] tmp;
	return result;
}

//std::string BytifySizes(int32 val1, int32 val2) {
//	return BytifySizes((double)val1, (double) val2);
//}

std::string BytifySizes(double val1, double val2) {
	
	double kb = 1024;
	double mb = kb*1024;
	double gb = mb*1024;
	double tb = gb*1024;

	char *tmp = new char[64];
	char *end = new char[64];

	double val;
	double maximum = ((val1 > val2) ? val1 : val2);

	if (maximum < 512) {
		val = 1;
		utils::strcpyavail(end, "B", 32, true);
	}
	else if (maximum < 512*kb) {
		val = kb;
		utils::strcpyavail(end, "KB", 32, true);
	}
	else if (maximum < 512*mb) {
		val = mb;
		utils::strcpyavail(end, "MB", 32, true);
	}
	else if (maximum < 512*gb) {
		val = gb;
		utils::strcpyavail(end, "GB", 32, true);
	}
	else {
		val = gb;
		utils::strcpyavail(end, "TB", 32, true);
	}
	snprintf(tmp, 64, "%.2f / %.2f %s", ((double)val1)/val, ((double)val2)/val, (char*) end);

	std::string result = tmp;
	delete [] tmp;
	delete [] end;
	return result;

}

//std::string BytifyRate(int32 val) {
//	return BytifyRate((double)val);
//}

std::string BytifyRate(double val) {
	char *tmp = new char[64];
	snprintf(tmp, 64, "%s/sec", (char*) BytifySize(val).c_str());
	std::string result = tmp;
	delete [] tmp;
	return result;
}

//std::string BytifyRates(int32 val1, int32 val2) {
//	return BytifyRates((double)val1, (double)val2);
//}

std::string BytifyRates(double val1, double val2) {
	char *tmp = new char[64];
	snprintf(tmp, 64, "%s/sec", (char*) BytifySizes(val1, val2).c_str());
	std::string result = tmp;
	delete [] tmp;
	return result;
}







#ifdef WINDOWS

uint32 ReadRegistryDWORD(const char* key, const char* entry) {
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS)
		return 0;

	DWORD dwType = REG_DWORD;
	DWORD numVal = 0;
	DWORD dwSize = sizeof(numVal);

	err = RegQueryValueEx(hKey, entry, NULL, &dwType, (LPBYTE)&numVal, &dwSize);
	RegCloseKey(hKey);
	if (err != ERROR_SUCCESS)
		return 0;
	return numVal;
}

uint64 ReadRegistryQWORD(const char* key, const char* entry) {
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS)
		return 0;

	DWORD dwType = REG_QWORD;
	uint64 numVal = 0;
	DWORD dwSize = sizeof(numVal);

	err = RegQueryValueEx(hKey, entry, NULL, &dwType, (LPBYTE)&numVal, &dwSize);
	RegCloseKey(hKey);
	if (err != ERROR_SUCCESS)
		return 0;
	return numVal;
}

std::string ReadRegistryString(const char* key, const char* entry) {
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS) {
		return "";
	}

	DWORD dwType = REG_SZ;
	DWORD dwSize = 4096;
	char* lszValue = new char[dwSize];

	err = RegQueryValueEx(hKey, entry, NULL, &dwType, (LPBYTE)lszValue, &dwSize);
	RegCloseKey(hKey);
	if (err != ERROR_SUCCESS) {
		delete[] lszValue;
		return "";
	}

	std::string val = lszValue;
	delete[] lszValue;
	return val;
}

bool WriteRegistryDWORD(const char* key, const char* entry, uint32 value) {
	DWORD dwDisposition;
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS) {
		err = RegCreateKeyEx(hKeyRoot, keySubName.c_str(), 0, NULL, 0, KEY_WRITE | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition);
		if (err != ERROR_SUCCESS)
			return false;
	}

	DWORD dwType = REG_DWORD;
	err = RegSetValueEx(hKey, entry, 0, dwType, (const BYTE*)&value, sizeof(value));
	RegCloseKey(hKey);
	return (err == ERROR_SUCCESS);
}

bool WriteRegistryQWORD(const char* key, const char* entry, uint64 value) {
	DWORD dwDisposition;
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS) {
		err = RegCreateKeyEx(hKeyRoot, keySubName.c_str(), 0, NULL, 0, KEY_WRITE | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition);
		if (err != ERROR_SUCCESS)
			return false;
	}

	DWORD dwType = REG_QWORD;
	err = RegSetValueEx(hKey, entry, 0, dwType, (const BYTE*)&value, sizeof(value));
	RegCloseKey(hKey);
	return (err == ERROR_SUCCESS);
}

bool WriteRegistryString(const char* key, const char* entry, const char* value) {
	DWORD dwDisposition;
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS) {
		err = RegCreateKeyEx(hKeyRoot, keySubName.c_str(), 0, NULL, 0, KEY_WRITE | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition);
		if (err != ERROR_SUCCESS)
			return false;
	}

	DWORD dwType = REG_SZ;
	DWORD dwSize = 4096;

	err = RegSetValueEx(hKey, entry, 0, dwType, (LPBYTE)value, (DWORD)(strlen(value) + 1));
	RegCloseKey(hKey);
	return (err == ERROR_SUCCESS);
}

bool DeleteRegistryEntry(const char* key, const char* entry) {
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_SET_VALUE | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS)
		return false;

	//std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
	err = RegDeleteValue(hKey, entry);
	return (err == ERROR_SUCCESS);
}

bool DeleteRegistryKey(const char* entry) {
	// https://msdn.microsoft.com/en-us/library/aa379776(VS.85).aspx

	HKEY hKeyRoot;
	std::string keySubName = GetRegistryKeyInfo(entry, hKeyRoot);
	uint32 err = RegDeleteKeyEx(hKeyRoot, keySubName.c_str(), KEY_WOW64_64KEY, 0);
	return (err == ERROR_SUCCESS);
}

bool DeleteRegistryTree(const char* root, const char* key) {
	if (!key || !strlen(key))
		return false;

	HKEY hKey;
	HKEY hKeyRoot;
	std::string keySubName = GetRegistryKeyInfo(root, hKeyRoot);
	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS)
		return false;

	err = RegDeleteTree(hKey, key);
	return (err == ERROR_SUCCESS);
}

//bool CopyRegistryKey(const char* key, const char* newName) {
//}

bool CopyRegistryTree(const char* entry, const char* newName) {
	// https://msdn.microsoft.com/en-us/library/aa379768(VS.85).aspx

	// access 64 or 32-bit registry: https://msdn.microsoft.com/en-us/library/aa384129(v=vs.85).aspx
	// we always use the 64-bit registry here

	DWORD dwDisposition;
	HKEY hKey;
	HKEY hKeyRoot;
	HKEY hKeyNew;
	HKEY hKeyNewRoot;
	uint32 err;

	std::string keySubName = GetRegistryKeyInfo(entry, hKeyRoot);
	std::string keySubNewName = GetRegistryKeyInfo(newName, hKeyNewRoot);

	err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS)
		return false;

	err = RegCreateKeyEx(hKeyNewRoot, keySubNewName.c_str(), 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, &hKeyNew, &dwDisposition);
	if (err != ERROR_SUCCESS) {
		RegCloseKey(hKey);
		return false;
	}

	err = RegCopyTree(hKey, NULL, hKeyNew);

	RegCloseKey(hKey);
	RegCloseKey(hKeyNew);

	if (err != ERROR_SUCCESS) {
		RegDeleteKeyEx(hKeyNewRoot, keySubNewName.c_str(), KEY_WOW64_64KEY, 0);
		return false;
	}
	return true;
}

//bool RenameRegistryKey(const char* key, const char* newName) {
//	if (!CopyRegistryKey(key, newName))
//		return false;
//	if (!DeleteRegistryKey(key)) {
//		CopyRegistryKey(newName, key);
//		return false;
//	}
//	return true;
//}

//bool RenameRegistryTree(const char* entry, const char* newName) {
	//if (!CopyRegistryTree(entry, newName))
	//	return false;
	//if (!DeleteRegistryTree(entry)) {
	//	CopyRegistryTree(newName, entry);
	//	return false;
	//}
//	return true;
//}

std::string GetRegistryKeyInfo(const char* key, HKEY &hKeyRoot) {
	std::vector<std::string> path = utils::TextListSplit(key, "\\");
	if (!path.size()) path = utils::TextListSplit(key, "/");
	if (path.size() < 2) return false;
	if (stristr(path.at(0).c_str(), "HKEY_CLASSES_ROOT"))
		hKeyRoot = HKEY_CLASSES_ROOT;
	else if (stristr(path.at(0).c_str(), "HKEY_CURRENT_USER"))
		hKeyRoot = HKEY_CURRENT_USER;
	else if (stristr(path.at(0).c_str(), "HKEY_LOCAL_MACHINE"))
		hKeyRoot = HKEY_LOCAL_MACHINE;
	else if (stristr(path.at(0).c_str(), "HKEY_USERS"))
		hKeyRoot = HKEY_USERS;
	return utils::TextJoin(path, "\\", 1, (DWORD)(path.size() - 1));
}

std::string GetRegistryParentKeyInfo(const char* key, HKEY &hKeyRoot) {
	std::vector<std::string> path = utils::TextListSplit(key, "\\");
	if (!path.size()) path = utils::TextListSplit(key, "/");
	if (path.size() < 2) return false;
	if (stristr(path.at(0).c_str(), "HKEY_CLASSES_ROOT"))
		hKeyRoot = HKEY_CLASSES_ROOT;
	else if (stristr(path.at(0).c_str(), "HKEY_CURRENT_USER"))
		hKeyRoot = HKEY_CURRENT_USER;
	else if (stristr(path.at(0).c_str(), "HKEY_LOCAL_MACHINE"))
		hKeyRoot = HKEY_LOCAL_MACHINE;
	else if (stristr(path.at(0).c_str(), "HKEY_USERS"))
		hKeyRoot = HKEY_USERS;
	return utils::TextJoin(path, "\\", 1, (DWORD)(path.size() - 2));
}


#else // WINDOWS
	int32 ReadRegistryDWORD(const char* key) { return 0; }
	int64 ReadRegistryQWORD(const char* key) { return 0; }
	std::string ReadRegistryString(const char* key) { return ""; }
	bool WriteRegistryDWORD(const char* key, int32 value) { return false; }
	bool WriteRegistryQWORD(const char* key, int64 value) { return false; }
	bool WriteRegistryString(const char* key, const char* value) { return false; }
	bool DeleteRegistryKey(const char* key) { return false; }
	bool DeleteRegistryTree(const char* key) { return false; }
	//bool CopyRegistryKey(const char* key, const char* newName) { return false; }
	bool CopyRegistryTree(const char* entry, const char* newName) { return false; }
	//bool RenameRegistryKey(const char* key, const char* newName) { return false; }
	bool RenameRegistryTree(const char* entry, const char* newName) { return false; }
#endif // WINDOWS



} // namespace utils
} // namespace cmlabs


