OS_Handle FS_FileOpen(Str8 path, FS_AccessFlags access) {
	OS_Handle result = { 0 };

	M_TempScope(0, 0) {
		Str8 zpath = Str8_Copy(temp.arena, path);

		int mode  = 0644;
		int flags = 0;
		if (access & FS_ACCESS_READ) {
			if (access & FS_ACCESS_WRITE) {
				flags = O_RDWR | O_CREAT;
			}
			else {
				flags = O_RDONLY;
			}
		}
		else if (access & FS_ACCESS_WRITE) {
			flags = O_WRONLY | O_CREAT;
		}


		int fd = open((const char *) zpath.data, flags, mode);
		result.v[0] = (fd > 0) ? (U64) fd : 0;
	}

	return result;
}
void FS_FileClose(OS_Handle file) {
	int fd = cast(int) file.v[0];
	if (fd > 0) {
		close(fd);
	}
}

void FS_FileRead(OS_Handle file, void *ptr, U64 size, U64 offset) {
	int fd = cast(int) file.v[0];
	if (fd > 0) {
		U8 *buffer    = ptr;
		U64 current   = offset;
		U64 remainder = size;

		while (remainder != 0) {
			S64 nread = pread(fd, buffer, remainder, current);
			if (nread <= 0) {
				break;
			}

			buffer    += nread;
			current   += nread;
			remainder -= nread;
		}
	}
}

void FS_FileWrite(OS_Handle file, void *ptr, U64 size, U64 offset) {
	int fd = cast(int) file.v[0];
	if (fd > 0) {
		U8 *buffer    = ptr;
		U64 current   = offset;
		U64 remainder = size;

		while (remainder != 0) {
			S64 nwritten = pwrite(fd, buffer, remainder, current);
			if (nwritten <= 0) {
				break;
			}

			buffer    += nwritten;
			current   += nwritten;
			remainder -= nwritten;
		}
	}
}

U64 FS_FileSize(OS_Handle file) {
	U64 result = 0;

	int fd = cast(int) file.v[0];
	if (fd > 0) {
		struct stat sb;
		fstat(fd, &sb);

		result = sb.st_size;
	}

	return result;
}

FS_List FS_PathList(M_Arena *arena, Str8 path) {
	FS_List result = { 0 };

	M_TempScope(1, &arena) {
		Str8 zpath = Str8_Copy(temp.arena, path);

		DIR *dir = opendir((const char *) zpath.data);

		struct dirent *ent = readdir(dir);
		while (ent != 0) {
			if (ent->d_name[0] != '.') {
				FS_Entry *entry = M_ArenaPush(arena, FS_Entry);

				entry->basename = Str8_Copy(arena, Sz(ent->d_name));
				entry->path     = Sf(arena, "%.*s/%.*s", Sv(path), Sv(entry->basename));

				struct stat sb;
				stat((const char *) entry->path.data, &sb);

				entry->type = ((sb.st_mode & S_IFMT) == S_IFDIR) ? FS_ENTRY_TYPE_DIR : FS_ENTRY_TYPE_FILE;
				entry->time = (sb.st_mtim.tv_sec * 1e9) + sb.st_mtim.tv_nsec;
				entry->size = sb.st_size;

				SLL_Enqueue(result.first, result.last, entry);
				result.count += 1;
			}

			ent = readdir(dir);
		}
	}

	return result;
}

Str8 FS_SystemPath(M_Arena *arena, FS_SystemPathType path) {
	Str8 result = { 0 };

	switch (path) {
		case FS_SYSTEM_PATH_EXE: {
			U64 offset = M_ArenaOffset(arena);

			Str8 buffer;
			buffer.count = KB(1);
			buffer.data  = M_ArenaPush(arena, U8, .count = buffer.count);

			ssize_t nwritten = 0;
			for (;;) {
				nwritten = readlink("/proc/self/exe", cast(char *) buffer.data, buffer.count);
				if (nwritten < buffer.count) {
					break;
				}

				M_ArenaPop(arena, offset);

				buffer.count *= 2;
				buffer.data   = M_ArenaPush(arena, U8, .count = buffer.count);
			}

			if (nwritten > 0) {
				// Walk back until the prevous slash, then null-terminate there
				while (buffer.data[nwritten] != '/') { nwritten -= 1; }
				buffer.data[nwritten] = 0;

				M_ArenaPopSize(arena, buffer.count - nwritten - 1);

				result.count = nwritten;
				result.data  = buffer.data;
			}
			else {
				M_ArenaPop(arena, offset);
			}
		}
		break;

		case FS_SYSTEM_PATH_WORKING: {
			U64 offset = M_ArenaOffset(arena);

			Str8 buffer;
			buffer.count = KB(1);
			buffer.data  = M_ArenaPush(arena, U8, .count = buffer.count);

			char *wd;

			for (;;) {
				wd = getcwd((char *) buffer.data, buffer.count);
				if (wd != 0) {
					break;
				}
				else if (errno != ERANGE) {
					break;
				}

				M_ArenaPop(arena, offset);

				buffer.count *= 2;
				buffer.data   = M_ArenaPush(arena, U8, .count = buffer.count);
			}

			if (wd) {
				result = Sz(wd);
				M_ArenaPopSize(arena, buffer.count - result.count - 1);
			}
		}
		break;

		case FS_SYSTEM_PATH_USER: {
			char *home = getenv("HOME");
			if (home) {
				result = Sf(arena, "%s/.local/share", home);
			}
		}
		break;

		case FS_SYSTEM_PATH_TEMP: {
			char *temp = getenv("TEMP");
			if (temp) {
				result = Str8_Copy(arena, Sz(temp));
			}
			else {
				result = Str8_Copy(arena, S("/tmp"));
			}
		}
		break;
	}

	return result;
}
