/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include <errno.h>
#include <string>
#include <sstream>
#include <cstring>

#ifdef _WIN32
#  include <windows.h>
#  include <io.h>
#elif defined(__APPLE__)
#  include <mach-o/dyld.h>
#  include <unistd.h>
#else
#  include <unistd.h>
#endif

#ifdef __FreeBSD__
#  include <sys/sysctl.h>
#endif

#include <limits.h>

#if defined(__linux__) || defined(__CYGWIN__)
std::string proc_self_dirname()
{
	char path[PATH_MAX];
	ssize_t buflen = readlink("/proc/self/exe", path, sizeof(path));
	if (buflen < 0) {
		fprintf(stderr, "fatal error: readlink(\"/proc/self/exe\") failed: %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}
	while (buflen > 0 && path[buflen-1] != '/')
		buflen--;
	return std::string(path, buflen);
}
#elif defined(__FreeBSD__)
std::string proc_self_dirname()
{
	int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
	size_t buflen;
	char *buffer;
	std::string path;
	if (sysctl(mib, 4, NULL, &buflen, NULL, 0) != 0) {
		fprintf(stderr, "fatal error: sysctl failed: %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}
	buffer = (char*)malloc(buflen);
	if (buffer == NULL) {
		fprintf(stderr, "fatal error: malloc failed: %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}
	if (sysctl(mib, 4, buffer, &buflen, NULL, 0) != 0) {
		fprintf(stderr, "fatal error: sysctl failed: %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}
	while (buflen > 0 && buffer[buflen-1] != '/')
		buflen--;
	path.assign(buffer, buflen);
	free(buffer);
	return path;
}
#elif defined(__APPLE__)
std::string proc_self_dirname()
{
	char *path = NULL;
	uint32_t buflen = 0;
	while (_NSGetExecutablePath(path, &buflen) != 0)
		path = (char *) realloc((void *) path, buflen);
	while (buflen > 0 && path[buflen-1] != '/')
		buflen--;
	return std::string(path, buflen);
}
#elif defined(_WIN32)
std::string proc_self_dirname()
{
	int i = 0;
#  ifdef __MINGW32__
	char longpath[MAX_PATH + 1];
	char shortpath[MAX_PATH + 1];
#  else
	WCHAR longpath[MAX_PATH + 1];
	TCHAR shortpath[MAX_PATH + 1];
#  endif
	if (!GetModuleFileName(0, longpath, MAX_PATH+1)) {
		fprintf(stderr, "fatal error: GetModuleFileName() failed.\n");
		exit(EXIT_FAILURE);
	}
	if (!GetShortPathName(longpath, shortpath, MAX_PATH+1)) {
		fprintf(stderr, "fatal error: GetShortPathName() failed.\n");
		exit(EXIT_FAILURE);
	}
	while (shortpath[i] != 0)
		i++;
	while (i > 0 && shortpath[i-1] != '/' && shortpath[i-1] != '\\')
		shortpath[--i] = 0;
	std::string path;
	for (i = 0; shortpath[i]; i++)
		path += char(shortpath[i]);
	return path;
}
#elif defined(EMSCRIPTEN)
std::string proc_self_dirname()
{
	return "/";
}
#else
	#error Dont know how to determine process executable base path!
#endif

bool file_test_open(std::string path)
{
	FILE *fdb = fopen(path.c_str(), "r");
	if (fdb == nullptr) {
		return false;
	}
	fclose(fdb);
	return true;
}

extern bool verbose;

std::string find_chipdb(std::string config_device)
{
	if (PREFIX[0] == '~' && PREFIX[1] == '/') {
		std::string homepath;
#ifdef _WIN32
		if (getenv("USERPROFILE") != nullptr) {
			homepath += getenv("USERPROFILE");
		}
		else {
			if (getenv("HOMEDRIVE") != nullptr &&
			    getenv("HOMEPATH") != nullptr) {
				homepath += getenv("HOMEDRIVE");
				homepath += getenv("HOMEPATH");
			}
		}
#else
		homepath += getenv("HOME");
#endif
		homepath += std::string(PREFIX + 1) + "/" CHIPDB_SUBDIR "/chipdb-" + config_device + ".txt";
		if (verbose)
			fprintf(stderr, "Looking for chipdb '%s' at %s\n", config_device.c_str(), homepath.c_str());
		if (file_test_open(homepath))
			return homepath;
	}

	std::string prefixpath = PREFIX "/share/" CHIPDB_SUBDIR "/chipdb-" + config_device + ".txt";
	if (verbose)
		fprintf(stderr, "Looking for chipdb '%s' at %s\n", config_device.c_str(), prefixpath.c_str());
	if (file_test_open(prefixpath))
		return prefixpath;

	std::string relbinarypath = proc_self_dirname() + "../share/" CHIPDB_SUBDIR "/chipdb-" + config_device + ".txt";
	if (verbose)
		fprintf(stderr, "Looking for chipdb '%s' at %s\n", config_device.c_str(), relbinarypath.c_str());
	if (file_test_open(relbinarypath))
		return relbinarypath;

	return "";
}