module std::io::os;

enum NativeSystemDir
{
	DESKTOP,
	DOCUMENTS,
	VIDEOS,
	MUSIC,
	DOWNLOADS,
	PICTURES,
	TEMPLATES,
	PUBLIC_SHARE,
	SAVED_GAMES,
	SCREENSHOTS
}

module std::io::os @if(env::LIBC);
import std::io, std::os;

fn String? win32_get_known_folder_temp(Win32_REFKNOWNFOLDERID rfid) @private @if(env::WIN32)
{
	Win32_PWSTR path;
	Win32_HRESULT res = win32::shGetKnownFolderPath(rfid, 0x00008000 /* KF_FLAG_CREATE */, null, &path);
	if (res) return io::PATH_COULD_NOT_BE_FOUND~;
	return string::from_wstring(tmem, (WString)path);
}

fn Path? native_home_directory(Allocator allocator) => @pool()
{
	$switch env::OS_TYPE:
		$case IOS:
		$case MACOS:
		$case TVOS:
		$case WATCHOS:
        $case FREEBSD:
        $case KFREEBSD:
		$case LINUX:
        $case NETBSD:
		$case OPENBSD:
		$case HAIKU:
			return path::new(allocator, env::tget_var("HOME")) ?? io::PATH_COULD_NOT_BE_FOUND~;
		$case WIN32:
			return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_PROFILE));
		$default:
			return io::PATH_COULD_NOT_BE_FOUND~;
	$endswitch
}

fn Path? native_user_directory(Allocator allocator, NativeSystemDir dir) => @pool()
{
	$switch env::OS_TYPE:
        $case FREEBSD:
        $case KFREEBSD:
		$case LINUX:
        $case NETBSD:
		$case OPENBSD:
		$case HAIKU:
			switch (dir)
			{
				case DESKTOP: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "DESKTOP"));
				case DOWNLOADS: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "DOWNLOAD"));
				case DOCUMENTS: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "DOCUMENTS"));
				case MUSIC: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "MUSIC"));
				case VIDEOS: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "VIDEOS"));
				case PICTURES: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "PICTURES"));
				case PUBLIC_SHARE: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "PUBLICSHARE"));
				case TEMPLATES: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "TEMPLATES"));
				case SAVED_GAMES:
				case SCREENSHOTS: nextcase;
				default: return io::PATH_COULD_NOT_BE_FOUND~;
			}
		$case IOS:
		$case MACOS:
		$case WATCHOS:
		$case TVOS:
			switch (dir)
			{
				case DESKTOP: return path::new(allocator, darwin::find_first_directory_temp(DESKTOP, USER));
				case DOWNLOADS: return path::new(allocator, darwin::find_first_directory_temp(DOWNLOADS, USER));
				case DOCUMENTS: return path::new(allocator, darwin::find_first_directory_temp(DOCUMENT, USER));
				case MUSIC: return path::new(allocator, darwin::find_first_directory_temp(MUSIC, USER));
				case VIDEOS: return path::new(allocator, darwin::find_first_directory_temp(MOVIES, USER));
				case PICTURES: return path::new(allocator, darwin::find_first_directory_temp(PICTURES, USER));
				case PUBLIC_SHARE: return path::new(allocator, darwin::find_first_directory_temp(SHARED_PUBLIC, USER));
				case SAVED_GAMES:
				case SCREENSHOTS:
				case TEMPLATES: nextcase;
				default: return io::PATH_COULD_NOT_BE_FOUND~;
			}
		$case WIN32:
			switch (dir)
			{
				case DOWNLOADS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_DOWNLOADS));
				case DOCUMENTS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_DOCUMENTS));
				case DESKTOP: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_DESKTOP));
				case MUSIC: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_MUSIC));
				case VIDEOS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_VIDEOS));
				case PICTURES: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_PICTURES));
				case SAVED_GAMES: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_SAVED_GAMES));
				case SCREENSHOTS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_SCREENSHOTS));
				case TEMPLATES: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_TEMPLATES));
				case PUBLIC_SHARE: nextcase;
				default: return io::PATH_COULD_NOT_BE_FOUND~;
			}
		$default:
			return io::PATH_COULD_NOT_BE_FOUND~;
	$endswitch
}


fn Path? native_temp_directory(Allocator allocator) @if(!env::WIN32)
{
	foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
	{
		String tmpdir = env::tget_var(env) ?? "";
		if (tmpdir) return path::new(allocator, tmpdir);
	}
	return path::new(allocator, "/tmp");
}

fn Path? native_temp_directory(Allocator allocator) @if(env::WIN32) => @pool()
{
	Win32_DWORD len = win32::getTempPathW(0, null);
	if (!len) return io::GENERAL_ERROR~;
	Char16[] buff = mem::talloc_array(Char16, len);
	Win32_DWORD res = win32::getTempPathW(len, buff);
	if (!res) return io::GENERAL_ERROR~;
	return path::new(allocator, string::tfrom_utf16(buff[:res]));
}

module std::io::os @if(env::NO_LIBC);
import std::io::path;

macro Path? native_home_directory(Allocator allocator) => io::PATH_COULD_NOT_BE_FOUND~;
macro Path? native_temp_directory(Allocator allocator) => io::PATH_COULD_NOT_BE_FOUND~;
fn Path? native_user_directory(Allocator allocator, NativeSystemDir dir) => io::PATH_COULD_NOT_BE_FOUND~;
