#ifdef _WIN32
#  include <windows.h> // for OutputDebugStringA
#endif
#include "Utilities.h"
#include <stdarg.h>


static ErrorHandler s_defaultErrorHandler;
static Logger s_defaultMessageLogger;
static HeapManager s_defaultHeapManager;
static ResourceDeviceFactory s_defaultDeviceFactory;


Module g_currentModule{ false, &s_defaultErrorHandler, &s_defaultMessageLogger, &s_defaultHeapManager, &s_defaultDeviceFactory };


void yqDebugBreak()
{
#ifdef _WIN32
	DebugBreak();
#endif
}


void yqConsolePrintString(const char* a_string)
{
#ifdef _WIN32
	// Windows code
	OutputDebugStringA(a_string);
#else
	// ANSI C / POSIX porting code
	fprintf(stderr, a_string);
	fprintf(stderr, "\n");
#endif
}


void ErrorHandler::handleError(yqResult a_result, const char *a_location, const char *a_message)
{
	yqConsolePrintString(a_location);
	yqConsolePrintString("Caught an error! ");
	yqConsolePrintString(a_message);
	yqConsolePrintString("\n");
	//yqDebugBreak();
}


void Logger::logMessage(yqLogLevel a_logLevel, const char *a_location, const char *a_message)
{
	if (BT_IsDebug)
	{
        yqConsolePrintString(a_location);
        yqConsolePrintString(a_message);
		yqConsolePrintString("\n");
	}
}


void *HeapManager::malloc(size_t a_size)
{
	return ::malloc(a_size);
}


void *HeapManager::realloc(void *ptr, size_t newSize)
{
	return ::realloc(ptr, newSize);
}


void HeapManager::free(void *ptr)
{
	::free(ptr);
}


yqResult ResourceDeviceFactory::CreateResourceDevice(const char * resourceID, uint32_t writerFlags, ResourceDevice **newDevice)
{
	// TODO:
	yqDebugBreak();
	YQ_API_LEAVE(R_Failure, "Unimplemented");
}


void yqLogMessage(yqLogLevel a_logLevel, const char *a_location, const char* a_func, const char *a_format, ...)
{
	va_list args;
	va_start(args, a_format);
    std::string loc = a_location;
    loc += a_func;
#ifdef _WIN32
	size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1);
	char* message = (char*)_alloca(sizeRequired);
	_vsnprintf(message, sizeRequired, a_format, args);
	if (g_currentModule.m_messageLogger)
        g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message);
	_freea(message);
#else
	char* message = 0;
	vasprintf(&message, a_format, args);
	if (g_currentModule.m_messageLogger)
        g_currentModule.m_messageLogger->logMessage(a_logLevel, loc.c_str(), message);
	free(message);
#endif
	va_end(args);
}


void yqHandleError(yqResult a_result, const char *a_location, const char* a_func, const char *a_format, ...)
{
	va_list args;
	va_start(args, a_format);
    std::string loc = a_location;
    loc += a_func;
#ifdef _WIN32
	size_t sizeRequired = size_t(_vscprintf(a_format, args) + 1);
	char* message = (char*)_alloca(sizeRequired);
	_vsnprintf(message, sizeRequired, a_format, args);
	if (g_currentModule.m_errorHandler)
        g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message);
	_freea(message);
#else
	char* message = 0;
	vasprintf(&message, a_format, args);
	if (g_currentModule.m_errorHandler)
        g_currentModule.m_errorHandler->handleError(a_result, loc.c_str(), message);
	free(message);
#endif
	va_end(args);
}


void* yqAllocateMemory(size_t a_newSize)
{
	return g_currentModule.m_heapManager->malloc(a_newSize);
}


void* yqReallocateMemory(void* a_ptr, size_t a_newSize)
{
	return g_currentModule.m_heapManager->realloc(a_ptr, a_newSize);
}


void yqFreeMemory(void* a_ptr)
{
	return g_currentModule.m_heapManager->free(a_ptr);
}



/*

extern char *StringDeepCopy(const char *string);
extern void StringFree(const char *string);
extern char* CreateFilenameHelper(const char* a_filename, const char* a_parent);

// Make a deep copy of the passed in string parameter, or return NULL if a_srcString is NULL
char *StringDeepCopy(const char *a_srcString)
{
	if (!a_srcString)
		return NULL;
	size_t strLength = strlen(a_srcString) + 1; // Add 1 to hold the nul terminator
	char* newStr = YQ_ALLOC_ARRAY(char, strLength);
	return (newStr == NULL) ? NULL : (char*)memcpy(newStr, a_srcString, strLength);
}
void StringFree(const char *a_string)
{
	if (a_string)
		YQ_FREE(a_string);
}
char* CreateFilenameHelper(const char* a_filename, const char* a_parent)
{
	if (!a_filename)
		return NULL;
	size_t neededLen = strlen(a_filename) + 1;
	if (a_parent)
		neededLen += strlen(a_parent) + 1;
	char* filename = YQ_ALLOC_ARRAY(char, neededLen);
	if (!filename)
		return NULL;
	if (a_parent)
	{
		strncpy(filename, a_parent, neededLen);
		char* file = strrchr(filename, '\\');
		if (file == NULL)
			file = filename;
		char* ext = strrchr(file, '.');
		if (ext == NULL)
			strncat(filename, "/", neededLen);
		else
			strncpy(ext, "/", neededLen - (ext - filename));
		strncat(filename, a_filename, neededLen);
	}
	else
	{
		strncpy(filename, a_filename, neededLen);
	}
	return filename;
}


*/

