#pragma once

/**
** \defgroup task Task
** Task is responsible for running JS in C.  It exposes just what is needed for
** sandboxed or trusted code, helps with communiciation between parent and
** child processes, including function calls and async operations across the
** boundaries.
** @{
*/

#include <stdbool.h>

#include "quickjs.h"

/** An event loop. */
typedef struct uv_loop_s uv_loop_t;
/** A timer. */
typedef struct uv_timer_s uv_timer_t;

/** A task identifier. */
typedef int taskid_t;
/** A promise identifier. */
typedef int promiseid_t;
/** An exported function identifier. */
typedef int exportid_t;
/** A handle to a task. */
typedef struct _tf_taskstub_t tf_taskstub_t;
/** A task. */
typedef struct _tf_task_t tf_task_t;
/** A trace instance. */
typedef struct _tf_trace_t tf_trace_t;
/** An SSB instance. */
typedef struct _tf_ssb_t tf_ssb_t;

/** The fixed ID of the parent task. */
static const taskid_t k_task_parent_id = 0;

/** A message type that can be sent between tasks. */
typedef enum _tf_task_message_t
{
	kResolvePromise,
	kRejectPromise,
	kInvokeExport,
	kReleaseExport,
	kReleaseImport,
	kActivate,
	kExecute,
	kKill,
	kSetImports,
	kGetExports,
	kLoadFile,
	kTaskError,
	kTaskTrace,
	kPrint,
} tf_task_message_t;

/**
** Create a task.
** @return A new task.
*/
tf_task_t* tf_task_create();

/**
** Configure a task from a file descriptor.  Typically a pipe to the parent
** task's process.
** @param task The task to configure.
** @param fd The file descriptor.
*/
void tf_task_configure_from_fd(tf_task_t* task, int fd);

/**
** Set the SSB network key.
** @param task The task.
** @param network_key The network key.
*/
void tf_task_set_ssb_network_key(tf_task_t* task, const char* network_key);

/**
** Set the path to the SQLite database.
** @param task The task.
** @param path The database path.
*/
void tf_task_set_db_path(tf_task_t* task, const char* path);

/**
** Set the path to a zip file from which to load all static data.
** @param task The task.
** @param path The zip file path or NULL.
*/
void tf_task_set_zip_path(tf_task_t* task, const char* path);

/**
** Set the path to the root of the project directory for data.
** @param task The task.
** @param path The file path or NULL.
*/
void tf_task_set_root_path(tf_task_t* task, const char* path);

/**
** Get the path to the zip file being used for static data.
** @param task The task.
** @return The zip file path or NULL.
*/
const char* tf_task_get_zip_path(tf_task_t* task);

/**
** Get the path to use for reading loose files.
** @param task The task.
** @return The path or NULL.
*/
const char* tf_task_get_root_path(tf_task_t* task);

/**
** Get the path to use for reading a given loose file.
** @param task The task.
** @param path The path to the file.
** @return The path or NULL.  Free with tf_free().
*/
const char* tf_task_get_path_with_root(tf_task_t* task, const char* path);

/**
** Set arbitrary named arguments that will be made available to the task.
** @param task The task.
** @param args A string of the form "key=value,other_key=other_value,..."
*/
void tf_task_set_args(tf_task_t* task, const char* args);

/**
** Get whether this instance is configure to run in a single process.
** @param task The running task.
** @return true if all tasks are running in a single process.
*/
bool tf_task_get_one_proc(tf_task_t* task);

/**
** Set whether all tasks should run in a single process.  Only supported to
** appease Apple's limitations.
** @param task The running task.
** @param one_proc True if subprocesses should not be used.
*/
void tf_task_set_one_proc(tf_task_t* task, bool one_proc);

/**
** Start a task running its script.
** @param task The task.
*/
void tf_task_activate(tf_task_t* task);

/**
** Update a task until it is done or stopped.
** @param task The task.
*/
void tf_task_run(tf_task_t* task);

/**
** Run a script from file on disk.
** @param task The task.
** @param file The path to the script file to run.
** @return 0 if there was a problem or 1 if the script was started.
*/
int tf_task_execute(tf_task_t* task, const char* file);

/**
** Set a task as trusted or untrusted.  Trusted tasks have more interface exposed to them.
** @param task The task.
** @param trusted true if the task is trusted.
*/
void tf_task_set_trusted(tf_task_t* task, bool trusted);

/**
** Get the JS context from a task.
** @param task The task.
** @return The context.
*/
JSContext* tf_task_get_context(tf_task_t* task);

/**
** Destroy a task.
** @param task The task.
*/
void tf_task_destroy(tf_task_t* task);

/**
** Convert a function to an integer handle that can be passed across processes.
** @param task The running task.
** @param to The task stub to which the handle will be passed.
** @param function The functoin to export.
** @return A handle representing the function.
*/
exportid_t tf_task_export_function(tf_task_t* task, tf_taskstub_t* to, JSValue function);

/**
** Create a function that can be called from a handle to an exported function
** from another task.
** @param task The running task.
** @param stub_id The task stub from which the function was exported.
** @param export_id The handle to the function.
** @return A function that, when called, invokes the corresponding function in
** the remote task.
*/
JSValue tf_task_add_import(tf_task_t* task, taskid_t stub_id, exportid_t export_id);

/**
** Get the event loop from a task.
** @param task The task.
** @return The loop.
*/
uv_loop_t* tf_task_get_loop(tf_task_t* task);

/**
** Get the task from a JS context.
** @param context The context.
** @return The task.
*/
tf_task_t* tf_task_get(JSContext* context);

/**
** Get the trace instance from a task.
** @param task The task.
** @return The trace instance.
*/
tf_trace_t* tf_task_get_trace(tf_task_t* task);

/**
** Get the SSB instance from a task.
** @param task The task.
** @return The SSB instance.
*/
tf_ssb_t* tf_task_get_ssb(tf_task_t* task);

/**
** Get the name of a task.
** @param task The task.
** @return The task's name as derived from the script it is running.
*/
const char* tf_task_get_name(tf_task_t* task);

/**
** Print through a task's parent.
** @param task The running task.
** @param argc The number of arguments to print.
** @param argv The arguments to print.
*/
void tf_task_print(tf_task_t* task, int argc, JSValueConst* argv);

/**
** Allocate a promise object.
** @param task The running task.
** @param[out] out_promise The promise that was allocated.
** @return The promise JS object.
*/
JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise);

/**
** Reject a promise.
** @param task The running task.
** @param promise The promise to reject.
** @param error The value with which to reject the promise.
*/
void tf_task_reject_promise(tf_task_t* task, promiseid_t promise, JSValue error);

/**
** Resolve a promise.
** @param task The running task.
** @param promise The promise to resolve.
** @param result The value with which to resolve the promise.
*/
void tf_task_resolve_promise(tf_task_t* task, promiseid_t promise, JSValue result);

/**
** Send a message referencing a promise across a packet stream.
** @param from The task originating the message.
** @param to The task handle receiving the message.
** @param type The message type.
** @param promise The promise.
** @param payload The content of the message.
*/
void tf_task_send_promise_message(tf_task_t* from, tf_taskstub_t* to, tf_task_message_t type, promiseid_t promise, JSValue payload);

/**
** Have a task handle a message from a packaet stream.
** @param packetType The type of the message.
** @param begin The data.
** @param length The size of the data.
** @param userData The task stub from which the packet was received.
*/
void tf_task_on_receive_packet(int packetType, const char* begin, size_t length, void* userData);

/**
** Generate an unused task identifier representing the task stub from the running task.
** @param task The running task.
** @param stub A handle to the task requesting an identifier.
** @return The new identifier.
*/
taskid_t tf_task_allocate_task_id(tf_task_t* task, tf_taskstub_t* stub);

/**
** Remove a task stub from a task.
** @param task The parent task.
** @param child The task handle to remove.
*/
void tf_task_remove_child(tf_task_t* task, tf_taskstub_t* child);

/**
** Send an error to the parent task.
** @param task The current task.
** @param error The potential error.
** @return true If the object was an error or exception and it was passed to
** the parent task.
*/
bool tf_task_send_error_to_parent(tf_task_t* task, JSValue error);

/**
** Get a report of miscellaneous debug information.
** @param task The task.
** @return A JSON representation of various debug information that must be
** freed with tf_free().
*/
char* tf_task_get_debug(tf_task_t* task);

/**
** A callback used to start an Android service.
** @param pipe_fd A file descriptor with which to communicate with the invoking
** task.
*/
typedef void(tf_android_start_service_t)(int pipe_fd);

/**
** A callback used to stop an Android service.
*/
typedef void(tf_android_stop_service_t)();

/**
** Set Android service callbacks.
** @param start_service Start service callback.
** @param stop_service Stop service callback.
*/
void tf_task_set_android_service_callbacks(tf_android_start_service_t* start_service, tf_android_stop_service_t* stop_service);

/**
** Get the callback registered for starting an Android service.
** @return the callback.
*/
tf_android_start_service_t* tf_task_get_android_start_service();

/**
** Get the callback registered for stopping an Android service.
** @return the callback.
*/
tf_android_stop_service_t* tf_task_get_android_stop_service();

/**
** Check for JS jobs that need to be run.  Generally to be called post-JS_Call
** in tf_util_report_error.
** @param task The task.
*/
void tf_task_check_jobs(tf_task_t* task);

/**
** Check whether tf_task_destroy has been called already.
** @param task The task.
** @return true if the task is in the process of shutting down.
*/
bool tf_task_is_shutting_down(tf_task_t* task);

/** @} */
