/*
 * FixScript v0.3 - http://www.fixscript.org/
 * Copyright (c) 2018-2020 Martin Dvorak <jezek2@advel.cz>
 *
 * This software is provided 'as-is', without any express or implied warranty.
 * In no event will the authors be held liable for any damages arising from
 * the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose, 
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */

#ifndef FIXSCRIPT_H
#define FIXSCRIPT_H

#ifdef __cplusplus
extern "C" {
#endif

typedef struct Heap Heap;
typedef struct Script Script;
typedef struct { int value; int is_array; } Value;
typedef void (*HandleFreeFunc)(void *p);
typedef void *(*HandleFunc)(Heap *heap, int op, void *p1, void *p2);
typedef Script *(*LoadScriptFunc)(Heap *heap, const char *fname, char **error_msg, void *data);
typedef Value (*NativeFunc)(Heap *heap, Value *error, int num_params, Value *params, void *data);

enum {
   FIXSCRIPT_SUCCESS                            = 0,
   FIXSCRIPT_ERR_INVALID_ACCESS                 = -1,
   FIXSCRIPT_ERR_INVALID_BYTE_ARRAY             = -2,
   FIXSCRIPT_ERR_INVALID_SHORT_ARRAY            = -3,
   FIXSCRIPT_ERR_INVALID_NULL_STRING            = -4,
   FIXSCRIPT_ERR_STATIC_WRITE                   = -5,
   FIXSCRIPT_ERR_OUT_OF_BOUNDS                  = -6,
   FIXSCRIPT_ERR_OUT_OF_MEMORY                  = -7,
   FIXSCRIPT_ERR_INVALID_SHARED_ARRAY_OPERATION = -8,
   FIXSCRIPT_ERR_KEY_NOT_FOUND                  = -9,
   FIXSCRIPT_ERR_RECURSION_LIMIT                = -10,
   FIXSCRIPT_ERR_UNSERIALIZABLE_REF             = -11,
   FIXSCRIPT_ERR_BAD_FORMAT                     = -12,
   FIXSCRIPT_ERR_FUNC_REF_LOAD_ERROR            = -13
};

enum {
   HANDLE_OP_FREE,
   HANDLE_OP_COPY,
   HANDLE_OP_COMPARE,
   HANDLE_OP_HASH,
   HANDLE_OP_TO_STRING,
   HANDLE_OP_MARK_REFS,
   HANDLE_OP_COPY_REFS
};

static inline Value fixscript_int(int value);
static inline Value fixscript_float(float value);
static inline int fixscript_is_int(Value value);
static inline int fixscript_is_float(Value value);
static inline int fixscript_get_int(Value value);
static inline float fixscript_get_float(Value value);

Heap *fixscript_create_heap();
void fixscript_free_heap(Heap *heap);
void fixscript_collect_heap(Heap *heap);
long long fixscript_heap_size(Heap *heap);
void fixscript_adjust_heap_size(Heap *heap, long long relative_change);
void fixscript_ref(Heap *heap, Value value);
void fixscript_unref(Heap *heap, Value value);
void fixscript_register_cleanup(Heap *heap, HandleFreeFunc free_func, void *data);
void fixscript_register_heap_key(volatile int *key);
int fixscript_set_heap_data(Heap *heap, int key, void *data, HandleFreeFunc free_func);
void *fixscript_get_heap_data(Heap *heap, int key);

void fixscript_mark_ref(Heap *heap, Value value);
Value fixscript_copy_ref(void *ctx, Value value);

Value fixscript_create_array(Heap *heap, int len);
Value fixscript_create_byte_array(Heap *heap, const char *buf, int len);
Value fixscript_create_shared_array(Heap *heap, int len, int elem_size);
Value fixscript_create_shared_array_from(Heap *heap, int type, void *ptr, int len, int elem_size, HandleFreeFunc free_func, void *data);
int fixscript_set_array_length(Heap *heap, Value arr_val, int len);
int fixscript_get_array_length(Heap *heap, Value arr_val, int *len);
int fixscript_is_array(Heap *heap, Value arr_val);
int fixscript_set_array_elem(Heap *heap, Value arr_val, int idx, Value value);
int fixscript_get_array_elem(Heap *heap, Value arr_val, int idx, Value *value);
int fixscript_append_array_elem(Heap *heap, Value arr_val, Value value);
int fixscript_get_array_range(Heap *heap, Value arr_val, int off, int len, Value *values);
int fixscript_set_array_range(Heap *heap, Value arr_val, int off, int len, Value *values);
int fixscript_get_array_bytes(Heap *heap, Value arr_val, int off, int len, char *bytes);
int fixscript_set_array_bytes(Heap *heap, Value arr_val, int off, int len, char *bytes);
int fixscript_copy_array(Heap *heap, Value dest, int dest_off, Value src, int src_off, int count);
void *fixscript_get_shared_array_data(Heap *heap, Value arr_val, int *len, int *elem_size, void **data, int expected_type, int *actual_type);
int fixscript_lock_array(Heap *heap, Value arr_val, int off, int len, void **data, int elem_size, int read);
void fixscript_unlock_array(Heap *heap, Value arr_val, int off, int len, void **data, int elem_size, int write);

Value fixscript_create_string(Heap *heap, const char *s, int len);
Value fixscript_create_string_utf16(Heap *heap, const unsigned short *s, int len);
int fixscript_get_string(Heap *heap, Value str_val, int str_off, int str_len, char **str, int *len_out);
int fixscript_get_string_utf16(Heap *heap, Value str_val, int str_off, int str_len, unsigned short **str, int *len_out);
int fixscript_is_string(Heap *heap, Value str_val);

Value fixscript_create_hash(Heap *heap);
int fixscript_is_hash(Heap *heap, Value hash_val);
int fixscript_set_hash_elem(Heap *heap, Value hash_val, Value key_val, Value value_val);
int fixscript_get_hash_elem(Heap *heap, Value hash_val, Value key_val, Value *value_val);
int fixscript_remove_hash_elem(Heap *heap, Value hash_val, Value key_val, Value *value_val);
int fixscript_clear_hash(Heap *heap, Value hash_val);
int fixscript_iter_hash(Heap *heap, Value hash_val, Value *key_val, Value *value_val, int *pos);

Value fixscript_create_handle(Heap *heap, int type, void *handle, HandleFreeFunc free_func);
Value fixscript_create_value_handle(Heap *heap, int type, void *handle, HandleFunc handle_func);
int fixscript_get_handle_type(Heap *heap, Value handle_val);
void *fixscript_get_handle(Heap *heap, Value handle_val, int expected_type, int *actual_type);
void fixscript_register_handle_types(volatile int *offset, int count);

int fixscript_create_weak_ref(Heap *heap, Value value, Value *container, Value *key, Value *weak_ref);
int fixscript_get_weak_ref(Heap *heap, Value weak_ref, Value *value);
int fixscript_is_weak_ref(Heap *heap, Value weak_ref);

const char *fixscript_get_error_msg(int error_code);
Value fixscript_create_error(Heap *heap, Value msg);
Value fixscript_create_error_string(Heap *heap, const char *s);
Value fixscript_error(Heap *heap, Value *error, int code);

int fixscript_dump_value(Heap *heap, Value value, int newlines);
int fixscript_to_string(Heap *heap, Value value, int newlines, char **str, int *len);

int fixscript_clone(Heap *heap, Value value, int deep, Value *clone);
int fixscript_clone_between(Heap *dest, Heap *src, Value value, Value *clone, LoadScriptFunc load_func, void *load_data, char **error_msg);
int fixscript_serialize(Heap *heap, Value *buf_val, Value value);
int fixscript_unserialize(Heap *heap, Value buf_val, int *off, int len, Value *value);
int fixscript_serialize_to_array(Heap *heap, char **buf, int *len_out, Value value);
int fixscript_unserialize_from_array(Heap *heap, const char *buf, int *off_out, int len, Value *value);

Script *fixscript_load(Heap *heap, const char *src, const char *fname, char **error_msg, LoadScriptFunc load_func, void *load_data);
Script *fixscript_load_file(Heap *heap, const char *name, char **error_msg, const char *dirname);
Script *fixscript_load_embed(Heap *heap, const char *name, char **error_msg, const char * const * const embed_files);
Script *fixscript_resolve_existing(Heap *heap, const char *name, char **error_msg, void *data);
Script *fixscript_get(Heap *heap, const char *fname);
char *fixscript_get_script_name(Heap *heap, Script *script);
Value fixscript_get_function(Heap *heap, Script *script, const char *func_name);
int fixscript_get_function_name(Heap *heap, Value func_val, char **script_name_out, char **func_name_out, int *num_params_out);
Value fixscript_run(Heap *heap, Script *script, const char *func_name, Value *error, ...);
Value fixscript_run_args(Heap *heap, Script *script, const char *func_name, Value *error, Value *args);
Value fixscript_call(Heap *heap, Value func, int num_params, Value *error, ...);
Value fixscript_call_args(Heap *heap, Value func, int num_params, Value *error, Value *args);
void fixscript_register_native_func(Heap *heap, const char *name, NativeFunc func, void *data);

char *fixscript_dump_code(Heap *heap, Script *script, const char *func_name);
char *fixscript_dump_heap(Heap *heap);


// inline functions:

static inline Value fixscript_int(int value)
{
   return (Value) { value, 0 };
}

static inline Value fixscript_float(float value)
{
   union {
      float f;
      unsigned int i;
   } u;
   u.f = value;
   // flush denormals to zero:
   if ((u.i & (0xFF << 23)) == 0) {
      u.i &= ~((1<<23)-1);
   }
   return (Value) { (int)u.i, 1 };
}

static inline int fixscript_is_int(Value value)
{
   return !value.is_array;
}

static inline int fixscript_is_float(Value value)
{
   return value.is_array && (value.value == 0 || ((unsigned int)value.value) >= (1 << 23));
}

static inline int fixscript_get_int(Value value)
{
   return value.value;
}

static inline float fixscript_get_float(Value value)
{
   union {
      float f;
      unsigned int i;
   } u;
   u.i = value.value;
   return u.f;
}

#ifdef __cplusplus
}
#endif

#endif /* FIXSCRIPT_H */
