#include "client_socket.h"
#include "local_runqueue_list.h"
#include "local_runqueue.h"
#include "global_request_scheduler.h"

__thread static struct ps_list_head local_runqueue_list;

bool
local_runqueue_list_is_empty()
{
	return ps_list_head_empty(&local_runqueue_list);
}

/* Get the sandbox at the head of the thread local runqueue */
struct sandbox *
local_runqueue_list_get_head()
{
	return ps_list_head_first_d(&local_runqueue_list, struct sandbox);
}

/**
 * Removes the thread from the thread-local runqueue
 * @param sandbox sandbox
 */
void
local_runqueue_list_remove(struct sandbox *sandbox_to_remove)
{
	ps_list_rem_d(sandbox_to_remove);
}

struct sandbox *
local_runqueue_list_remove_and_return()
{
	struct sandbox *sandbox_to_remove = ps_list_head_first_d(&local_runqueue_list, struct sandbox);
	ps_list_rem_d(sandbox_to_remove);
	return sandbox_to_remove;
}

/**
 * Execute the sandbox at the head of the thread local runqueue
 * If the runqueue is empty, pull a fresh batch of sandbox requests, instantiate them, and then execute the new head
 * @return the sandbox to execute or NULL if none are available
 */
struct sandbox *
local_runqueue_list_get_next()
{
	struct sandbox_request *sandbox_request;

	// If our local runqueue is empty, try to pull and allocate a sandbox request from the global request scheduler
	if (local_runqueue_is_empty()) {
		if (global_request_scheduler_remove(&sandbox_request) < 0) goto err;

		struct sandbox *sandbox = sandbox_allocate(sandbox_request);
		if (!sandbox) goto err_allocate;

		sandbox->state = SANDBOX_RUNNABLE;
		local_runqueue_add(sandbox);

	done:
		return sandbox;
	err_allocate:
		client_socket_send(sandbox_request->socket_descriptor, 503);
		client_socket_close(sandbox_request->socket_descriptor);
		free(sandbox_request);
	err:
		sandbox = NULL;
		goto done;
	}

	/* Execute Round Robin Scheduling Logic */
	struct sandbox *next_sandbox = local_runqueue_list_remove_and_return();
	assert(next_sandbox->state != SANDBOX_RETURNED);
	local_runqueue_add(next_sandbox);

	return next_sandbox;
}


/**
 * Append a sandbox to the runqueue
 * @returns the appended sandbox
 */
void
local_runqueue_list_append(struct sandbox *sandbox_to_append)
{
	assert(ps_list_singleton_d(sandbox_to_append));
	ps_list_head_append_d(&local_runqueue_list, sandbox_to_append);
}

/**
 * Conditionally checks to see if current sandbox should be preempted.
 * FIFO doesn't preempt, so just return.
 */
void
local_runqueue_list_preempt(ucontext_t *user_context)
{
	return;
}


void
local_runqueue_list_initialize()
{
	ps_list_head_init(&local_runqueue_list);

	/* Register Function Pointers for Abstract Scheduling API */
	struct local_runqueue_config config = { .add_fn      = local_runqueue_list_append,
		                                .is_empty_fn = local_runqueue_list_is_empty,
		                                .delete_fn   = local_runqueue_list_remove,
		                                .get_next_fn = local_runqueue_list_get_next,
		                                .preempt_fn  = local_runqueue_list_preempt };
	local_runqueue_initialize(&config);
};
