#include "StdAfx.h"
#include "fs/NativeFilesystem.h"

#include "fs/FileSource.h"
#include "fs/FileSink.h"
#include "fs/FileMapSource.h"
#include "fs/FileMapSink.h"
#include "fs/NativeUtils.h"
#include "fs/BufferedFileSource.h"
#include "tl/assert.h"
#include <filesystem>
#include "fs/CopyStream.h"


//////////////////////////////////////////////////////////////////////////

namespace fs
{
namespace native_utils
{
#if defined(TL_PLATFORM_WINDOWS_DESKTOP_APP)
int isCapitalizationCorrect(const tl::string& str, HANDLE handle);
#endif
}

//////////////////////////////////////////////////////////////////////////
FS_API NativeFilesystem native(NativeFilesystem::CheckFlag::ValidatePathCase);
FS_API NativeFilesystem nativeRaw;
//////////////////////////////////////////////////////////////////////////

std::filesystem::path toStdPath(AbsPathView path) noexcept
{
	const tl::string str = path.str();
	return { str.data(), str.data() + str.size() };
}
AbsPath toFSAbsPath(const std::filesystem::path& path) noexcept
{
	if (!path.is_absolute())
		return {};
	auto str = path.u8string();
	return {(char*)str.data(), str.size()};
}
void toFSAbsPath(AbsPath& dst, const std::filesystem::path& path) noexcept
{
	if (!path.is_absolute())
		return;
	auto str = path.u8string();
	dst.parse((char*)str.data(), str.size());
}
RelPath toFSRelPath(const std::filesystem::path& path) noexcept
{
	if (!path.is_relative())
		return {};
	auto str = path.u8string();
	return {(char*)str.data(), str.size()};
}

//fix for the fact that the time_time clock doesn't have a from/to_time_t on some compilers
std::time_t to_time_t(std::filesystem::file_time_type tp)
{
	using namespace std::chrono;
#ifdef TL_PLATFORM_WINDOWS_FAMILY
	return system_clock::to_time_t(utc_clock::to_sys(file_clock::to_utc(tp)));
#else
    return system_clock::to_time_t(file_clock::to_sys(tp));
#endif
}
std::filesystem::file_time_type from_time_t(std::time_t time)
{
	using namespace std::chrono;
#ifdef TL_PLATFORM_WINDOWS_FAMILY
	return file_clock::from_utc(utc_clock::from_sys(system_clock::from_time_t(time)));
#else
    return file_clock::from_sys(system_clock::from_time_t(time));
#endif
}

//////////////////////////////////////////////////////////////////////////

#if defined(TL_PLATFORM_WIN_API)
//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
std::string getErrorAsString(int error)
{
	LPSTR messageBuffer = nullptr;
	const size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
		nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, nullptr);

	std::string message(messageBuffer, size);

	//Free the buffer.
	LocalFree(messageBuffer);

	return message;
}

//////////////////////////////////////////////////////////////////////////

std::string getErrorAsString()
{
	return getErrorAsString(GetLastError());
}

//////////////////////////////////////////////////////////////////////////

#elif defined(TL_PLATFORM_POSIX_API)
//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
std::string getErrorAsString(int error)
{
	return std::string(strerror(error));
}
std::string getErrorAsString()
{
	return getErrorAsString(errno);
}

#endif

//////////////////////////////////////////////////////////////////////////

NativeFilesystem::NativeFilesystem(CheckFlags checkFlags)
	: m_checkFlags(checkFlags)
{
}

//////////////////////////////////////////////////////////////////////////

OpenSourceResult NativeFilesystem::openSource(AbsPathView path, SourceFlags flags) const
{
	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFilename(path) == 0)
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	if (flags.test(SourceFlag::Unbuffered))
	{
		tl::unique_ref<FileSource> source = tl::make_unique<FileSource>(path, flags);
		if (source->isValid())
			return std::move(source);

		tl::optional<Error> sourceErrorOpt = source->getLastError();
		if (sourceErrorOpt.has_value())
			return *sourceErrorOpt;
	}
	else
	{
		FileSource source(path, flags);
		if (source.isValid())
			return tl::make_unique<BufferedFileSource>(std::move(source), 32768);

		tl::optional<Error> sourceErrorOpt = source.getLastError();
		if (sourceErrorOpt.has_value())
			return *sourceErrorOpt;
	}

	return tl::make_error<Error>(ErrorCode::SystemError, "Cannot open file '{}': {}", path, getErrorAsString());
}

//////////////////////////////////////////////////////////////////////////

OpenStreamSourceResult NativeFilesystem::openStreamSource(AbsPathView path, SourceFlags flags) const
{
	OUTCOME_TRY(auto source, openSource(path, flags));
	return source;
}

//////////////////////////////////////////////////////////////////////////

OpenMapSourceResult NativeFilesystem::openMapSource(AbsPathView path, MapView mapView, SourceFlags flags) const
{
	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	tl::unique_ref<FileMapSource> source(new FileMapSource(path, mapView.offset, mapView.size));
	if (source->isValid())
		return std::move(source);

	tl::optional<Error> sourceErrorOpt = source->getLastError();
	if (sourceErrorOpt.has_value())
		return *sourceErrorOpt;

	return tl::make_error<Error>(ErrorCode::SystemError, "Cannot open file '{}': {}", path, getErrorAsString());
}

//////////////////////////////////////////////////////////////////////////

OpenSinkResult NativeFilesystem::openSink(AbsPathView path, Mode mode, SinkFlags flags)
{
	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	tl::unique_ref<FileSink> source(new FileSink(path, mode, flags));
	if (source->isValid())
		return std::move(source);

	tl::optional<Error> sourceErrorOpt = source->getLastError();
	if (sourceErrorOpt.has_value())
		return *sourceErrorOpt;

	return tl::make_error<Error>(ErrorCode::SystemError, "Cannot open file '{}': {}", path, getErrorAsString());
}

//////////////////////////////////////////////////////////////////////////

OpenStreamSinkResult NativeFilesystem::openStreamSink(AbsPathView path, Mode mode, SinkFlags flags)
{
	OUTCOME_TRY(auto source, openSink(path, mode, flags));
	return source;
}

//////////////////////////////////////////////////////////////////////////

OpenMapSinkResult NativeFilesystem::openMapSink(AbsPathView path, Mode mode, size_t size, SinkFlags flags)
{
	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	tl::unique_ref<FileMapSink> source(new FileMapSink(path, size, mode, flags));
	if (source->isValid())
		return std::move(source);

	tl::optional<Error> sourceErrorOpt = source->getLastError();
	if (sourceErrorOpt.has_value())
		return *sourceErrorOpt;

	return tl::make_error<Error>(ErrorCode::SystemError, "Cannot open file '{}': {}", path, getErrorAsString());
}

//////////////////////////////////////////////////////////////////////////

ConvertToNativePathResult NativeFilesystem::convertToNativePath(AbsPathView path) const
{
	return path;
}

//////////////////////////////////////////////////////////////////////////

ExistsResult NativeFilesystem::exists(AbsPathView path) const
{
	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFilename(path) == 0)
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	std::error_code ec;
	const bool v = std::filesystem::exists(toStdPath(path), ec);
	if (ec == std::errc::permission_denied)
		return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied: {}", ec.message());
	if (ec)
		return tl::make_error<Error>(ErrorCode::SystemError, "System error: {}", ec.message());

	return v;
}

//////////////////////////////////////////////////////////////////////////

IsFileResult NativeFilesystem::isFile(AbsPathView path) const
{
	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFilename(path) == 0)
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	std::error_code ec;
	const bool v = std::filesystem::is_regular_file(toStdPath(path), ec);
	if (ec == std::errc::permission_denied)
		return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied: {}", ec.message());
	if (ec == std::errc::no_such_file_or_directory)
		return false;
	if (ec)
		return tl::make_error<Error>(ErrorCode::SystemError, "System error: {}", ec.message());

	return v;
}

//////////////////////////////////////////////////////////////////////////

IsFolderResult NativeFilesystem::isFolder(AbsPathView path) const
{
	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFolder(path) == 0)
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	std::error_code ec;
	const bool v = std::filesystem::is_directory(toStdPath(path), ec);
	if (ec == std::errc::permission_denied)
		return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied: {}", ec.message());
	if (ec == std::errc::no_such_file_or_directory)
		return false;
	if (ec)
		return tl::make_error<Error>(ErrorCode::SystemError, "System error: {}", ec.message());

	return v;
}

//////////////////////////////////////////////////////////////////////////

RenameResult NativeFilesystem::rename(AbsPathView path, AbsPathView newPath)
{
	if (!path.is_valid() || !newPath.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFilename(path) == 0)
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	std::error_code ec;
	std::filesystem::rename(toStdPath(path), toStdPath(newPath), ec);
	if (ec == std::errc::no_such_file_or_directory)
		return tl::make_error<Error>(ErrorCode::NotFound, "Not found: {}", ec.message());
	if (ec == std::errc::permission_denied)
		return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied: {}", ec.message());
	if (ec)
		return tl::make_error<Error>(ErrorCode::SystemError, "System error: {}", ec.message());

	return tl::success();
}

//////////////////////////////////////////////////////////////////////////

CopyResult NativeFilesystem::copy(AbsPathView path, AbsPathView newPath)
{
	if (!path.is_valid() || !newPath.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFilename(path) == 0)
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	std::error_code ec;
	const bool v = std::filesystem::copy_file(toStdPath(path), toStdPath(newPath), ec);
	if (ec == std::errc::no_such_file_or_directory)
		return tl::make_error<Error>(ErrorCode::NotFound, "Not found: {}", ec.message());
	if (ec == std::errc::permission_denied)
		return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied: {}", ec.message());
	if (ec)
		return tl::make_error<Error>(ErrorCode::SystemError, "System error: {}", ec.message());
	if (!v)
		return tl::make_error<Error>(ErrorCode::SystemError, "Unknown error");

	return tl::success();
}

//////////////////////////////////////////////////////////////////////////

MakeHardLinkResult NativeFilesystem::makeHardLink(AbsPathView sourcePath, AbsPathView linkPath)
{
	if (!sourcePath.is_valid() || !linkPath.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && 
		(native_utils::validateCaseSensitiveFilename(sourcePath) == 0 || native_utils::validateCaseSensitiveFilename(linkPath) == 0))
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	std::error_code ec;
	std::filesystem::create_hard_link(toStdPath(sourcePath), toStdPath(linkPath), ec);
	if (ec == std::errc::no_such_file_or_directory)
		return tl::make_error<Error>(ErrorCode::NotFound, "Not found: {}", ec.message());
	if (ec == std::errc::permission_denied)
		return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied: {}", ec.message());
	if (ec)
		return tl::make_error<Error>(ErrorCode::SystemError, "System error: {}", ec.message());

	return tl::success();
}

//////////////////////////////////////////////////////////////////////////

MakeSoftLinkResult NativeFilesystem::makeSymLink(AbsPathView sourcePath, AbsPathView linkPath)
{
	if (!sourcePath.is_valid() || !linkPath.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) &&
		(native_utils::validateCaseSensitiveFilename(sourcePath) == 0 || native_utils::validateCaseSensitiveFilename(linkPath) == 0))
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	OUTCOME_TRY(const bool isFolder, isFolder(sourcePath));

	std::error_code ec;
	if (isFolder)
		std::filesystem::create_directory_symlink(toStdPath(sourcePath), toStdPath(linkPath), ec);
	else
		std::filesystem::create_symlink(toStdPath(sourcePath), toStdPath(linkPath), ec);

	if (ec == std::errc::no_such_file_or_directory)
		return tl::make_error<Error>(ErrorCode::NotFound, "Not found: {}", ec.message());
	if (ec == std::errc::permission_denied)
		return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied: {}", ec.message());
	if (ec)
		return tl::make_error<Error>(ErrorCode::SystemError, "System error: {}", ec.message());

	return tl::success();
}

//////////////////////////////////////////////////////////////////////////

MakeFolderResult NativeFilesystem::makeFolder(AbsPathView path)
{
	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	OUTCOME_TRY(const bool isFolder, isFolder(path));
	if (isFolder)
		return tl::success();

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFolder(path) == 0)
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	std::error_code ec;
	std::filesystem::create_directories(toStdPath(path), ec);
	if (ec == std::errc::no_such_file_or_directory)
		return tl::make_error<Error>(ErrorCode::NotFound, "Not found: {}", ec.message());
	if (ec == std::errc::permission_denied)
		return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied: {}", ec.message());
	if (ec)
		return tl::make_error<Error>(ErrorCode::SystemError, "System error: {}", ec.message());

	return tl::success();
}

//////////////////////////////////////////////////////////////////////////

RemoveResult NativeFilesystem::remove(AbsPathView path)
{
	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFilename(path) == 0)
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	std::error_code ec;
	std::filesystem::remove(toStdPath(path), ec);
	if (ec == std::errc::no_such_file_or_directory)
		return tl::make_error<Error>(ErrorCode::NotFound, "Not found: {}", ec.message());
	if (ec == std::errc::permission_denied)
		return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied: {}", ec.message());
	if (ec)
		return tl::make_error<Error>(ErrorCode::SystemError, "System error: {}", ec.message());

	return tl::success();
}

//////////////////////////////////////////////////////////////////////////

RemoveRecursivelyResult NativeFilesystem::removeRecursively(AbsPathView path)
{
	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFolder(path) == 0)
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	OUTCOME_TRY(const bool isFolder, isFolder(path));

	std::error_code ec;
	if (isFolder)
		std::filesystem::remove_all(toStdPath(path), ec);
	else
		std::filesystem::remove(toStdPath(path), ec);
	if (ec == std::errc::no_such_file_or_directory)
		return tl::make_error<Error>(ErrorCode::NotFound, "Not found: {}", ec.message());
	if (ec == std::errc::permission_denied)
		return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied: {}", ec.message());
	if (ec)
		return tl::make_error<Error>(ErrorCode::SystemError, "System error: {}", ec.message());

	return tl::success();
}

//////////////////////////////////////////////////////////////////////////

SetWriteTimeResult NativeFilesystem::setWriteTime(AbsPathView path, time_t time)
{
	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	tl::string pathStr = path.str();

	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFilename(path) == 0)
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	std::error_code ec;
	std::filesystem::last_write_time(toStdPath(path), from_time_t(time), ec);
	if (ec == std::errc::no_such_file_or_directory)
		return tl::make_error<Error>(ErrorCode::NotFound, "Not found: {}", ec.message());
	if (ec == std::errc::permission_denied)
		return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied: {}", ec.message());
	if (ec)
		return tl::make_error<Error>(ErrorCode::SystemError, "System error: {}", ec.message());

	return tl::success();
}

//////////////////////////////////////////////////////////////////////////

GetStatResult NativeFilesystem::getStat(AbsPathView path) const
{
	if (!path.is_valid())
		return tl::make_error<Error>(ErrorCode::BadPath, "Invalid Path");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFilename(path) == 0)
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	struct stat s;
	if (const int res = stat(path.str().c_str(), &s); res != 0)
	{
		if (res == ENOENT || res == ENOTDIR)
			return tl::make_error<Error>(ErrorCode::NotFound, "Not found");
		if (res == EACCES)
			return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied");
		return tl::make_error<Error>(ErrorCode::SystemError, "System error");
	}

	return Stat{ static_cast<uint64_t>(s.st_size), s.st_mtime, s.st_atime };
}

//////////////////////////////////////////////////////////////////////////

cppcoro::generator<EnumerateEntry> NativeFilesystem::enumerate(AbsPath path) const
{
	TL_ASSERT(path.is_valid());

	IsFolderResult isFolderResult = isFolder(path);
	if (isFolderResult.has_error() || isFolderResult.value() == false)
		co_return;

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFolder(path) == 0)
	{
		TL_FAIL("Bad path case!");
		co_return;
	}

	for (auto const& entry : std::filesystem::directory_iterator{ toStdPath(path) })
	{
		const auto status = entry.status();
		const auto type = status.type();
		if (type == std::filesystem::file_type::directory)
		{
			auto str = entry.path().filename().u8string();
			co_yield { RelPath((char*)str.data(), str.size()), true };
		}
		else if (type == std::filesystem::file_type::regular)
		{
			auto str = entry.path().filename().u8string();
			co_yield { RelPath((char*)str.data(), str.size()), false };
		}
	}
}

//////////////////////////////////////////////////////////////////////////

cppcoro::generator<EnumerateEntry> NativeFilesystem::enumerateRecursively(AbsPath path) const
{
	TL_ASSERT(path.is_valid());

	IsFolderResult isFolderResult = isFolder(path);
	if (isFolderResult.has_error() || isFolderResult.value() == false)
		co_return;

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFolder(path) == 0)
	{
		TL_FAIL("Bad path case!");
		co_return;
	}

	AbsPath eePath;
	const auto stdRootPath = toStdPath(path);

	for (auto const& entry : std::filesystem::recursive_directory_iterator{ stdRootPath })
	{
		const auto status = entry.status();
		const auto type = status.type();
		if (type == std::filesystem::file_type::directory)
		{
			toFSAbsPath(eePath, entry.path());
			co_yield{ path.path_to(eePath), true };
		}
		else if (type == std::filesystem::file_type::regular)
		{
			toFSAbsPath(eePath, entry.path());
			co_yield{ path.path_to(eePath), false };
		}
	}
}

//////////////////////////////////////////////////////////////////////////

AbsPath NativeFilesystem::getCurrentFolder() const
{
	std::error_code ec;
	const auto path = std::filesystem::current_path(ec);
	return toFSAbsPath(path);
}

//////////////////////////////////////////////////////////////////////////

tl::result<Error> NativeFilesystem::setCurrentFolder(AbsPathView path)
{
	if (!path.is_valid())
		return tl::make_error<Error>(Error::code_t::BadPath, "Path empty");

	if (m_checkFlags.test(CheckFlag::ValidatePathCase) && native_utils::validateCaseSensitiveFolder(path) == 0)
		return tl::make_error<Error>(ErrorCode::BadPath, "Bad path case!");

	std::error_code ec;
	std::filesystem::current_path(toStdPath(path), ec);
	if (ec == std::errc::no_such_file_or_directory)
		return tl::make_error<Error>(ErrorCode::NotFound, "Not found: {}", ec.message());
	if (ec == std::errc::permission_denied)
		return tl::make_error<Error>(ErrorCode::NotAllowed, "Access denied: {}", ec.message());
	if (ec)
		return tl::make_error<Error>(ErrorCode::SystemError, "System error: {}", ec.message());

	return tl::success();
}

//////////////////////////////////////////////////////////////////////////

NativeFilesystem::EnumerateFilesResult NativeFilesystem::enumerateFiles(AbsPathView fullPath) const
{
	tl::vector<tl::string> files;
	for (EnumerateEntry ee: enumerate(fullPath))
	{
		if (!ee.isFolder)
		{
			files.reserve(1024);
			files.push_back(ee.path.back());
		}
	}
	return files;
}

//////////////////////////////////////////////////////////////////////////

NativeFilesystem::EnumerateFoldersResult NativeFilesystem::enumerateFolders(AbsPathView fullPath) const
{
	tl::vector<tl::string> folders;
	for (EnumerateEntry ee : enumerate(fullPath))
	{
		if (ee.isFolder)
		{
			folders.reserve(1024);
			folders.push_back(ee.path.back());
		}
	}
	return folders;
}

//////////////////////////////////////////////////////////////////////////

AbsPath NativeFilesystem::makeAbsPath(const tl::string& string) const
{
	return AbsPath::from_string(string, getCurrentFolder());
}

//////////////////////////////////////////////////////////////////////////

}
