/*
 *
 *  Singe 2
 *  Copyright (C) 2006-2020 Scott Duensing <scott@kangaroopunch.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */


#ifdef _WIN32
#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

static const int CONSOLE_LINES = 1000;
#endif


#include <stdlib.h>
#include <dirent.h>
#include <string.h>

#include "util.h"


extern bool _confNoConsole;


FILE *utilTraceFile = NULL;


char *utilCreateString(char *format, ...) {
	va_list  args;
	char    *string;

	va_start(args, format);
	string = utilCreateStringVArgs(format, args);
	va_end(args);

	return string;
}


__attribute__((__format__(__printf__, 1, 0)))
char *utilCreateStringVArgs(char *format, va_list args) {
	va_list  argsCopy;
	int32_t  size     = 0;
	char    *buffer   = NULL;

	va_copy(argsCopy, args);
	size = vsnprintf(NULL, 0, format, argsCopy) + 1;
	va_end(argsCopy);
	buffer = calloc(1, (size_t)size);
	if (buffer) {
		vsnprintf(buffer, (size_t)size, format, args);
	}

	return buffer;
}


__attribute__((__format__(__printf__, 1, 0)))
__attribute__((noreturn))
void utilDie(char *fmt, ...) {
	va_list args;
	va_start(args, fmt);
	vfprintf(stderr, fmt, args);
	va_end(args);
	if (!_confNoConsole) {
		printf("\n");
		fflush(stderr);
#ifdef _WIN32
		getchar();
#endif
	}
	exit(1);
}


bool utilFileExists(char *filename) {
	FILE *file;
	if ((file = fopen(filename, "r+"))) {
		fclose(file);
		return true;
	}
	return false;
}


void utilFixPathSeparators(char **path) {

	int32_t  i    =  0;
	char    *temp = *path;

	// Flip path separators to whatever our OS wants
	while (temp[i] != 0) {
		if (temp[i] == '\\' || temp[i] == '/') {
			temp[i] = utilGetPathSeparator();
		}
		i++;
	}

	// Does this string end with a path separator?
	if (temp[strlen(temp) - 1] != utilGetPathSeparator()) {
		// No - append one.
		temp = realloc(temp, sizeof(char) * (strlen(temp) + 1));
		temp[strlen(temp)] = utilGetPathSeparator();
		temp[strlen(temp) + 1] = 0;
		*path = temp;
	}
}


char *utilGetFileExtension(char *filename) {
	char    *start = filename + strlen(filename);
	int32_t  x;

	// Scan through name and find the last '.'
	for (x=0; x<(int32_t)strlen(filename); x++) {
		if (filename[x] == '.') {
			start = &filename[x + 1];
		}
		// Reset if we find a path separator
		if (filename[x] == utilGetPathSeparator()) {
			start = filename + strlen(filename);
		}
	}

	return start;
}


char *utilGetLastPathComponent(char *pathname) {
	char    *start = pathname;
	int32_t  x;

	// Scan through name and find the last path separator
	for (x=0; x<(int32_t)strlen(pathname); x++) {
		if (pathname[x] == utilGetPathSeparator()) {
			start = &pathname[x + 1];
		}
	}

	return start;
}


char utilGetPathSeparator(void) {
#ifdef _WIN32
	return '\\';
#else
	return '/';
#endif
}


bool utilPathExists(char *pathname) {
	DIR *dir = opendir(pathname);
	if (dir) {
		closedir(dir);
		return true;
	}
	return false;
}


char *utilReadFile(char *filename, size_t *bytes) {
	char   *data   =  NULL;
	FILE   *in     =  fopen(filename, "rb");

	*bytes = 0;

	if (in) {
		fseek(in, 0, SEEK_END);
		*bytes = ftell(in);
		fseek(in, 0, SEEK_SET);
		data = malloc(sizeof(char) * (*bytes));
		fread(data, sizeof(char), *bytes, in);
		fclose(in);
	}

	return data;
}


char *utilReadLine(char *haystack, size_t length, char **offset) {
	size_t  bytes  =  0;
	char   *temp   =  *offset;
	char   *tail   =  temp;
	char   *result =  NULL;

	// They didn't know where to start
	if (temp == NULL) {
		temp = haystack;
		tail = temp;
	}

	// Is there still data to read?
	while ((size_t)(tail - haystack) < length) {
		// Is this the end of a line?
		if ((tail[0] == 10) || (tail[0] == 13)) {
			// Yep!
			bytes = tail - temp + 1;
			result = malloc(sizeof(char) * bytes);
			memcpy(result, temp, bytes - 1);
			result[bytes - 1] = 0;
			// Read past any additional CR/LFs
			while ((tail[0] == 10) || (tail[0] == 13)) {
				tail++;
			}
			// Return where we left off
			temp = tail;
			*offset = temp;
			return result;
		}
		// Next character
		tail++;
	}

	// Was there data at the end of the block with no CR/LF?
	if (tail > temp) {
		// Yep.  Treat it as a line.
		bytes = tail - temp + 1;
		result = malloc(sizeof(char) * bytes);
		memcpy(result, temp, bytes - 1);
		result[bytes - 1] = 0;
		temp = tail;
		*offset = temp;
	}

	// Didn't find anything
	return result;
}


void utilRedirectConsole(void) {
#ifdef _WIN32
	// http://dslweb.nwnexus.com/~ast/dload/guicon.htm
	int                         hConHandle;  // Not changing this int.
	intptr_t                    lStdHandle;
	CONSOLE_SCREEN_BUFFER_INFO  coninfo;
	FILE                       *fp;

	// allocate a console for this app
	AllocConsole();

	// set the screen buffer to be big enough to let us scroll text
	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
	coninfo.dwSize.Y = CONSOLE_LINES;
	SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);

	// redirect unbuffered STDOUT to the console
	lStdHandle = (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE);
	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
	fp = _fdopen(hConHandle, "w");
	*stdout = *fp;
	setvbuf(stdout, NULL, _IONBF, 0);

	// redirect unbuffered STDIN to the console
	lStdHandle = (intptr_t)GetStdHandle(STD_INPUT_HANDLE);
	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
	fp = _fdopen(hConHandle, "r");
	*stdin = *fp;
	setvbuf(stdin, NULL, _IONBF, 0);

	// redirect unbuffered STDERR to the console
	lStdHandle = (intptr_t)GetStdHandle(STD_ERROR_HANDLE);
	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
	fp = _fdopen(hConHandle, "w");
	*stderr = *fp;
	setvbuf(stderr, NULL, _IONBF, 0);
#endif
}


__attribute__((__format__(__printf__, 1, 0)))
void utilSay(char *fmt, ...) {
	va_list args;
	va_start(args, fmt);
	vfprintf(stdout, fmt, args);
	va_end(args);
	if (!_confNoConsole) {
		printf("\n");
		fflush(stdout);
	}
}


void utilTrace(char *fmt, ...) {
	va_list args;
	if (utilTraceFile) {
		va_start(args, fmt);
		utilTraceVArgs(fmt, args);
		va_end(args);
	}
}


void utilTraceEnd(void) {
	if (utilTraceFile) fclose(utilTraceFile);
}


void utilTraceStart(char *filename) {
	utilTraceFile = fopen(filename, "wt");
	if (!utilTraceFile) utilDie("Unable to create trace file:  %s", filename);
}


__attribute__((__format__(__printf__, 1, 0)))
void utilTraceVArgs(char *fmt, va_list args) {
	if (utilTraceFile) {
		vfprintf(utilTraceFile, fmt, args);
		fprintf(utilTraceFile, "\n");
		fflush(utilTraceFile);
	}
}
