
/*---------------------------------------------------------------------------*/
/* idatabase.cpp                                                             */
/* copyright (c) innovaphone 2022                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include <platform.h>

#include "common/ilib/str.h"
#include "sdk/common/interface/database.h"

/*---------------------------------------------------------------------------*/
/* class IDatabase - static functions                                        */
/*---------------------------------------------------------------------------*/

size_t IDatabase::QueryPrint(IInstanceLog* log, char* buffer, size_t bufferSize, const char* sqlcmd, ...)
{
	va_list argList;
	va_start(argList, sqlcmd);
	size_t res = IDatabase::QueryPrintV(log, buffer, bufferSize, sqlcmd, argList);
	va_end(argList);
	return res;
}


size_t IDatabase::QueryPrint(char* buffer, size_t bufferSize, const char* sqlcmd, ...)
{
	va_list argList;
	va_start(argList, sqlcmd);
	size_t res = IDatabase::QueryPrintV(nullptr, buffer, bufferSize, sqlcmd, argList);
	va_end(argList);
	return res;
}


size_t IDatabase::QueryPrintV(char* buffer, size_t size, const char* sqlcmd, va_list argList)
{
	return IDatabase::QueryPrintV(nullptr, buffer, size, sqlcmd, argList);
}


size_t IDatabase::QueryPrintV(IInstanceLog* log, char* buffer, size_t size, const char* sqlcmd, va_list argList)
{
	size_t bytesCopied = 0;
	size_t maxSize = size - 1; // Remove 1 for the closing zero
	size_t tmp = 0;
	char * s = nullptr;
	bool * b = nullptr;
    int * i = nullptr;
	double * f = nullptr;
	bool countOnly = buffer == nullptr;
	bool done = false;

	//	while (*sqlcmd && (countOnly == false || bytesCopied < maxSize)) {
	while (!done) {
		if (*sqlcmd == '%') {
			++sqlcmd;

			if (countOnly) size = bytesCopied;

			switch (*sqlcmd) {
			case '%':
				tmp = _snprintf(buffer, size - bytesCopied, "%%");
				break;

			case 'i':
			case 'u':
				tmp = _snprintf(buffer, size - bytesCopied, "%i", va_arg(argList, int));
				break;

			case 'f':
				tmp = _snprintf(buffer, size - bytesCopied, "%f", va_arg(argList, double));
				break;

			case 'l':
				if (*(sqlcmd + 1) == 'l' && (*(sqlcmd + 2) == 'i' || *(sqlcmd + 2) == 'u')) {
					sqlcmd += 2;
					tmp = _snprintf(buffer, size - bytesCopied, "%lli", (long long)(va_arg(argList, long64)));
				}
				else {
					if (log != nullptr) log->Log(LOG_DATABASE, "IDatabase::QueryPrintV found unkown format specifier \'l%c%c\'", *(sqlcmd - 1), *sqlcmd);
					ASSERT(false, "PostgreSQLDatabase::QueryPrintV unkown format specifier found");
				}
				break;

			case 'b':
				tmp = _snprintf(buffer, size - bytesCopied, va_arg(argList, int) ? "TRUE" : "FALSE");
				break;

			case 'q':
				tmp = _snprintf(buffer, size - bytesCopied, "%s", va_arg(argList, char *));
				break;

			case 'd':
				++sqlcmd;
				if (*sqlcmd != 'u') {
					if (log != nullptr) log->Log(LOG_DATABASE, "IDatabase::QueryPrintV found unkown format specifier \'%c%c\'", *(sqlcmd - 1), *sqlcmd);
					ASSERT(false, "PostgreSQLDatabase::QueryPrintV unkown format specifier found");
				}
				else {
					byte * ptr = va_arg(argList, byte *);
					size_t bufSize = va_arg(argList, size_t);
					tmp = bufSize * 2 + 4; // = 4 = 2x ' and the leading \x
					if (!countOnly) {
						if (bytesCopied + tmp > maxSize) return bytesCopied;

						char * tmpBuf = (char *)buffer;

						// Because we already checked, if everyting will fit into the buffer, we could use sprintf
						tmpBuf += _sprintf(tmpBuf, "\'\\x");
						str::from_hexmem(ptr, (dword)bufSize, tmpBuf);
						tmpBuf += (bufSize * 2);
						*tmpBuf = '\'';
					}
				}
				break;

			case 'p':
				switch (*++sqlcmd) {
				case 'i':
				case 'u':
					i = va_arg(argList, int *);
					if (!i) tmp = _snprintf(buffer, size - bytesCopied, "NULL");
					else tmp = _snprintf(buffer, size - bytesCopied, "%i", *i);
					break;

				case 'l':
					if (*(sqlcmd + 1) == 'l' && (*(sqlcmd + 2) == 'i' || *(sqlcmd + 2) == 'u')) {
						sqlcmd += 2;
						long64 * i = va_arg(argList, long64 *);
						if (!i) tmp = _snprintf(buffer, size - bytesCopied, "NULL");
						else tmp = _snprintf(buffer, size - bytesCopied, "%lli", (long long)(*i));
					}
					else {
						if (log != nullptr) log->Log(LOG_DATABASE, "IDatabase::QueryPrintV found unkown format specifier pl\'%c%c\'", *(sqlcmd - 1), *sqlcmd);
						ASSERT(false, "PostgreSQLDatabase::QueryPrintV unkown format specifier found");
					}
					break;

				case 'b':
					b = va_arg(argList, bool *);
					if (!b) tmp = _snprintf(buffer, size - bytesCopied, "NULL");
					else tmp = _snprintf(buffer, size - bytesCopied, *b ? "TRUE" : "FALSE");
					break;

				case 'f':
					f = va_arg(argList, double *);
					if (!f) tmp = _snprintf(buffer, size - bytesCopied, "NULL");
					else tmp = _snprintf(buffer, size - bytesCopied, "%f", *f);
					break;

				default:
					if (log != nullptr) log->Log(LOG_DATABASE, "IDatabase::QueryPrintV found unkown format specifier p\'%c\'", *sqlcmd);
					ASSERT(false, "PostgreSQLDatabase::QueryPrintV unkown format specifier found");
					break;
				}
				break;

			case 's':
			case 'S':
			case 'n':
			case 'e':
				s = va_arg(argList, char *);
				if (!s) {
					tmp = _snprintf(buffer, size - bytesCopied, "NULL");
				}
				else {
					bool needQuote = false;
					bool needEscapeSearchLiterals = false;
					char quote = 0;
					tmp = 0; // No need for tmp, because pointers will be incremented now.

					if (*sqlcmd == 'e') {
						needEscapeSearchLiterals = true;
					}
					else if (*sqlcmd != 'n') {
						needQuote = true;
						quote = (*sqlcmd == 's' ? '\'' : '\"');
					}

					// Check if there is space for to \'
					if (needQuote) {
						++bytesCopied;
						if (!countOnly) {
							*buffer++ = quote;
							if (bytesCopied == maxSize) {
								*buffer = 0;
								return maxSize;
							}
						}
					}

					while (*s != '\0') {
						// Need to excape '
						if (*s == '\'') {
							if (!countOnly) *buffer++ = '\'';
							++bytesCopied;
						}
						else if (needEscapeSearchLiterals && (*s == '%' || *s == '_')) {
							if (!countOnly) *buffer++ = '\\';
							++bytesCopied;
						}

						if (!countOnly) {
							if (bytesCopied == maxSize) {
								*buffer = '\0';
								return maxSize;
							}
							*buffer++ = *s;
						}
						++s;
						++bytesCopied;
					}
					if (needQuote) {
						++bytesCopied;
						if (!countOnly) {
							if (bytesCopied == maxSize) {
								*buffer = '\0';
								return maxSize;
							}
							*buffer++ = quote;
						}
					}
				}
				break;

			default:
				if (log != nullptr) log->Log(LOG_DATABASE, "IDatabase::QueryPrintV found unkown format specifier \'%c\'", *sqlcmd);
				ASSERT(false, "PostgreSQLDatabase::QueryPrintV unkown format specifier found");
				break;
			}
    		
			// If tmp is zero, there is nothing to add or the difference had already been added (and tmp
			// had been set to 0 after that).
			if (tmp > 0) {
				if (!countOnly && bytesCopied + tmp >= maxSize) {
					buffer += maxSize - bytesCopied;
					bytesCopied = maxSize;
				}
				else {
					bytesCopied += tmp;
					if (!countOnly) buffer += tmp;
				}
			}

			++sqlcmd;
		}
		else {
			if (!countOnly) *buffer++ = *sqlcmd;
			++sqlcmd;
			++bytesCopied;
		}


		if (!countOnly) {
			if (*sqlcmd == '\0') done = true;
		}
		else {
			if (*sqlcmd == '\0' || bytesCopied >= maxSize) done = true;
		}
	}

	if (!countOnly) *buffer = '\0';
	return bytesCopied;
}
