/*
 * Purple - Internet Messaging Library
 * Copyright (C) Pidgin Developers <devel@pidgin.im>
 *
 * Purple is the legal property of its developers, whose names are too numerous
 * to list here. Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This library 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 library 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 library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifdef G_LOG_DOMAIN
# undef G_LOG_DOMAIN
#endif /* G_LOG_DOMAIN */
#define G_LOG_DOMAIN "PurpleWin32Dep"

#include "purpleconfig.h"

#include "win32/win32dep.h"

#include <windows.h>
#include <winuser.h>

#include <birb.h>

#include "debug.h"

#define MAX_PATH_LEN 2048

static char *app_data_dir = NULL;
static char *bin_dir = NULL;
static char *data_dir = NULL;
static char *lib_dir = NULL;
static char *locale_dir = NULL;
static char *sysconf_dir = NULL;
static char *cert_dir = NULL;

static HINSTANCE libpurpledll_hInstance = NULL;

const char *
wpurple_bin_dir(void) {
	static gboolean initialized = FALSE;

	if(!initialized) {
		char *tmp = NULL;
		wchar_t winstall_dir[MAXPATHLEN];

		/* We might use g_win32_get_package_installation_directory_of_module
		 * here, but we won't because this routine strips bin or lib
		 * part of the path.
		 */
		if(GetModuleFileNameW(libpurpledll_hInstance, winstall_dir,
		                      MAXPATHLEN) > 0)
		{
			tmp = g_utf16_to_utf8(winstall_dir, -1, NULL, NULL, NULL);
		}

		if (tmp == NULL) {
			tmp = g_win32_error_message(GetLastError());
			g_critical("GetModuleFileName error: %s\n", tmp);
			g_free(tmp);
			return NULL;
		} else {
			bin_dir = g_path_get_dirname(tmp);
			g_free(tmp);
			initialized = TRUE;
		}
	}

	return bin_dir;
}

static char *
wpurple_install_relative_path(const char *abspath) {
	GString *bin_esc = NULL;
	char *ret;
	const char *bindir = WIN32_FHS_BINDIR;
	const char *relpath;
	int i = 0;
	int last_dirsep = -1;
	int bin_esc_cnt = 0;

	g_return_val_if_fail(!birb_str_is_empty(bindir), NULL);
	g_return_val_if_fail(!birb_str_is_empty(abspath), NULL);

	/* let's find the common prefix of those paths */
	for(i = 0; bindir[i] == abspath[i]; i++) {
		if(bindir[i] == '\0') {
			break;
		}

		if(bindir[i] == '\\' || bindir[i] == '/') {
			last_dirsep = i;
		}
	}

	if(bindir[i] == '\0' && (abspath[i] == '\\' || abspath[i] == '/')) {
		last_dirsep = i;
	}

	if(abspath[i] == '\0' && (bindir[i] == '\\' || bindir[i] == '/')) {
		last_dirsep = i;
	}

	/* there is no common prefix, return absolute path */
	if(last_dirsep == -1) {
		return g_strdup(abspath);
	}

	/* let's check, how many dirs we need to go up to the common prefix */
	bin_esc_cnt = 0;
	for(i = last_dirsep; bindir[i]; i++) {
		if(bindir[i] != '\\' && bindir[i] != '/') {
			continue;
		}

		if(bindir[i + 1] == '\0') { /* trailing dir separator */
			break;
		}

		bin_esc_cnt++;
	}

	bin_esc = g_string_new("");
	for(i = 0; i < bin_esc_cnt; i++) {
		g_string_append(bin_esc, ".." G_DIR_SEPARATOR_S);
	}

	/* now, we need to go back deeper into the directory tree */
	relpath = &abspath[last_dirsep];
	if(relpath[0] != '\0') {
		relpath++;
	}

	/* - enter bin dir
	 * - escape it to the common prefix
	 * - dive into the abspath dir
	 */
	ret = g_build_filename(wpurple_bin_dir(), bin_esc->str, relpath, NULL);
	g_string_free(bin_esc, TRUE);

	g_info("wpurple_install_relative_path(%s) = %s", abspath, ret);

	return ret;
}

const char *
wpurple_data_dir(void) {
	static gboolean initialized = FALSE;

	if(initialized) {
		return data_dir;
	}

	data_dir = wpurple_install_relative_path(WIN32_FHS_DATADIR);
	initialized = TRUE;

	return data_dir;
}


const char *
wpurple_lib_dir(const char *subdir) {
	static gboolean initialized = FALSE;
	static char subpath[MAX_PATH_LEN];

	if(!initialized) {
		lib_dir = wpurple_install_relative_path(WIN32_FHS_LIBDIR);
		initialized = TRUE;
	}

	if(subdir == NULL) {
		return lib_dir;
	}

	g_snprintf(subpath, sizeof(subpath), "%s" G_DIR_SEPARATOR_S "%s", lib_dir,
	           subdir);

	return subpath;
}

const char *
wpurple_locale_dir(void) {
	static gboolean initialized = FALSE;

	if(!initialized) {
		locale_dir = wpurple_install_relative_path(WIN32_FHS_LOCALEDIR);
		initialized = TRUE;
	}

	return locale_dir;
}

const char *
wpurple_sysconf_dir(void) {
	static gboolean initialized = FALSE;

	if(!initialized) {
		sysconf_dir = wpurple_install_relative_path(WIN32_FHS_SYSCONFDIR);
		initialized = TRUE;
	}

	return sysconf_dir;
}

void
wpurple_init(void) {
	g_message("libpurple version: " DISPLAY_VERSION);
	g_message("Glib: %u.%u.%u", glib_major_version, glib_minor_version,
	          glib_micro_version);
}

void wpurple_cleanup(void) {
	g_message("wpurple_cleanup");

	g_clear_pointer(&app_data_dir, g_free);
	g_clear_pointer(&bin_dir, g_free);
	g_clear_pointer(&data_dir, g_free);
	g_clear_pointer(&lib_dir, g_free);
	g_clear_pointer(&locale_dir, g_free);
	g_clear_pointer(&sysconf_dir, g_free);
	g_clear_pointer(&cert_dir, g_free);

	libpurpledll_hInstance = NULL;
}

/* DLL initializer */
/* suppress gcc "no previous prototype" warning */
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);

BOOL WINAPI
DllMain(HINSTANCE hinstDLL, G_GNUC_UNUSED DWORD fdwReason,
        G_GNUC_UNUSED LPVOID lpvReserved)
{
	libpurpledll_hInstance = hinstDLL;

	return TRUE;
}
