/**
 * file_utils.c - file utilities for SciteProj
 *
 *  Copyright 2009-2017 Andreas Rönnquist
 *
 * This file is part of SciteProj.
 *
 * SciteProj 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 3 of the License, or
 * (at your option) any later version.
 *
 * SciteProj 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 SciteProj.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <stdio.h>
#include <string.h>
#include <glib.h>
#include <glib/gi18n.h>

#include <unistd.h>
#include <stdlib.h>

#include <locale.h>

#include <errno.h>

#include "string_utils.h"
#include "file_utils.h"
#include "load_folder.h"
#include "sort.h"


/**
 *
 */
#define APP_SCITEPROJ_ERROR g_quark_from_static_string("APP_STRINGUTILS_ERROR")

/**
 *
 */
gchar *current_directory = 0;


/**
 *
 */
gboolean is_separator(gchar ch)
{
	gboolean result = FALSE;

	if ((ch == '/') || (ch == G_DIR_SEPARATOR) || (ch == '\\')) {
		result = TRUE;
	}
	return result;
}


/**
 *
 */
gchar *path_up_string(gchar *instring)
{
	int len = (int)strlen(instring);
	int co;

	int start = len--;

	if (is_separator(instring[start])) {
		start--;
	}

	int res = -1;
	for (co = start; co > 0; co--) {
		if (is_separator(instring[co])) {
			if (res == -1) res = co;
		}
	}

	gchar *resstring = g_strdup_printf("%s", instring);

	if (res != -1) {
		resstring[res] = '\0';
	}

	return resstring;
}


/**
 *		fix_separators
 *
 *		Replaces \\ and \ and // with /, and replaces stuff like /./ with just one separator
 */
gchar *fix_separators(gchar *source)
{
	gchar *result = g_strdup(source);
	gchar *pointer = result;

	if (pointer) {
		int co = 0;
		for (co = 0; co < strlen(source); co++) {
			if (is_separator(source[co])) {
				// if ((*pointer == G_DIR_SEPARATOR) || (*pointer == '\\') || (*pointer == '/')) {

				*pointer = '/';

				// skip /./ and similar
				if ((source[co + 1] == '.') && (is_separator(source[co + 2]))) {
					co += 2;
				}


			} else {
				*pointer = source[co];
			}
			pointer++;

		}

		*pointer = '\0';
	}

	return result;
}


/**
 *
 */
gchar *fix_path(char *base_dir, char *temp)
{
	// first, make slashes real ones for the platform.
	gchar *curr = g_strdup(base_dir);

	gchar *infile = fix_separators(temp);

	//gchar *pointer = infile;


	gchar *out_path = g_strdup_printf("%s",curr);
	gchar *current_path = 0;


	if (!g_path_is_absolute(infile)) {
		int co;
		for (co = 0; co < (int)strlen(infile); co++) {
			char ch = infile[co];

			if (!is_separator(ch)) {

				if (current_path != NULL) {
					gchar *buf = g_strdup_printf("%s%c", current_path, ch);

					g_free(current_path);

					current_path = buf;

				} else {
					current_path = g_strdup_printf("%c", ch);
				}

			} else {
				//g_print("%s\n",current_path);

				if (strcmp(current_path, "..") == 0) {
					//g_print("Up! curr:%s\n",out_path);

					//file_pointer = &infile[co];

					gchar *buf = path_up_string(out_path);

					g_free(out_path);
					out_path = buf;

					g_free(current_path);
					current_path = 0;

				}
			}
		}
	} else {
		infile++;
	}

	gchar *tempfile = infile;

	do {

		if ((tempfile[0] == '.') && (tempfile[1] == G_DIR_SEPARATOR)) {
			tempfile+=2;
		}

	} while((tempfile[0] == '.') && (tempfile[0] == G_DIR_SEPARATOR));

	if (current_path != 0) g_free(current_path);

	//gchar *new_res = g_strdup_printf("%s%c%s",out_path,G_DIR_SEPARATOR,get_filename_from_full_path(file_pointer));
	gchar *new_res = g_strdup_printf("%s%c%s", out_path, G_DIR_SEPARATOR, tempfile);

	if (infile != 0) g_free(infile);

	g_free(curr);
	//g_free(infile);

	return new_res;
}


/**
 *
 */
void init_file_utils()
{
	gchar *temp = g_get_current_dir();
	current_directory = fix_separators(temp);

	g_free(temp);

	if (current_directory[strlen(current_directory) - 1] != G_DIR_SEPARATOR) {

	}
}


/**
 *	get_filename_from_full_path
 */
gchar *get_filename_from_full_path(gchar *src)
{
	gchar *pointer;
	gchar *result = NULL;
	gboolean slashFound = FALSE;

	g_assert(src != NULL);

	pointer = src;

	do {
		if ((*pointer == G_DIR_SEPARATOR) || (*pointer == '\\') || (*pointer == '/')) {

			// point to the character after the slash
			result = ++pointer;
			slashFound = TRUE;
		}

		pointer++;

	} while((*pointer) != '\0');

	if (!slashFound) result = src;

	return result;
}


/**
 * Convert an absolute file path to a relative file path.
 *
 * @return TRUE on success, FALSE on failure (further details returned in err)
 *
 * @param absPath is the absolute file path
 * @param relativePath returns the relative file path (must be later freed with g_free() )
 * @param basePath is the base file path against which the relative path is determined; pass NULL if the current working directory should be used as the base path
 * @param err returns any errors
 */
gboolean abs_path_to_relative_path(const gchar *absPath, gchar **relativePath, const gchar *basePath, GError **err)
{
	g_assert(absPath != NULL);
	g_assert(relativePath != NULL);

	gboolean finalResult = FALSE;
	gchar *localBasePath = NULL;
	char *workingDir = NULL;
	int localBasePathLength = 0;
	int absPathLength = strlen(absPath);
	int i = 0;
	int dirSlashOffset = 0;


	if (!g_path_is_absolute(absPath)) {
		g_set_error(err, APP_SCITEPROJ_ERROR, -1, "%s: Specified path, '%s', is not absolute", __func__ , absPath);

		goto EXITPOINT;
	}

	// If no basepath specified, use current working dir

	if (!basePath) {
		workingDir = getcwd(NULL, 0);

		if (!workingDir) {
			g_set_error(err, APP_SCITEPROJ_ERROR, -1, "%s: Could not determine current working directory, getcwd() = NULL, errno = %d", __func__ , errno);

			goto EXITPOINT;
		}

		basePath = workingDir;

	}

	// Set up a local copy of the base path, and ensure it ends with a '/'

	if (!str_append(&localBasePath, basePath, err)) {
		goto EXITPOINT;
	}

	localBasePathLength = strlen(localBasePath);

	if (localBasePathLength <= 0 || localBasePath[localBasePathLength - 1] != G_DIR_SEPARATOR) {
		if (!str_append(&localBasePath, G_DIR_SEPARATOR_S, err)) {
			goto EXITPOINT;
		}

		++localBasePathLength;
	}


	// Start relative path with local dir

	if (!str_append(relativePath, "./", err)) {
		goto EXITPOINT;
	}

	// Skip over matching parent dirs

	while (i < localBasePathLength && i < absPathLength && localBasePath[i] == absPath[i]) {
		if (absPath[i] == G_DIR_SEPARATOR) {
			dirSlashOffset = i + 1;
		}

		++i;
	}

	// Step up one dir for every dir in the leftover part of the base path

	for (i = dirSlashOffset; i < localBasePathLength; ++i) {
		if (localBasePath[i] == G_DIR_SEPARATOR) {
			if (!str_append(relativePath, "../", err)) {
				goto EXITPOINT;
			}
		}
	}

	// Finally, add the leftover part of the absolute path

	if (!str_append(relativePath, absPath + dirSlashOffset, err)) {
		goto EXITPOINT;
	}

	finalResult = TRUE;


EXITPOINT:

	free(workingDir);
	if (localBasePath) g_free(localBasePath);

	return finalResult;
}


/**
 * Convert a relative file path to an absolute file path.
 *
 * @return TRUE on success, FALSE on failure (further details returned in err)
 *
 * @param relativePath is the relative file path
 * @param absPath returns the absolute file path (must be later freed with g_free() )
 * @param basePath is the base file path used to formulate the absolute path; pass NULL if the current working directory should be used as the base path
 * @param err returns any errors
 */
gboolean relative_path_to_abs_path(gchar *relativePath, gchar **absPath, const gchar *basePath, GError **err)
{
	g_assert(absPath != NULL);
	g_assert(relativePath != NULL);

	int i=0;
	gboolean finalResult = FALSE;
	char *workingDir = NULL;
	gchar *localAbsPath = NULL;
	int absPathLength;
	int co;

	for (co = 0; co < strlen(relativePath); co++) {
		if ((relativePath[co] == '/') || (relativePath[co] == '\\')) {
			relativePath[co] = G_DIR_SEPARATOR;
		}
	}

	// On GNU/Linux check if first character is '/', and on windows check for something
	// like "C:" in the beginning of the string
	if (relativePath[0] == G_DIR_SEPARATOR) {
		g_set_error(err, APP_SCITEPROJ_ERROR, -1, "%s: Specified path, '%s', is absolute", __func__ , relativePath);
		g_print("Separator at birth.\n");
		goto EXITPOINT;
	}


	// If no basepath specified, use current working dir

	if (!basePath) {
		debug_printf("No basepath.\n");

		basePath = g_get_current_dir ();
	}

	// Absolute path is base path + '/' + relative path

	if (!str_append(&localAbsPath, basePath, err)) {
		goto EXITPOINT;
	}

	if (!str_append(&localAbsPath, G_DIR_SEPARATOR_S, err)) {
		goto EXITPOINT;
	}

	//// Only do this if the relativepath REALLY is a realative path.
	if (!str_append(&localAbsPath, relativePath, err)) {
		goto EXITPOINT;
	}

	// Now go through and collapse elements like  "/./" and "/foo/../" and "//"

	absPathLength = strlen(localAbsPath);

	for (i = 0; i < absPathLength; ) {
		gchar *tail = NULL;
		int tailLength;

		if (g_str_has_prefix(localAbsPath + i, "/./")) {
			tail = localAbsPath + i + 2;
			tailLength = absPathLength - i - 1;
		}
		else if (g_str_has_prefix(localAbsPath + i, "\\./")) {
			tail = localAbsPath + i + 2;
			tailLength = absPathLength - i - 1;
		}
		else if (g_str_has_prefix(localAbsPath + i, "\\.\\")) {
			tail = localAbsPath + i + 2;
			tailLength = absPathLength - i - 1;
		}
		else if (g_str_has_prefix(localAbsPath + i, "//")) {
			tail = localAbsPath + i + 1;
			tailLength = absPathLength - i;
		}
		else if (
		    (g_str_has_prefix(localAbsPath + i, "/../")) ||
		    (g_str_has_prefix(localAbsPath + i, "\\..\\"))
		) {
			tail = localAbsPath + i + 3;
			tailLength = absPathLength - i - 2;

			do {
				i = (i > 0) ? i - 1 : 0;
			} while (i > 0 && !is_separator(localAbsPath[i])/* != G_DIR_SEPARATOR*/);
		}

		if (tail != NULL) {
			memmove(localAbsPath + i, tail, tailLength);

			absPathLength -= (tail - localAbsPath - i);
		}
		else {
			++i;
		}
	}

	*absPath = localAbsPath;

	localAbsPath = NULL;

	finalResult = TRUE;

EXITPOINT:

	if (workingDir) free(workingDir);
	if (localAbsPath) g_free(localAbsPath);

	return finalResult;
}


/**
 *
 */
gchar *get_file_extension(gchar *filename)
{
	gchar *result = g_strrstr(filename, ".");

	if (!result) result = "";

	return result;
}


/**
 *
 */
int get_number_of_files_in_folder(gchar *folder_name)
{
	int result = 0;

	GSList *file_list;
	GSList *folder_list;

	file_list = load_folder_to_list(folder_name, FALSE, file_sort_by_extension_bigger_func, NULL);
	folder_list = load_folder_to_list(folder_name, TRUE, compare_strings_bigger, NULL);

	if (file_list) {
		while (file_list)	{
			result++;
			file_list = file_list->next;
		};
	}

	if (folder_list) {
		while (folder_list)	{
			result++;
			folder_list = folder_list->next;
		};
	}

	g_slist_foreach(file_list, (GFunc)g_free, NULL);
	g_slist_free(file_list);

	g_slist_foreach(folder_list, (GFunc)g_free, NULL);
	g_slist_free(folder_list);

	return result;
}


/**
 * is_string_folder
 * @return gboolean TRUE if the entered string represent a folder in the
 *         systems folder structure.
 */
gboolean is_string_folder(gchar *instring)
{
	gboolean result = FALSE;

	GDir *dir = g_dir_open(instring, 0, NULL);

	if (dir) {
		result = TRUE;
		g_dir_close(dir);
	}

	return result;
}


/**
 *
 */
gchar *clean_folder(gchar *infolder)
{
	gchar *result = NULL;

	if (g_strcmp0(infolder, "./") == 0) {
		result = g_strdup_printf(".");

		return result;
	}

	if (infolder) {

		gchar *temp = infolder;

		int len = strlen(temp);

		if ((temp[0] == '.') && (temp[1] == G_DIR_SEPARATOR)) {

			temp += 2;
			len -= 2;
		}

		if (temp[len - 1] == G_DIR_SEPARATOR) {
			result = g_strndup(temp, len - 1);
		} else {
			result = g_strdup(temp);
		}
	}

	return result;
}
