#ifdef WIN32
#	include <shlobj.h>
#elif defined(MACOSX)
#	include "sys/osx/filesystem.h"
#elif defined(__linux__)
#	include <pwd.h>
#	include <sys/types.h>
#endif

#ifdef WIN32
#	define DELIM "\\"
#else
#	define DELIM "/"
#	include <errno.h>
#	include <unistd.h>
#	include <sys/stat.h>
#endif

#include "core/filesystem.h"
#include "core/config.h"
#include "core/strings.h"
#include <limits.h>

namespace Filesystem {

Path::Path() {
	_path = "";
}
Path::Path(const std::string& path) {
	_path = path;
}
Path::Path(const Path& path) {
	_path = path.GetPath();
}

bool Path::CreateDirectories() const {
	std::string temp = "";
	size_t start;
	size_t end = 0;
	temp.append(_path.substr(0, _path.find_first_not_of(DELIM, end)));

	while ((start = _path.find_first_not_of(DELIM, end)) != std::string::npos) {
		end = _path.find(DELIM, start);
		temp.append(_path.substr(start, end - start));
#ifdef WIN32
		if (!CreateDirectoryW(Strings::ToWstring(temp).c_str(), NULL) && GetLastError() == ERROR_PATH_NOT_FOUND)
			/* ERROR_PATH_NOT_FOUND should NOT happen here */
			return false;
#else
		struct stat st;
		if (stat(temp.c_str(), &st) == -1)
			mkdir(temp.c_str(), 0755);
#endif
		temp.append(DELIM);
	}
	return true;
}

bool Path::Exists() const {
#ifdef WIN32
	std::wstring buf = Strings::ToWstring(_path);
	return GetFileAttributesW(buf.c_str()) != INVALID_FILE_ATTRIBUTES;
#else
	struct stat st;
	return stat(_path.c_str(), &st) == 0;
#endif
}

std::string Path::Basename() const {
	unsigned long long pos = _path.find_last_of(DELIM);
	return pos != std::string::npos ? _path.substr(pos + 1, _path.length()) : "";
}

std::string Path::Stem() const {
	std::string basename = Basename();
	unsigned long long pos = basename.find_last_of(".");
	return pos != std::string::npos ? basename.substr(0, pos) : "";
}

std::string Path::Extension() const {
	std::string basename = Basename();
	unsigned long long pos = basename.find_last_of(".");
	return pos != std::string::npos ? basename.substr(pos + 1, basename.length()) : "";
}

Path Path::GetParent() const {
	return _path.substr(0, _path.find_last_of(DELIM));
}

void Path::SetPath(const std::string& path) {
	_path = path;
}

std::string Path::GetPath() const {
	return _path;
}

Path GetDotPath(void) {
	std::string ret = "";
#ifdef WIN32
	std::wstring buf(MAX_PATH, '\0');
	if (SHGetFolderPathAndSubDirW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, CONFIG_WDIR, &buf.front()) ==
	    S_OK) {
		buf.resize(buf.find('\0'));
		ret += Strings::ToUtf8String(buf);
	}
#elif defined(MACOSX)
	ret += osx::GetApplicationSupportDirectory();
	ret += DELIM CONFIG_DIR;
#else // just assume POSIX
	if (getenv("HOME") != NULL)
		ret += getenv("HOME");
#	ifdef __linux__
	else
		ret += getpwuid(getuid())->pw_dir;
#	endif // __linux__
	if (!ret.empty())
		ret += DELIM ".config" DELIM CONFIG_DIR;
#endif     // !WIN32 && !MACOSX
	return ret;
}

Path GetConfigPath(void) {
	std::string ret = "";
	ret += GetDotPath().GetPath();
	if (!ret.empty())
		ret += DELIM CONFIG_NAME;
	return ret;
}

Path GetPlayersPath(void) {
	std::string ret = "";
	ret += GetDotPath().GetPath();
	if (!ret.empty())
		ret += DELIM "players.json";
	return ret;
}

Path GetExtensionsPath(void) {
	std::string ret = "";
	ret += GetDotPath().GetPath();
	if (!ret.empty())
		ret += DELIM "extensions.json";
	return ret;
}

Path GetAnimeDBPath(void) {
	std::string ret = "";
	ret += GetDotPath().GetPath();
	if (!ret.empty())
		ret += DELIM "anime" DELIM "db.json";
	return ret;
}

} // namespace Filesystem
