

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  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.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
#if !defined(_UTILS_H_)
#define _UTILS_H_

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <sys/types.h>
#include <stdarg.h>
#include <iomanip>

#include "Types.h"

#ifdef WINDOWS
	#include <io.h>
//	#include <windows.h>
	#include <winsock2.h>
	#include <iphlpapi.h>
	#include <iptypes.h>
	#include "Psapi.h"
	#include "direntwin.h"
	#include <process.h>
	// #include <Dbghelp.h> // needed for program trace only, link with Dbghelp.lib
#else
	#include <semaphore.h>
	#include <dlfcn.h>
	#include <errno.h>
	#include <sys/utsname.h>
	#include <sys/time.h>
	#include <pthread.h>
	#include <signal.h>
	#include <unistd.h>
	#include <time.h>
	#include <sys/mman.h>
	#include <sys/syscall.h>
	#include <sys/sysinfo.h>
	#include <sys/socket.h>
	#include <netdb.h>
	#include <net/if.h>
	#include <sys/ioctl.h>
	#include <dirent.h>
	#include <netinet/tcp.h>
	#include <sys/wait.h>
	#include <execinfo.h>
	#include <sys/resource.h>
//	#include <curses.h>
#endif

#define WINSHMEMSPACE		"Local"
//#define WINSHMEMSPACE		"Global"

#define MAXKEYNAMELEN		256
#define MAXVALUENAMELEN		1024
#define MAXCONTENTLEN		4196
#define MAXCOMMANDLINELEN	1024
#define MAXFILENAMELEN		256
#define MAXSHMEMNAMELEN		1024
#define MAXSCRIPTLEN		10*1024

#define THREAD_NONE			0
#define THREAD_TERMINATED	1
#define THREAD_INIT			2
#define THREAD_RUNNING		3
#define THREAD_PAUSED		4
#define THREAD_INTERRUPTED	5

#define THREAD_STATS_OFF	0
#define THREAD_STATS_AUTO	1
#define THREAD_STATS_ADHOC	2

#ifdef WINDOWS
//	#define Mutex HANDLE
//	#define NMutex HANDLE
//	#define Semaphore HANDLE
	#define ThreadHandle HANDLE
//	#define THREAD_FUNCTION static DWORD WINAPI
//	#define THREAD_FUNCTION DWORD WINAPI
	#define THREAD_RET DWORD
	typedef LPTHREAD_START_ROUTINE THREAD_FUNCTION;
	#define	THREAD_FUNCTION_CALL WINAPI
	#define THREAD_ARG LPVOID
	#define thread_ret_val(ret) return ret;
	#define DllExport __declspec(dllexport)
	#define MUTEX_INVALID NULL
	#define SOCKETWOULDBLOCK WSAEWOULDBLOCK
	#define SOCKETTRYAGAIN WSAEWOULDBLOCK
	#define abs64 _abs64
#else
//	#define Mutex pthread_mutex_t
//	#define NMutex sem_t *
//	#define Semaphore sem_t*
	#define ThreadHandle pthread_t
//	#define THREAD_FUNCTION void*(*)(void*)
	#define THREAD_RET void *
	typedef THREAD_RET (*THREAD_FUNCTION)(void *);
	#define	THREAD_FUNCTION_CALL 
	#define THREAD_ARG void*
	#define thread_ret_val(ret) pthread_exit((THREAD_RET)(intptr_t)ret);
	#define stricmp strcasecmp
	#define MUTEX_INVALID 0
	#define SOCKET int
	typedef struct sockaddr SOCKADDR;
	#define SOCKET_ERROR	-1
	#define INVALID_SOCKET	-1
	#define closesocket(X)	close(X)
	#define SOCKETWOULDBLOCK EINPROGRESS
	#define SOCKETTRYAGAIN EAGAIN
	#define abs64 llabs
	#define MUTEXMEMSIZE 64
	#define EVENTMEMSIZE 256
	#define _strcmpi strcasecmp
	#define _strnicmp strnicmp
	#define DllExport
#endif

#ifndef SD_BOTH
	#define SD_BOTH 2
#endif

#define OSCPU_UNKNOWN 0
#define OSCPU_X86 1
#define OSCPU_AMD64 2
#define OSCPU_IA64 3

#define ERROR64 0xFFFFFFFFFFFFFFFFL
#define ERROR32 0xFFFFFFFF
#define ERROR16 0xFFFF
#define ERROR8 0xFF

#include "PsyTime.h"

namespace cmlabs {

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

class CleanShutdown {
public:
	CleanShutdown();
	~CleanShutdown();
};

bool ClearUtilsMaps();

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

#define LOG_NOTOPIC		0
#define LOG_SYSTEM		1
#define LOG_NETWORK		2
#define LOG_MEMORY		3
#define LOG_PROCESS		4
#define LOG_TOPIC5		5
#define LOG_TOPIC6		6
#define LOG_TOPIC7		7
#define LOG_TOPIC8		8
#define LOG_TOPIC9		9
#define LOG_TOPIC10		10
#define LOG_TOPIC11		11
#define LOG_MAXCOUNT	12

#define LOGPRINT	1
#define LOGDEBUG	2

std::string GetDataTypeName(uint32 datatype);
uint32 GetDataTypeID(const char* typeName);

// #define GETLOGTEXT(l) (char*)l+sizeof(LogEntry)
struct LogEntry {
	uint32	size;		// 
	uint32	cid;		// LOGENTRYID
	uint64	time;		// 
	uint32	source;		// 
	uint8	subject;	// 
	uint8	level;		// 
	uint8	type;		// 
	const char* getText(uint32& len);
	bool setText(char* text, uint32 len);
	std::string toJSON();
	std::string toXML();
};

class LogReceiver {
public:
	LogReceiver() {}
//	virtual bool logPrint(uint32 source, uint8 subject, uint8 level, std::string text, uint64 time) = 0;
//	virtual bool logDebug(uint32 source, uint8 subject, uint8 level, std::string text, uint64 time) = 0;
	virtual bool logEntry(LogEntry* entry) = 0;
};

class LogSystem {
public:
	LogSystem();
	~LogSystem();

	static LogSystem* LogSingleton;
	static bool SetLogReceiver(LogReceiver* rec);

	static bool SetLogFileOutput(const char* logfile, bool printOut = true);
	static bool SetLogLevelDebug(uint8 level);
	static bool SetLogLevelDebug(uint8 subject, uint8 level);
	static bool SetLogLevelVerbose(uint8 level);
	static bool SetLogLevelVerbose(uint8 subject, uint8 level);

	static bool LogSystemPrint(uint32 source, uint8 subject, uint8 level, const char *formatstring, ... );
	static bool LogSystemDebug(uint32 source, uint8 subject, uint8 level, const char *formatstring, ... );

	uint8 logLevelVerbose[LOG_MAXCOUNT];
	uint8 logLevelDebug[LOG_MAXCOUNT];
	LogReceiver* logReceiverObj;
	char* logfile;
	bool printToStdOut;
};

#define LogPrint LogSystem::LogSystemPrint
#define LogDebug LogSystem::LogSystemDebug

namespace utils {

bool UtilsTest();

// bool GetSystemName(uint32 id, const char* title, char* name, uint32 size);



//////////////////////////////////////////////////////////////
//                         Mutexes                          //
//////////////////////////////////////////////////////////////

#ifdef WINDOWS
	#define SharedMemoryFileHandleMapType std::map<char*, HANDLE>
#else
	#define SharedMemoryFileHandleMapType std::map<void*, std::string>
#endif
static SharedMemoryFileHandleMapType* SharedMemoryFileHandleMap = NULL;

static uint16 SharedSystemInstance = 0;

bool SetSharedSystemInstance(uint32 inst);
uint32 GetSharedSystemInstance();

typedef bool (* DrumBeatFunc)(uint32 id, uint32 count);

struct DrumBeatInfo {
	uint32 id;
	uint64 createdTime;
	uint64 started;
	uint32 interval;
	DrumBeatFunc func;
	uint32 count;
	#ifdef WINDOWS
		HANDLE handle;
	#else
		timer_t handle;
	#endif
};

static std::map<uint32, DrumBeatInfo>* DrumBeatMap = NULL;

bool CreateDrumBeat(uint32 id, uint32 interval, DrumBeatFunc func, bool autostart = false);
bool StartDrumBeat(uint32 id);
bool StopDrumBeat(uint32 id);
bool EndDrumBeat(uint32 id);

class Mutex {
public:
	Mutex();
	Mutex(const char* name, bool force = false);
	~Mutex();
	bool enter();
	bool enter(uint32 timeout, const char* errorMsg = NULL);
	bool leave();
//	bool destroy();
protected:
	char *name;
	bool created;
	uint32 osid;
	uint32 total;
	uint32 count;
	#ifdef WINDOWS
		HANDLE mutex;
	#else
		//sem_t* semaphore;
		pthread_mutex_t* mutex;
	#endif
};

class Semaphore {
public:
	Semaphore(uint32 maxCount = 100);
	Semaphore(const char* name, uint32 maxCount = 100);
	~Semaphore();
	bool wait();
	bool wait(uint32 timeout);
	bool signal();
//	bool destroy();
	char *name;
protected:
	#ifdef WINDOWS
		HANDLE semaphore;
	#else
		sem_t* semaphore;
	#endif
};

class Event {
public:
	Event();
	Event(const char* name);
	~Event();
	bool waitNext();
	bool waitNext(uint32 timeout);
	bool signal();
//	bool destroy();
protected:
	char *name;
	#ifdef WINDOWS
		HANDLE event;
	#else
		pthread_cond_t* event;
		pthread_mutex_t* mutex;
	#endif
};

bool EnterMutex(const char* name, uint32 ms, bool autocreate = true);
bool LeaveMutex(const char* name);
bool DestroyMutex(const char* name);

Semaphore* GetSemaphore(const char* name, bool autocreate = true);
bool WaitForSemaphore(const char* name, uint32 ms, bool autocreate = true);
bool SignalSemaphore(const char* name);
bool DestroySemaphore(const char* name);

bool WaitNextEvent(const char* name, uint32 ms, bool autocreate = true);
bool SignalEvent(const char* name);
bool DestroyEvent(const char* name);

static Mutex* SharedMemoryMutexMapMutex = NULL;
static std::map<std::string, Mutex*>* SharedMemoryMutexMap = NULL;
typedef std::pair<std::string, Mutex*> SharedMemoryMutexMapPair;
static Mutex* SharedMemorySemaphoreMapMutex = NULL;
static std::map<std::string, Semaphore*>* SharedMemorySemaphoreMap = NULL;
typedef std::pair<std::string, Semaphore*> SharedMemorySemaphoreMapPair;
static Mutex* SharedMemoryEventMapMutex = NULL;
static std::map<std::string, Event*>* SharedMemoryEventMap = NULL;
typedef std::pair<std::string, Event*> SharedMemoryEventMapPair;

#ifdef WINDOWS
struct ProcessData {
	PROCESS_INFORMATION procInfo;
	STARTUPINFO si;
};
static std::map<uint32, ProcessData*>* ProcessInformationMap = NULL;
typedef std::pair <uint32, ProcessData*> Proc_Pair;
#endif

struct TimerSchedule {
	uint32 globalID;
	uint32 id;
	uint64 start;
	uint64 end;
	uint32 interval;
	#ifdef WINDOWS
		HANDLE handle;
	#else
		timer_t handle;
	#endif
};

struct TimerTrigger {
	uint32 id;
	uint64 time;
};

class Timer {
public:
	static std::map<uint32, Timer*>* timers;

	Timer();
	~Timer();

	bool addTimer(uint32 id, uint32 interval, uint64 start = 0, uint64 end = 0);
	bool triggerTimer(uint32 id);
	bool waitForTimer(uint32 timeout, uint32& id, uint64& time);
	bool removeTimer(uint32 id);

private:
	uint32 globalID;
	Mutex mutex;
	Semaphore semaphore;
	std::queue<TimerTrigger*> triggers;
	std::map<uint32, TimerSchedule*> schedules;
	std::list<TimerSchedule*> oldSchedules;
};

#ifdef WINDOWS
	void CALLBACK TimerCallback(PVOID arg, BOOLEAN TimerOrWaitFired);
#else
   static void TimerCallback(int sig, siginfo_t *siginfo, void *context);
#endif
bool UnitTest_Timer();
bool UnitTest_Utils();

//////////////////////////////////////////////////////////////
//                       Wait Queue                         //
//////////////////////////////////////////////////////////////

template <typename T>
class WaitQueue {
public:
	WaitQueue() {}
	virtual ~WaitQueue() {}

	bool add(T entry) {
		if (!mutex.enter(200, __FUNCTION__))
			return false;
		internalQueue.push(entry);
		semaphore.signal();
		mutex.leave();
		return true;
	}
	uint32 getCount() {
		uint32 size;
		if (!mutex.enter(200, __FUNCTION__))
			return 0;
		size = (uint32)internalQueue.size();
		mutex.leave();
		return size;
	}
	bool peekFirst(T& first) {
		if (!mutex.enter(200, __FUNCTION__))
			return false;
		if (!internalQueue.size()) {
			mutex.leave();
			return false;
		}
		first = internalQueue.front();
		mutex.leave();
		return true;
	}
	bool takeFirst(T& first) {
		if (!mutex.enter(200, __FUNCTION__))
			return false;
		if (!internalQueue.size()) {
			mutex.leave();
			return false;
		}
		first = internalQueue.front();
		internalQueue.pop();
		mutex.leave();
		return true;
	}
	bool waitForAndPeekFirst(T& first, uint32 timeoutMS) {
		if (!mutex.enter(200, __FUNCTION__))
			return false;
		if (internalQueue.size()) {
			first = internalQueue.front();
			mutex.leave();
			return true;
		}
		mutex.leave();
		if (!semaphore.wait(timeoutMS))
			return false;
		if (!mutex.enter(200, __FUNCTION__))
			return false;
		if (internalQueue.size()) {
			first = internalQueue.front();
			mutex.leave();
			return true;
		}
		else {
			mutex.leave();
			return false;
		}
	}
	bool waitForAndTakeFirst(T& first, uint32 timeoutMS) {
		if (!mutex.enter(200, __FUNCTION__))
			return false;
		if (internalQueue.size()) {
			first = internalQueue.front();
			internalQueue.pop();
			mutex.leave();
			return true;
		}
		mutex.leave();
		if (!semaphore.wait(timeoutMS))
			return false;
		if (!mutex.enter(200, __FUNCTION__))
			return false;
		if (internalQueue.size()) {
			first = internalQueue.front();
			internalQueue.pop();
			mutex.leave();
			return true;
		}
		else {
			mutex.leave();
			return false;
		}
	}
	virtual bool clear() {
		if (!mutex.enter(200, __FUNCTION__))
			return false;
		std::queue<T> empty;
		std::swap(internalQueue, empty);
		mutex.leave();
		return true;
	}

protected:
	Mutex mutex;
	Semaphore semaphore;
	std::queue<T> internalQueue;
};

template <typename T>
class WaitQueuePointer : public WaitQueue<T> {
public:
	WaitQueuePointer() {}
	virtual ~WaitQueuePointer() { clear(); }

	T waitForAndTakeFirst(uint32 timeoutMS) {
		T first;
		if (WaitQueue<T>::waitForAndTakeFirst(first, timeoutMS))
			return first;
		else
			return NULL;
	}

	virtual bool clear() {
		if (!WaitQueue<T>::mutex.enter(200, __FUNCTION__))
			return false;
		while (WaitQueue<T>::internalQueue.size()) {
			delete(WaitQueue<T>::internalQueue.front());
			WaitQueue<T>::internalQueue.pop();
		}
		WaitQueue<T>::mutex.leave();
		return true;
	}
};


//////////////////////////////////////////////////////////////
//                       Time Queue                         //
//////////////////////////////////////////////////////////////

template <typename T>
struct TimeQueueEntry {
	uint64 time;
	T entry;
};

template <typename T>
class TimeQueue {
public:
	TimeQueue() {}
	virtual ~TimeQueue() {}

	bool add(uint64 time, T entry) {
		if (!mutex.enter(200, __FUNCTION__))
			return false;
		TimeQueueEntry<T> newEntry;
		newEntry.time = time;
		newEntry.entry = entry;
		typename std::list< TimeQueueEntry<T> >::iterator i = internalList.begin(),
			e = internalList.end();
		while (i != e) {
			if (time < (*i).time) {
				internalList.insert(i, newEntry);
				mutex.leave();
				return true;
			}
			i++;
		}
		internalList.push_back(newEntry);
		mutex.leave();
		return true;
	}
	uint32 getCount() {
		uint32 size;
		if (!mutex.enter(200, __FUNCTION__))
			return 0;
		size = (uint32)internalList.size();
		mutex.leave();
		return size;
	}
	bool getNextEntryDue(T& entry) {
		if (!mutex.enter(200, __FUNCTION__))
			return false;
		if (!internalList.size()) {
			mutex.leave();
			return false;
		}
		uint64 now = GetTimeNow();
		TimeQueueEntry<T> first = internalList.front();
		if (first.time < now) {
			internalList.pop_front();
			entry = first.entry;
			mutex.leave();
			return true;
		}
		mutex.leave();
		return false;
	}
	virtual bool clear() {
		if (!mutex.enter(200, __FUNCTION__))
			return false;
		std::list< TimeQueueEntry<T> > empty;
		std::swap(internalList, empty);
		mutex.leave();
		return true;
	}

protected:
	Mutex mutex;
	std::list< TimeQueueEntry<T> > internalList;
};

template <typename T>
class TimeQueuePointer : public TimeQueue<T> {
public:
	TimeQueuePointer() {}
	virtual ~TimeQueuePointer() { clear(); }

	T getNextEntryDue() {
		T first;
		if (TimeQueue<T>::getNextEntryDue(first))
			return first;
		else
			return NULL;
	}

	virtual bool clear() {
		if (!TimeQueue<T>::mutex.enter(200, __FUNCTION__))
			return false;
		typename std::list< TimeQueueEntry<T> >::iterator i = TimeQueue<T>::internalList.begin(),
			e = TimeQueue<T>::internalList.end();
		while (i != e) {
			delete((*i).entry);
			i++;
		}
		TimeQueue<T>::internalList.clear();
		TimeQueue<T>::mutex.leave();
		return true;
	}
};


//////////////////////////////////////////////////////////////
//                      Shared Memory                       //
//////////////////////////////////////////////////////////////

// Create named shared memory segment
char* CreateSharedMemorySegment(const char* name, uint64 size, bool force = false);
// Get named shared memory segment
char* OpenSharedMemorySegment(const char* name, uint64 size);
// Close named shared memory segment
bool CloseSharedMemorySegment(char* data, uint64 size);
// Destroy named shared memory segment
// bool DestroySharedMemorySegment(char* data, uint32 size);

//////////////////////////////////////////////////////////////
//                      Bit Operations                      //
//////////////////////////////////////////////////////////////

// Calc byte size of bitfield with uint32 boundary 
uint32 Calc32BitFieldSize(uint32 bitsize);
// Reset bitfield set all as free
bool Reset32BitField(char* bitfield, uint32 bytesize);
// Get first zero bit in field of size bits, starting from data loc
bool GetFirstFreeBitLoc(const char* bitfield, uint32 bytesize, uint32& loc);
// 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);
// 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);
// 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);
// Get the nth bit in field of size bits, starting from data loc
bool GetBit(uint32 loc, const char* bitfield, uint32 bytesize, bit& val);
// Get location of last bit in use, starting from data loc
bool GetLastOccupiedBitLoc(const char* bitfield, uint32 bytesize, uint32& loc);



// Return string representing the bitfield
std::string GetBitFieldAsString(const char* bitfield, uint32 bytesize, uint32 size);
// Print string representing the bitfield
std::string PrintBitFieldString(const char* bitfield, uint32 bytesize, uint32 size, const char* title);


//////////////////////////////////////////////////////////////
//                        Threading                         //
//////////////////////////////////////////////////////////////

std::string PrintProgramTrace(uint32 startLine = 0, uint32 endLine = 0);
std::list<std::string> GetProgramTrace();

int32 AtomicIncrement32(int32 volatile &v);
int64 AtomicIncrement64(int64 volatile &v);
int32 AtomicDecrement32(int32 volatile &v);
int64 AtomicDecrement64(int64 volatile &v);

bool Sleep(uint32 ms);

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

bool WaitForThreadToFinish(ThreadHandle hThread);

bool TerminateThread(ThreadHandle hThread);

bool PauseThread(ThreadHandle hThread);

bool ContinueThread(ThreadHandle hThread);

bool GetCurrentThreadUniqueID(uint32 &tid);

bool GetCurrentThreadOSID(uint32 &tid);

bool GetCurrentThread(ThreadHandle& thread);

bool IsThreadRunning(ThreadHandle hThread);

uint32 GetThreadStatColAbility();

#if defined	WINDOWS
	#define FileTimeToUint64(ft) (((uint64)(ft.dwHighDateTime))<<32)|((uint64)ft.dwLowDateTime)
#endif

// For Windows only until pthreads supports getrusage from other threads
bool GetCPUTicks(ThreadHandle hThread, uint64& ticks);

bool GetCPUTicks(uint64& ticks);

bool GetProcessCPUTicks(uint64& ticks);

bool GetProcessCPUTicks(uint32 osProcID, uint64& ticks);

bool GetProcAndOSCPUUsage(double& procPercentUse, double& osPercentUse, uint64 &prevOSTicks, uint64 &prevOSIdleTicks, uint64 &prevProcTicks);

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

bool GetOSCPUUsage(double& percentUse);

bool GetThreadPriority(ThreadHandle hThread, uint16 priority);

bool SetThreadPriority(ThreadHandle hThread, uint16 priority);

int ToOSPriority(int pri);

int FromOSPriority(int pri);

bool SignalThread(ThreadHandle hThread, int32 signal);

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

bool GetDesktopSize(uint32& width, uint32& height);

bool RenameConsoleWindow(const char* name, bool prepend = false);

bool MoveConsoleWindow(int32 x = -1, int32 y = -1, int32 w = -1, int32 h = -1);

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

#define PROC_ERROR		0
#define PROC_TERMINATED	1
#define PROC_NOTSTARTED	2
#define PROC_RUNNING	3
#define PROC_TIMEOUT	4

#define NUM_PIPES          2
#define PARENT_WRITE_PIPE  0
#define PARENT_READ_PIPE   1
/* always in a pipe[], pipe[0] is for read and 
	pipe[1] is for write */
#define READ_FD  0
#define WRITE_FD 1
#define PARENT_READ_FD  ( (*pipes)[PARENT_READ_PIPE][READ_FD]   )
#define PARENT_WRITE_FD ( (*pipes)[PARENT_WRITE_PIPE][WRITE_FD] )
#define CHILD_READ_FD   ( (*pipes)[PARENT_WRITE_PIPE][READ_FD]  )
#define CHILD_WRITE_FD  ( (*pipes)[PARENT_READ_PIPE][WRITE_FD]  )

#define PROCBUFSIZE 4096

int RunOSCommand(const char* cmdline, const char* initdir, uint32 timeout, std::string& stdoutString, std::string& stderrString);
uint32 NewProcess(const char* cmdline, const char* initdir = NULL, const char* title = NULL,
				  int16 x = -1, int16 y = -1, int16 w = -1, int16 h = -1);
uint8 GetProcessStatus(uint32 proc, int &returncode);
bool EndProcess(uint32 proc);
uint8 WaitForProcess(uint32 proc, uint32 timeout, int &returncode);
uint32 GetLocalProcessID();

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

#ifdef WINDOWS
	#define DLLHandle HINSTANCE
#else // WINDOWS
	#define DLLHandle void*
#endif // WINDOWS

typedef int (* LibraryFunction)();

class CommandLineInfo;
class CommandLineInfo {
public:
	static CommandLineInfo* CommandLineInfoSingleton;
	CommandLineInfo() {}
	std::string CommandLineString;
	std::string CommandLineExec;
	std::string CommandLineExecOnly;
	std::string CommandLineExecPath;
	std::vector<std::string> CommandLineItems;
	std::map<std::string, std::string> CommandLineArgs;
};



bool SetCommandLine(int argc, char* argv[]);
std::string GetCommandLine();
std::string GetCommandLinePath();
std::string GetCommandLineExecutable();
std::string GetCommandLineExecutableOnly();
uint32 GetCommandLineArgCount();
std::string GetCommandLineArg(uint16 n);
std::string GetCommandLineArg(const char* key);

class Library {
public:
	static std::string patchLibraryFilename(const char* filename, const char* path = NULL);

	Library();
	~Library();
	bool load(const char* filename);
	LibraryFunction getFunction(const char* funcName);
private:
	DLLHandle handle;
};

Library* OpenLibrary(const char* libName);

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

const char* GetComputerName();

uint64 GetProcessMemoryUsage();
uint64 GetPeakProcessMemoryUsage();

uint16 GetCPUCount();

uint16 GetCPUArchitecture();

uint64 GetCPUSpeed();

uint64 GetSystemMemorySize();

bool GetSystemMemoryUsage(uint64 &totalRAM, uint64 &freeRAM);

const char* GetSystemArchitecture();

const char* GetSystemOSName();

bool GetSystemOSVersion(uint16& major, uint16& minor, uint16& build, char* text, uint16 textSize);

//bool RunOSTextCommand(const char* cmd, char* result, uint32 size);

//extern char OSLocalHostName[1024];
//extern char OSArchitectureName[1024];
//extern char OSName[1024];


//////////////////////////////////////////////////////////////
//                         Sockets                          //
//////////////////////////////////////////////////////////////

#ifdef WINDOWS
	bool CheckNetworkInit();
	#define CHECKNETWORKINIT utils::CheckNetworkInit();
#else
	#define CHECKNETWORKINIT
	#define WSAEWOULDBLOCK EAGAIN
#endif

bool GetSocketError(int wsaError, char* errorString, uint16 errorStringMaxSize, bool* isRecoverable);

int GetLastOSErrorNumber();
std::string GetLastOSErrorMessage();

bool WaitForSocketReadability(SOCKET s, int32 timeout);
bool WaitForSocketWriteability(SOCKET s, int32 timeout);

bool SetSocketNonBlockingMode(SOCKET s);
bool SetSocketBlockingMode(SOCKET s);

struct NetworkInterfaces {
	uint32 address;
	uint64 mac;
	char name[MAXKEYNAMELEN+1];
	char friendlyName[MAXKEYNAMELEN+1];
};

bool LookupIPAddress(const char* name, uint32& address);
bool LookupHostname(uint32 address, char* name, uint32 maxSize);
std::string GetLocalHostnameString();
bool GetLocalHostname(char* name, uint32 maxSize);
bool IsLocalIPAddress(const char* addr);
bool IsLocalIPAddress(uint32& address);
bool GetLocalIPAddress(uint32& address);
uint32* GetLocalIPAddresses(uint32& count);
bool GetLocalMACAddress(uint64& address);
uint64* GetLocalMACAddresses(uint32& count);
NetworkInterfaces* GetLocalInterfaces(uint32& count);
bool GetNextAvailableLocalPort(uint16 lastPort, uint16 &nextPort);

#define LOCALHOSTIP 16777343 // 127.0.0.1 as 32-bit dec
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))

#define GETIPPORT(a) *(uint16*)((char*)&a + sizeof(uint32))
#define GETIPADDRESS(a) *(uint32*)&a
#define GETIPADDRESSPORT(a,p) (((uint32)a)) | (((uint64)p)<<32)
#define GETIPADDRESSQUAD(a) ((uint8*)&a)[0], ((uint8*)&a)[1], ((uint8*)&a)[2], ((uint8*)&a)[3]
#define GETIPADDRESSQUADPORT(a) ((uint8*)&a)[0], ((uint8*)&a)[1], ((uint8*)&a)[2], ((uint8*)&a)[3], ((uint16*)&a)[2]

#ifdef WINDOWS
	std::string GetRegistryKeyInfo(const char* key, HKEY &hKeyRoot);
	std::string GetRegistryParentKeyInfo(const char* key, HKEY &hKeyRoot);
#endif // WINDOWS
uint32 ReadRegistryDWORD(const char* key, const char* entry);
uint64 ReadRegistryQWORD(const char* key, const char* entry);
std::string ReadRegistryString(const char* key, const char* entry);
bool WriteRegistryDWORD(const char* key, const char* entry, uint32 value);
bool WriteRegistryQWORD(const char* key, const char* entry, uint64 value);
bool WriteRegistryString(const char* key, const char* entry, const char* value);
bool DeleteRegistryEntry(const char* key, const char* entry);
bool DeleteRegistryKey(const char* key);
bool DeleteRegistryTree(const char* root, const char* key);
//bool CopyRegistryKey(const char* key, const char* newName);
bool CopyRegistryTree(const char* entry, const char* newName);
//bool RenameRegistryKey(const char* key, const char* newName);
//bool RenameRegistryTree(const char* entry, const char* newName);



bool UtilsTest();
void PrintBinary(void* p, uint32 size, bool asInt, const char* title);
const char* stristr(const char *str, const char *substr, uint32 len = 0);
bool GetNextLineEnd(const char *str, uint32 size, uint32& len, uint32& crSize);
uint32 TextReplaceCharsInPlace(char* str, uint32 size, char find, char replace);
bool TextEndsWith(const char* str, const char* end, bool caseSensitive = true);
bool TextStartsWith(const char* str, const char* start, bool caseSensitive = true);
uint32 strcpyavail(char* dst, const char* src, uint32 maxlen, bool copyAvailable);
const char* laststrstr(const char* str1, const char* str2);
const char* laststristr(const char* str1, const char* str2);

uint32 StringSingleReplace(std::string& text, std::string key, std::string value, bool onlyFirst);
uint32 StringMultiReplace(std::string& text, std::map<std::string, std::string>& map, bool onlyFirst);
uint32 StringScriptReplace(std::string& text, std::string name, std::list< std::map<std::string, std::string> >& list);

std::string TextCapitalise(const char* text);
std::string TextUppercase(const char* text);
std::string TextLowercase(const char* text);
std::string TextIndent(const char* text, const char* indent);
std::string TextUnindent(const char* text);
std::string TextTrimQuotes(const char* text);
std::string TextTrim(const char* text);
std::multimap<std::string, std::string> TextMultiMapSplit(const char* text, const char* outersplit, const char* innersplit);
std::map<std::string, std::string> TextMapSplit(const char* text, const char* outersplit, const char* innersplit);
std::string TextJoin(std::vector<std::string> &list, const char* split, uint32 start = 0, uint32 count = 0);
std::string TextJoin(std::list<std::string> &list, const char* split, uint32 start = 0, uint32 count = 0);
std::string TextJoin(std::map<std::string, std::string> &map, const char* innersplit, const char* outersplit, bool keyquotes, bool valquotes);
std::string TextJoinJSON(std::map<std::string, std::string> &map);
std::string TextJoinXML(std::map<std::string, std::string> &map, const char* innernodeName, const char* outernodeName);
std::vector<std::string> TextListSplit(const char* text, const char* split, bool keepEmpty = true, bool autoTrim = false);
std::vector<std::string> TextListSplitLines(const char* text, bool keepEmpty = true, bool autoTrim = false);
std::vector<std::string> TextListBreakLines(const char* text, uint32 maxLineLength = 80);
std::string TextListBreakLines(const char* text, uint32 maxLineLength, const char* breakstr);
std::string TextVectorConcat(std::vector<std::string>, const char* sep, bool allowEmpty = true);
std::vector<std::string> TextCommandlineSplit(const char* cmdline);
char** SplitCommandline(const char* cmdline, int& argc);
wchar_t** SplitCommandlineW(const char* cmdline, int& argc);
bool DeleteCommandline(char** argv, int argc);

std::string StringFormat(const char *format, ...);
char* StringFormat(uint32& size, const char *format, ...);
bool StringFormatInto(char* dst, uint32 maxsize, const char *format, ...);
char* StringFormatVA(uint32& size, const char *format, va_list args);

unsigned char Hex2Char(const char* str);
unsigned char Dec2Char(const char* str);
int CRC32(const char *addr, uint32 length, int32 crc = 0);

struct FileDetails {
	bool doesExist;
	bool isDirectory;
	bool isReadable;
	bool isWritable;
	bool isExecutable;

	uint32 size;
	uint64 lastAccessTime;
	uint64 lastModifyTime;
	uint64 creationTime;
};

#define NOSUCHFILE		0
#define STANDARDFILE	1
#define STANDARDDIR		2

std::string ReadAFileString(std::string filename);
char* ReadAFile(const char* filename, uint32& length, bool binary = false);
char* ReadAFile(const char* dir, const char* filename, uint32& length, bool binary = false);
bool WriteAFile(const char* filename, const char* data, uint32 length, bool binary = false);
bool WriteAFile(const char* dir, const char* filename, const char* data, uint32 length, bool binary = false);
bool AppendToAFile(const char* filename, const char* data, uint32 length, bool binary = false);
bool AppendToAFile(const char* dir, const char* filename, const char* data, uint32 length, bool binary = false);
char* GetFileList(const char* dirname, const char* ext, uint32& count, bool fullpath, uint32 maxNameLen = 256);
FileDetails GetFileDetails(const char* filename);
std::string GetFilePath(const char* filename);
const char* GetFileBasename(const char* filename);
bool DeleteAFile(const char* filename, bool force);
bool DeleteAFile(const char* dir, const char* filename, bool force);
bool DeleteADir(const char* dirname, bool force);
bool CreateADir(const char* dirname);
bool DoesADirExist(const char* dirname);
bool DoesAFileExist(const char* filename);
bool DeleteFilesInADir(const char* dirname, bool force);
bool ChangeAFileAttr(const char* filename, bool read, bool write);
bool MoveAFile(const char* oldfilename, const char* newfilename, bool force = false);
bool MoveAFile(const char* olddir, const char* oldfilename, const char* newdir, const char* newfilename, bool force = false);
bool CopyAFile(const char* oldfilename, const char* newfilename, bool force = false);
bool CopyAFile(const char* olddir, const char* oldfilename, const char* newdir, const char* newfilename, bool force = false);

char* Int2Ascii(int64 value, char* result, uint16 size, uint8 base);
char* Uint2Ascii(uint64 value, char* result, uint16 size, uint8 base);
unsigned char* Ascii2UTF16LE(const char* ascii, uint32 len, uint32& size);

bool IsTextNumeric(const char* ascii, uint32 start = 0, uint32 end = 0);
int64 Ascii2Int64(const char* ascii, uint32 start = 0, uint32 end = 0);
uint64 Ascii2Uint64(const char* ascii, uint32 start = 0, uint32 end = 0);
uint64 AsciiHex2Uint64(const char* ascii, uint32 start = 0, uint32 end = 0);
int32 Ascii2Int32(const char* ascii, uint32 start = 0, uint32 end = 0);
uint32 Ascii2Uint32(const char* ascii, uint32 start = 0, uint32 end = 0);
uint32 AsciiHex2Uint32(const char* ascii, uint32 start = 0, uint32 end = 0);
float64 Ascii2Float64(const char* ascii, uint32 start = 0, uint32 end = 0);

char* TextSubstringCopy(const char* ascii, uint32 start, uint32 end);

std::string DecodeJSON(std::string str);
std::string EncodeJSON(std::string str);

bool SeedRandomValues(uint32 seedvalue = 0);
double RandomValue();
double RandomValue(double to);
double RandomValue(double from, double to);
double RandomValue(double from, double to, double interval);
int64 RandomInt(int64 from = 0, int64 to = 100);

//std::string BytifySize(int32 val);
std::string BytifySize(double val);
//std::string BytifySizes(int32 val1, int32 val2);
std::string BytifySizes(double val1, double val2);
//std::string BytifyRate(int32 val);
std::string BytifyRate(double val);
//std::string BytifyRates(int32 val1, int32 val2);
std::string BytifyRates(double val1, double val2);

} // namespace utils
} // namespace cmlabs

#endif //_UTILS_H_

