// Either Vala's GirParser needs to learn to handle out caller-allocates
// arrays properly, or libdex should ship a fixup in a metadata file.
namespace DexVapiFixes {
	[CCode (cname = "dex_input_stream_read", cheader_filename = "libdex.h")]
	static extern Dex.Future input_stream_read (GLib.InputStream self, uint8[:size_t] buffer, int io_priority);
}

async int main () {
	var port = 8080;

	Dex.init ();

	var thread_pool = new Dex.ThreadPoolScheduler ();
	var socket_listener = new GLib.SocketListener ();

	try {
		socket_listener.add_inet_port (port, null);
	} catch (Error err) {
		error ("Failed to listen on port %u: %s", port, err.message);
	}
	print ("Listening on 0.0.0.0:%u\n", port);

	// Spawn a fiber on current thread for socket loop.
	var future = Dex.Scheduler.get_default ().spawn (0, () => {
		while (true) {
			// Accept an incoming connection.
			GLib.SocketConnection connection;
			try {
				connection = (GLib.SocketConnection) Dex.socket_listener_accept (socket_listener).await_object ();
			} catch (Error err) {
				return new Dex.Future.for_error ((owned) err);
			}
			// Spawn a fiber to handle the connection on the thread pool.
			thread_pool.spawn (0, () => {
				unowned var input = connection.get_input_stream ();
				unowned var output = connection.get_output_stream ();

				while (true) {
					try {
						uint8 buffer[1024];
						var n_read = DexVapiFixes.input_stream_read (input, buffer, GLib.Priority.DEFAULT).await_int64 ();
						int64 n_written = 0;
						for (var to_write = n_read; to_write > 0; to_write -= n_written) {
							n_written = Dex.output_stream_write (output, buffer[n_read - to_write : n_read], GLib.Priority.DEFAULT).await_int64 ();
							if (n_written == 0) {
								// EOF.
								break;
							}
						}
					} catch (Error err) {
						return new Dex.Future.for_error ((owned) err);
					}
				}
			});
		}
	});

	// When it completes, resume the main coroutine.
	future = new Dex.Future.finally ((owned) future, _res => {
		main.callback ();
		return null;
	});

	yield;
	return 0;
}