#pragma once

/**
** \defgroup util_js Utilities
** This is becoming just a dumping ground of small helpers.
** @{
*/

#include "quickjs.h"

#include <stdbool.h>

/**
** Type of a setting.
*/
typedef enum _tf_setting_kind_t
{
	k_kind_unknown,
	k_kind_bool,
	k_kind_int,
	k_kind_string,
} tf_setting_kind_t;

/** An event loop. */
typedef struct uv_loop_s uv_loop_t;

/**
** Register utility script functions.
** @param context The JS context.
*/
void tf_util_register(JSContext* context);

/**
** Convert UTF-8 bytes in a buffer or Uint8Array or similar to a String.
** @param context The JS context.
** @param value The UTF-8 bytes.
** @return A string representation of the data interpreted as UTF-8 bytes.
*/
JSValue tf_util_utf8_decode(JSContext* context, JSValue value);

/**
** Get the data from what might be an ArrayBuffer.
** @param context The JS context.
** @param[out] psize The size of the data in bytes.
** @param obj The object which might be an ArrayBuffer.
** @return The ArrayBuffer's data.
*/
uint8_t* tf_util_try_get_array_buffer(JSContext* context, size_t* psize, JSValueConst obj);

/**
** Get the ArrayBuffer from what might be a typed ArrayBuffer.
** @param context The JS context.
** @param obj The object which might be a typed ArrayBuffer.
** @param[out] pbyte_offset The offset into the buffer at which the typed ArrayBuffer starts.
** @param[out] pbyte_length The length of the buffer.
** @param[out] pbytes_per_element Element size in bytes.
** @return An ArrayBuffer if obj was a typed ArrayBuffer.
*/
JSValue tf_util_try_get_typed_array_buffer(JSContext* context, JSValueConst obj, size_t* pbyte_offset, size_t* pbyte_length, size_t* pbytes_per_element);

/**
** Print an error and message the owning task if possible.
** @param context The JS context.
** @param value The value which might be an exception.
** @return true If the value was an exception and an error was reported.
*/
bool tf_util_report_error(JSContext* context, JSValue value);

/**
** Get the length of an array.
** @param context The JS context.
** @param value An array with a "length" field.
** @return The array's length.
*/
int tf_util_get_length(JSContext* context, JSValue value);

/**
** Get the index at which to insert into an array in order to preserve sorted order.
** @param key The key being inserted.
** @param base The beginning of the array.
** @param count The number of elements in the array.
** @param size The size of a single element of the array.
** @param compare A comparison function comparing key and an element of the array.
** @return The index at which to insert key in the array, between 0 and count inclusive.
*/
int tf_util_insert_index(const void* key, const void* base, size_t count, size_t size, int (*compare)(const void*, const void*));

/**
** Create a Uint8Array from bytes.
** @param context The JS context.
** @param data The bytes.
** @param size The number of bytes in data.
** @return The created array.
*/
JSValue tf_util_new_uint8_array(JSContext* context, const uint8_t* data, size_t size);

/**
** Base64-encode data.
** @param source The source data.
** @param source_length The length of the source data.
** @param[out] out A buffer to receive the encoded data.
** @param out_length The size of the buffer to receive encoded data.
** @return The size of the encoded data.
*/
size_t tf_base64_encode(const uint8_t* source, size_t source_length, char* out, size_t out_length);

/**
** Base64-decode data.
** @param source The source data.
** @param source_length The length of the source data.
** @param[out] out A buffer to receipve the decoded data.
** @param out_length The size of the buffer to receive decoded data.
** @return The size of the decoded data.
*/
size_t tf_base64_decode(const char* source, size_t source_length, uint8_t* out, size_t out_length);

/**
** Capture a stack backtrace of the calling thread.
** @param[out] buffer A buffer with at least count element to receive the backtrace.
** @param count The size of buffer.
** @return The numbef of captured frames.
*/
int tf_util_backtrace(void** buffer, int count);

/**
** Convert a stack backtrace to string.
** @param buffer A stack backtrace.
** @param count The number of elements in the backtrace.
** @return A string representation of the stack backtrace with function names,
** files, and line numbers, as possible.  Must be freed with tf_free() by the
** caller.
*/
const char* tf_util_backtrace_to_string(void* const* buffer, int count);

/**
** Capture a stack backtrace of the calling thread and convert it immediately to string.
** @return A string representation of the stack backtrace with function names,
** files, and line numbers, as possible.  Must be freed with tf_free() by the
** caller.
*/
const char* tf_util_backtrace_string();

/**
** Print a stack backtrace of the calling thread.
*/
void tf_util_print_backtrace();

/**
** Convert a function pointer to its name, if possible.
** @return The function name or null.
*/
const char* tf_util_function_to_string(void* function);

/**
** Get the minimum of two values.
** @param a The first value.
** @param b The second value.
** @return The minimum of a and b.
*/
#define tf_min(a, b) \
	({ \
		__typeof__(a) _a = (a); \
		__typeof__(b) _b = (b); \
		_a > _b ? _b : _a; \
	})

/**
** Get the maximum of two values.
** @param a The first value.
** @param b The second value.
** @return The maximum of a and b.
*/
#define tf_max(a, b) \
	({ \
		__typeof__(a) _a = (a); \
		__typeof__(b) _b = (b); \
		_a > _b ? _a : _b; \
	})

/**
** Get the number of elements in an array.
** @param a The array.
** @return The number of array elements.
*/
#define tf_countof(a) ((int)(sizeof((a)) / sizeof(*(a))))

/**
** Get the default value of a global setting as a boolean.
** @param name The setting name.
** @return The default value.
*/
bool tf_util_get_default_global_setting_bool(const char* name);

/**
** Get the default value of a global setting as an integer.
** @param name The setting name.
** @return The default value.
*/
int tf_util_get_default_global_setting_int(const char* name);

/**
** Get the default value of a global setting as a string.
** @param name The setting name.
** @return The default value.
*/
const char* tf_util_get_default_global_setting_string(const char* name);

/**
** Get the expected kind of a global setting.
** @param name The setting name.
** @return The setting kind or unknown if nonexistent.
*/
tf_setting_kind_t tf_util_get_global_setting_kind(const char* name);

/**
** Log documentation for the available settings.
** @param line_prefix Text to prefix each line with."
*/
void tf_util_document_settings(const char* line_prefix);

/**
** Check if the app is running on a mobile device.
** @return true for iPhone/Android, false otherwise.
*/
bool tf_util_is_mobile();

/**
** Populate a string buffer, truncating if necessary.
** @param buffer The buffer.
** @param size The size of the buffer.
** @param string The value to set.
** @return The number of bytes set, not including the NULL terminator.
*/
size_t tf_string_set(char* buffer, size_t size, const char* string);

/** @} */
