#ifndef BYONDAPI_H
#define BYONDAPI_H

/*
	Byondapi calling format:

	In call_ext() or load_ext(), prefix your library's exported function name with byond: to use
	Byondapi format. Prefix with @ to use STDCALL instead of CDECL calling convention.
	Prefixing a function name with byond,await: uses the await format (below)

	Functions in your library that are called via Byondapi look like this:

		extern "C" BYOND_EXPORT CByondValue MyFunction(u4c n, CByondValue v[]) {
			...
		}

	And it's called from DM via:

		call_ext("mylib", "byond:MyFunction")(arguments)

	If load_ext() is used:

		var myfunction = load_ext("mylib", "byond:MyFunction")
		call(myfunction)(arguments)

	AWAIT FORMAT

	If the function is prefixed with byond,await, you can use a special asynchronous format. This will
	put the calling proc to sleep until you call Byond_Return(), so it's non-blocking.

		var/result = call("mylib", "byond,await:LongFunction")(arguments)
        world << "After a long wait, my result is [result]."

	The function in your library has an extra argument for the /callee of the sleeping proc.

		extern "C" BYOND_EXPORT void LongFunction(u4c n, CByondValue v[], CByondValue waiting_proc) {
			std::thread t(LongFunctionStuff, waiting_proc);
			t.detach();
			// return right away
		}

		void LongFunctionStuff(CByondValue waiting_proc) {
			CByondValue retval;
			... // do stuff that takes a long time
			
			Byond_Return(&waiting_proc, &retval);
		}
 */

/*
	BYOND public API version 516.1674

	Because for some reason nobody can get their ducks in a row, all of the
	exported functions from byondcore.dll/libbyond.so are limited to C
	conventions only. A header and source file for C++ wrappers is available
	for inclusion in your projects.

	IMPORTANT CHANGES:

	- BYOND 516.1674:

	  - Temporary refs have been abolished. All references are persistent.
	  - The ByondValue class attaches (accepts ownership without incref) a ref
	    in its CByondValue constructor.

	  - For thread safety, Byond_LastError() no longer takes zero arguments
	    and returns a char pointer. Instead, it requires you to pass in a
	    char* buffer and a u4c pointer for the buffer length, like
	    Byond_ToString() uses. Returns true if assignment was successful,
	    false if more room is needed in the buffer. The buffer length needed
	    is assigned to the length pointer in either case.

	  - ByondExtException is no longer thrown unless a thread-local var is set
	    to signal it should be thrown.

	- BYOND 516.1651:
	  
	  - Reference counting has been reworked completely.
	  - Calls on the main thread still produce temporary references, but now
	    they have a proper count.
	  - Calls made from other threads always produce persistent references
	    that must be cleaned up with Byond_DecRef().
	  - Byond_ThreadSync() always creates persistent references.

	- BYOND 516.1664:

	  - New async format introduced.
 */

#if defined(WIN32) || defined(WIN64)
#define IS_WINDOWS
#else
#define IS_LINUX
#endif


// See https://github.com/cpredef/predef/blob/master/Architectures.md
#if defined(i386) || defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__X86__)
#define _X86
#define _X86ORX64
#define DM_32BIT
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) || defined(_M_X64) || defined(_WIN64) || defined(WIN64)
#define _X64
#define _X86ORX64
#define DM_64BIT
#elif defined(__arm__) || defined(_M_ARM)
#define _ARM
#if defined(__LP64__) || defined(_LP64)
#define DM_64BIT
#else
#define DM_32BIT
#endif
#endif



/*types*/
typedef unsigned char  u1c;
typedef signed   char  s1c;
typedef unsigned short u2c;
typedef signed   short s2c;
#ifdef DM_64BIT
typedef unsigned int   u4c;
typedef signed   int   s4c;
#else
typedef unsigned long  u4c;
typedef signed   long  s4c;
#endif

#if defined(_MSC_VER) || defined(__BORLANDC__)
  typedef __int64 s8c;
  typedef unsigned __int64 u8c;
#else
  typedef long long int s8c;
  typedef unsigned long long int u8c;
#endif

union u4cOrPointer {
	u4c num;
	void *ptr;
};

#ifdef __GNUC__
#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#else
#define GCC_VERSION 0
#endif


// determine if move-constructor and move-assignment are supported
#if defined(_MSC_VER) && _MSC_VER >= 1800
#define _SUPPORT_MOVES
#elif defined(__GNUC__) && GCC_VERSION >= 50000
#define _SUPPORT_MOVES
#endif


#define u1cMASK ((u1c)0xff)
#define u2cMASK ((u2c)0xffff)
#define u3cMASK ((u4c)0xffffffL)
#define u4cMASK ((u4c)0xffffffffL)

#define u1cMAX u1cMASK
#define u2cMAX u2cMASK
#define u3cMAX u3cMASK
#define u4cMAX u4cMASK

#define s1cMAX 0x7f
#define s1cMIN (-0x7f)
#define s2cMAX 0x7fff
#define s2cMIN (-0x7fff)
#define s4cMAX 0x7fffffffL
#define s4cMIN (-0x7fffffffL)

#define NONE u2cMAX
#define NOCH u1cMAX

#ifdef WIN32
#define THREAD_VAR __declspec(thread)
#else
#define THREAD_VAR __thread
#endif

/* dll export stuff */
#ifdef WIN32
#define DUNGPUB    __declspec(dllimport)
#define BYOND_EXPORT __declspec(dllexport)	// for functions in user-defined DLLs for use with call_ext()
#else // unix/g++, combine with -fvisibility=hidden to hide non-exported symbols
#define DUNGPUB
#define BYOND_EXPORT __attribute__ ((visibility("default")))	// for functions in user-defined .so libraries for use with call_ext()
#endif


// ByondValue

/*
	Many of the routines in this library return a bool value. If true, the
	operation succeeded. If false, it failed and calling Byond_LastError() will
	return an error string.

	The C++ wrappers change this true/false behavior to raise exceptions instead.
	This would be preferable across the board but throwing exceptions across
	module boundaries is bad juju.

	Any functions that return CByondValue values via pointers, such as
	Byond_ReadVar(), add to a reference count. See REFERENCE COUNTING below
	for more details. If you're not spawning any threads of your own, you
	pretty much don't have to do anything with reference counts unless you need
	to hold onto a reference for a while longer.
 */

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Gets the last error from a failed call
 * If the buffer isn't big enough, it will be filled as far as it can be, and includes a trailing null.
 * Note: This function is thread-safe, but there is no guarantee the error message can't be changed by another thread between calls.
 * Best practice is to use a while() loop to call this function, reallocating the buffer on failure, until success.
 * @param buf char array, allocated by caller (can be null if querying length)
 * @param buflen Pointer to length of array in bytes; receives the string length (including trailing null)
 * @return True on success; false if array is not big enough
 */
DUNGPUB bool Byond_LastError(char *buf, u4c *buflen);

/**
 * Sets the last error string to a copy of the input
 * NULL is treated as an empty string
 * @param err The error string
 */
DUNGPUB void Byond_SetLastError(char const *err);

/**
 * Gets the current BYOND version
 * @param version Pointer to the major version number
 * @param build Pointer to the build number
 */
DUNGPUB void Byond_GetVersion(u4c *version, u4c *build);

/**
 * Gets the DMB version
 * @return Version number the .dmb was built with
 */
DUNGPUB u4c Byond_GetDMBVersion();

typedef u1c ByondValueType;
union ByondValueData {
	u4c ref;	//!< 4-byte reference ID
	float num;	//!< floating-point number
};

struct CByondValue {
	ByondValueType type;	//!< 1-byte intrinsic data type
	u1c junk1, junk2, junk3;	//!< padding
	ByondValueData data;	//!< 4-byte reference ID or floating point number
};

/**
 * Fills a CByondValue struct with a null value.
 * @param v Pointer to CByondValue
 */
DUNGPUB void ByondValue_Clear(CByondValue *v);

/**
 * Reads CByondVale's 1-byte data type
 * @param v Pointer to CByondValue
 * @return Type of value
 */
DUNGPUB ByondValueType ByondValue_Type(CByondValue const *v);

/**
 * @param v Pointer to CByondValue
 * @return True if value is null
 */
DUNGPUB bool ByondValue_IsNull(CByondValue const *v);
/**
 * @param v Pointer to CByondValue
 * @return True if value is a numeric type
 */
DUNGPUB bool ByondValue_IsNum(CByondValue const *v);
/**
 * @param v Pointer to CByondValue
 * @return True if value is a string
 */
DUNGPUB bool ByondValue_IsStr(CByondValue const *v);
/**
 * @param v Pointer to CByondValue
 * @return True if value is a list (any list type, not just user-defined)
 */
DUNGPUB bool ByondValue_IsList(CByondValue const *v);

/**
 * Determines if a value is logically true or false
 *
 * @param v Pointer to CByondValue
 * @return Truthiness of value
 */
DUNGPUB bool ByondValue_IsTrue(CByondValue const *v);

/**
 * @param v Pointer to CByondValue
 * @return Floating point number for v, or NaN if not numeric
 */
DUNGPUB float ByondValue_GetNum(CByondValue const *v);
/**
 * @param v Pointer to CByondValue
 * @return Reference ID if value is a reference type, or 0 otherwise
 */
DUNGPUB u4c ByondValue_GetRef(CByondValue const *v);

/**
 * Fills a CByondValue struct with a floating point number.
 * @param v Pointer to CByondValue
 * @param f Floating point number
 */
DUNGPUB void ByondValue_SetNum(CByondValue *v, float f);
/**
 * Creates a string and sets CByondValue to a reference to that string, and increases the reference count. See REFERENCE COUNTING in byondapi.h.
 * Blocks if not on the main thread. If string creation fails, the struct is set to null.
 * @param v Pointer to CByondValue
 * @param str Null-terminated UTF-8 string
 * @see Byond_AddGetStrId()
 */
DUNGPUB void ByondValue_SetStr(CByondValue *v, char const *str);
/**
 * Fills a CByondValue struct with a reference to a string with a given ID. Does not validate, and does not increase the reference count.
 * If the strid is NONE, it will be changed to 0.
 * @param v Pointer to CByondValue
 * @param strid 4-byte string ID
 * @see Byond_TestRef()
 */
DUNGPUB void ByondValue_SetStrId(CByondValue *v, u4c strid);
/**
 * Fills a CByondValue struct with a reference (object) type. Does not validate.
 * @param v Pointer to CByondValue
 * @param type 1-byte teference type
 * @param ref 4-byte reference ID; for most types, an ID of NONE is invalid
 * @see Byond_TestRef()
 */
DUNGPUB void ByondValue_SetRef(CByondValue *v, ByondValueType type, u4c ref);

/**
 * Compares two values for equality
 * @param a Pointer to CByondValue
 * @param b Pointer to CByondValue
 * @return True if values are equal
 */
DUNGPUB bool ByondValue_Equals(CByondValue const *a, CByondValue const *b);
/**
 * Compares two values for equivalence (a ~= b)
 * Blocks if not on the main thread, unless equivalence can be determined trivially.
 * @param a Pointer to CByondValue
 * @param b Pointer to CByondValue
 * @return True if values are equal
 */
DUNGPUB bool ByondValue_Equiv(CByondValue const *a, CByondValue const *b);

// Other useful structs

struct CByondXYZ {
	s2c x, y, z;	//!< signed 2-byte integer coordinates
	s2c junk;		//!< padding
};

struct CByondPixLoc {
	float x, y;		//!< pixel coordinates as floats
	s2c z;			//!< signed 2-byte integer coordinates
	s2c junk;		//!< padding
};

/*
	In the following functions, anything that fills a result value (e.g.,
	ReadVar, CallProc) will create a reference to the value. See REFERENCE
	COUNTING below for more details.

	In general, if you're not creating any threads of your own and you
	don't need to save a reference for later, you don't need to worry about
	reference counting. The main thread uses temporary references that will
	clean themselves up at tick's end.

	If the validity of a reference is ever in doubt, call Byond_TestRef().

	THREAD SAFETY:

	Anything called outside of the main	thread will block, unless otherwise
	noted.

	Also note that references created outside the main thread are always
	persistent, and must be cleaned up with ByondValue_DecRef().
 */

typedef CByondValue (*ByondCallback)(void *);
/**
 * Runs a function as a callback on the main thread (or right away if already there)
 * All references created from Byondapi calls within your callback are persistent, not temporary, even though your callback runs on the main thread.
 * Blocking is optional. If already on the main thread, the block parameter is meaningless.
 * @param callback Function pointer to CByondValue function(void*)
 * @param data Void pointer (argument to function)
 * @param block True if this call should block while waiting for the callback to finish; false if not
 * @return CByondValue returned by the function (if it blocked; null if not)
 */
DUNGPUB CByondValue Byond_ThreadSync(ByondCallback callback, void *data, bool block=false);

/**
 * Returns a reference to an existing string ID, but does not create a new string ID.
 * Blocks if not on the main thread.
 * @param str Null-terminated string
 * @return ID of string; NONE if string does not exist
 */
DUNGPUB u4c Byond_GetStrId(char const *str);	// does not add a string to the tree if not found; returns NONE if no string match
/**
 * Returns a reference to an existing string ID or creates a new string ID reference.
 * The new string is reference-counted. See REFERENCE COUNTING in byondapi.h for details.
 * Call ByondValue_SeStrId() to use the returned ID in a CByondValue.
 * Blocks if not on the main thread.
 * @param str Null-terminated string
 * @return ID of string; NONE if string creation failed
 */
DUNGPUB u4c Byond_AddGetStrId(char const *str);	// adds a string to the tree if not found

/**
 * Reads an object variable by name.
 * Blocks if not on the main thread.
 * @param loc Object that owns the var
 * @param varname Var name as null-terminated string
 * @param result Pointer to accept result (referenced)
 * @return True on success
 */
DUNGPUB bool Byond_ReadVar(CByondValue const *loc, char const *varname, CByondValue *result);
/**
 * Reads an object variable by the string ID of its var name.
 * ID can be cached ahead of time for performance.
 * Blocks if not on the main thread.
 * @param loc Object that owns the var
 * @param varname Var name as string ID
 * @param result Pointer to accept result (referenced)
 * @return True on success
 * @see Byond_GetStrId()
 */
DUNGPUB bool Byond_ReadVarByStrId(CByondValue const *loc, u4c varname, CByondValue *result);
/**
 * Writes an object variable by name.
 * Blocks if not on the main thread.
 * @param loc Object that owns the var
 * @param varname Var name as null-terminated string
 * @param val New value
 * @return True on success
 */
DUNGPUB bool Byond_WriteVar(CByondValue const *loc, char const *varname, CByondValue const *val);
/**
 * Writes an object variable by the string ID of its var name.
 * ID can be cached ahead of time for performance.
 * Blocks if not on the main thread.
 * @param loc Object that owns the var
 * @param varname Var name as string ID
 * @param val New value
 * @return True on success
 */
DUNGPUB bool Byond_WriteVarByStrId(CByondValue const *loc, u4c varname, CByondValue const *val);

/**
 * Creates an empty list (referenced). Equivalent to list().
 * Blocks if not on the main thread.
 * @param result Result
 * @return True on success
 */
DUNGPUB bool Byond_CreateList(CByondValue *result);

/**
 * Creates a list (referenced) with a given length. Equivalent to new /list(len).
 * Blocks if not on the main thread.
 * @param result Result
 * @param len Length of new list
 * @return True on success
 */
DUNGPUB bool Byond_CreateListLen(CByondValue *result, u4c len);

/**
 * Creates a multi-dimensional list (referenced) with a given size. Equivalent to new/list(sizes[0],sizes[1],...).
 * Blocks if not on the main thread.
 * @param result Result
 * @param sizes Length of each dimension
 * @param dimension Number of dimensions
 * @return True on success
 */
DUNGPUB bool Byond_CreateDimensionalList(CByondValue *result, u4c const *sizes, u4c dimension);

/**
 * Reads items (referenced) from a list.
 * Blocks if not on the main thread. 
 * @param loc The list to read
 * @param list CByondValue array, allocated by caller (can be null if querying length)
 * @param len Pointer to length of array (in items); receives the number of items read on success, or required length of array if not big enough
 * @return True on success; false with *len=0 for failure; false with *len=required size if array is not big enough
 */
DUNGPUB bool Byond_ReadList(CByondValue const *loc, CByondValue *list, u4c *len);

/**
 * Reads items as key,value pairs from an associative list, storing them sequentially as key1, value1, key2, value2, etc.
 * The length of the array is always a multiple of 2.
 * Blocks if not on the main thread. 
 * @param loc The list to read
 * @param list CByondValue array, allocated by caller (can be null if querying length)
 * @param len Pointer to length of array (in items); receives the number of items read on success, or required length of array if not big enough
 * @return True on success; false with *len=0 for failure; false with *len=required size if array is not big enough
 */
DUNGPUB bool Byond_ReadListAssoc(CByondValue const *loc, CByondValue *list, u4c *len);

/**
 * Writes items to a list, in place of old contents.
 * Blocks if not on the main thread. 
 * @param loc The list to fill
 * @param list CByondValue array of items to write
 * @param len Number of items to write
 * @return True on success
 */
DUNGPUB bool Byond_WriteList(CByondValue const *loc, CByondValue const *list, u4c len);

/**
 * Reads an item (referenced) from a list.
 * Blocks if not on the main thread. 
 * @param loc The list
 * @param idx The index in the list (may be a number, or a non-number if using associative lists)
 * @param result Pointer to accept result
 * @return True on success
 */
DUNGPUB bool Byond_ReadListIndex(CByondValue const *loc, CByondValue const *idx, CByondValue *result);
/**
 * Writes an item to a list.
 * Blocks if not on the main thread. 
 * @param loc The list
 * @param idx The index in the list (may be a number, or a non-number if using associative lists)
 * @param val New value
 * @return True on success
 */
DUNGPUB bool Byond_WriteListIndex(CByondValue const *loc, CByondValue const *idx, CByondValue const *val);
/**
 * Removes items from a list
 * Note: Unlike in DM, if one of the items to remove is a list itself, this will treat it as an item instead of looking at the list's contents.
 * This call will never fail. It will simply return 0 if nothing was removed.
 * Blocks if not on the main thread. 
 * @param loc The list
 * @param items An array of items to remove
 * @param count The number of items
 * @return Number of items removed
 */
DUNGPUB u4c Byond_ListRemove(CByondValue const *loc, CByondValue const *items, u4c count);

/**
 * Reads from a BYOND pointer
 * Blocks if not on the main thread. 
 * @param ptr The BYOND pointer
 * @param result Pointer to accept result (referenced)
 * @return True on success
 */
DUNGPUB bool Byond_ReadPointer(CByondValue const *ptr, CByondValue *result);
/**
 * Writes to a BYOND pointer
 * Blocks if not on the main thread. 
 * @param ptr The BYOND pointer
 * @param val New value
 * @return True on success
 */
DUNGPUB bool Byond_WritePointer(CByondValue const *ptr, CByondValue const *val);

/*
	Proc calls:

	arg is an array of arguments; can be null arg_count is 0.

	The call is implicitly a waitfor=0 call; if the callee sleeps it will return
	immediately and finish later.
 */

/**
 * Calls an object proc by name.
 * The proc call is treated as waitfor=0 and will return immediately on sleep.
 * Blocks if not on the main thread. 
 * @param src The object that owns the proc
 * @param name Proc name as null-terminated string
 * @param arg Array of arguments
 * @param arg_count Number of arguments
 * @param result Pointer to accept result (referenced)
 * @return True on success
 */
DUNGPUB bool Byond_CallProc(CByondValue const *src, char const *name, CByondValue const *arg, u4c arg_count, CByondValue *result);
/**
 * Calls an object proc by name, where the name is a string ID.
 * The proc call is treated as waitfor=0 and will return immediately on sleep.
 * Blocks if not on the main thread. 
 * @param src The object that owns the proc
 * @param name Proc name as string ID
 * @param arg Array of arguments
 * @param arg_count Number of arguments
 * @param result Pointer to accept result (referenced)
 * @return True on success
 * @see Byond_GetStrId()
 */
DUNGPUB bool Byond_CallProcByStrId(CByondValue const *src, u4c name, CByondValue const *arg, u4c arg_count, CByondValue *result);

/**
 * Calls a global proc by name.
 * The proc call is treated as waitfor=0 and will return immediately on sleep.
 * Blocks if not on the main thread. 
 * @param name Proc name as null-terminated string
 * @param arg Array of arguments
 * @param arg_count  Number of arguments
 * @param result Pointer to accept result (referenced)
 * @return True on success
 */
DUNGPUB bool Byond_CallGlobalProc(char const *name, CByondValue const *arg, u4c arg_count, CByondValue *result);	// result MUST be initialized first!
/**
 * Calls a global proc by name, where the name is a string ID.
 * The proc call is treated as waitfor=0 and will return immediately on sleep.
 * Blocks if not on the main thread. 
 * @param name Proc name as string ID
 * @param arg Array of arguments
 * @param arg_count Number of arguments
 * @param result Pointer to accept result
 * @return True on success
 * @see Byond_GetStrId()
 */
DUNGPUB bool Byond_CallGlobalProcByStrId(u4c name, CByondValue const *arg, u4c arg_count, CByondValue *result);	// result MUST be initialized first!

/**
 * Uses BYOND's internals to represent a value as text
 * If the value isn't already a string, this acts like the value is embedded in a string via brackets.
 * Note: This means objects with lowercase names get "the" prepended, and the operator"" overload can be called.
 * Blocks if not on the main thread. 
 * @param src The value to convert to text
 * @param buf char array, allocated by caller (can be null if querying length)
 * @param buflen Pointer to length of array in bytes; receives the string length (including trailing null) on success, or required length of array if not big enough
 * @return True on success; false with *buflen=0 for failure; false with *buflen=required size if array is not big enough
 */
DUNGPUB bool Byond_ToString(CByondValue const *src, char *buf, u4c *buflen);

/**
 * Returns from a byond,await: call
 * Blocks if not on the main thread. 
 * @param waiting_proc The /callee waiting on a response
 * @param retval The return value to pass back to the waiting proc
 * @return True on success; false if the waiting proc doesn't exist
 */
DUNGPUB bool Byond_Return(CByondValue const *waiting_proc, CByondValue const *retval);

// Other builtins

/**
 * Equivalent to calling block(x1,y1,z1, x2,y2,z2).
 * Blocks if not on the main thread. 
 * @param corner1 One corner of the block
 * @param corner2 Another corner of the block
 * @param list CByondValue array, allocated by caller (can be null if querying length)
 * @param len Pointer to length of array (in items); receives the number of items read on success, or required length of array if not big enough
 * @return True on success; false with *len=0 for failure; false with *len=required size if array is not big enough
 */
DUNGPUB bool Byond_Block(CByondXYZ const *corner1, CByondXYZ const *corner2, CByondValue *list, u4c *len);

/**
 * Equivalent to calling istype(src, text2path(typestr)).
 * Blocks if not on the main thread. 
 * @param src The value
 * @param typestr The type path to check
 * @return True if src is of the given type, false if not
 */
DUNGPUB bool ByondValue_IsType(CByondValue const *src, char const *typestr);

/**
 * Equivalent to calling length(src).
 * Blocks if not on the main thread. 
 * @param src The value
 * @param result Pointer to accept result as a CByondValue (intended for future possible override of length)
 * @return True on success
 */
DUNGPUB bool Byond_Length(CByondValue const *src, CByondValue *result);

/**
 * Equivalent to calling locate(type), or locate(type) in list.
 * Blocks if not on the main thread. 
 * @param type The type to locate
 * @param list The list to locate in; can be a null pointer instead of a CByondValue to locate(type) without a list
 * @param result Pointer to accept result (referenced); can be null if nothing is found
 * @return True on success (including if nothing is found); false on error
 */
DUNGPUB bool Byond_LocateIn(CByondValue const *type, CByondValue const *list, CByondValue *result);

/**
 * Equivalent to calling locate(x,y,z)
 * Blocks if not on the main thread.
 * Result is null if coords are invalid.
 * @param xyz The x,y,z coords
 * @param result Pointer to accept result (NOT referenced; is a turf or null)
 * @return True (always)
 */
DUNGPUB bool Byond_LocateXYZ(CByondXYZ const *xyz, CByondValue *result);

/**
 * Equivalent to calling new type(...)
 * Blocks if not on the main thread. 
 * @param type The type to create (type path or string)
 * @param arg Array of arguments
 * @param arg_count Number of arguments
 * @param result Pointer to accept result (referenced)
 * @return True on success
 */
DUNGPUB bool Byond_New(CByondValue const *type, CByondValue const *arg, u4c arg_count, CByondValue *result);

/**
 * Equivalent to calling new type(arglist)
 * Blocks if not on the main thread. 
 * @param type The type to create (type path or string)
 * @param arglist Arguments, as a reference to an arglist
 * @param result Pointer to accept result (referenced)
 * @return True on success
 */
DUNGPUB bool Byond_NewArglist(CByondValue const *type, CByondValue const *arglist, CByondValue *result);	// result MUST be initialized first!

/**
 * Equivalent to calling refcount(value)
 * Blocks if not on the main thread. 
 * @param src The object to refcount
 * @param result Pointer to accept result
 * @return True on success
 */
DUNGPUB bool Byond_Refcount(CByondValue const *src, u4c *result);	// result MUST be initialized first!

/**
 * Get x,y,z coords of an atom
 * Blocks if not on the main thread. 
 * @param src The object to read
 * @param xyz Pointer to accept CByondXYZ result
 * @return True on success
 */
DUNGPUB bool Byond_XYZ(CByondValue const *src, CByondXYZ *xyz);	// still returns true if the atom is off-map, but xyz will be 0,0,0

/**
 * Get pixloc coords of an atom
 * Blocks if not on the main thread. 
 * @param src The object to read
 * @param pixloc Pointer to accept CByondPixLoc result
 * @return True on success
 */
DUNGPUB bool Byond_PixLoc(CByondValue const *src, CByondPixLoc *pixloc);	// still returns true if the atom is off-map, but pixloc will be 0,0,0

/**
 * Get pixloc coords of an atom based on its bounding box
 * Blocks if not on the main thread. 
 * @param src The object to read
 * @param dir The direction
 * @param pixloc Pointer to accept CByondPixLoc result
 * @return True on success
 */
DUNGPUB bool Byond_BoundPixLoc(CByondValue const *src, u1c dir, CByondPixLoc *pixloc);	// still returns true if the atom is off-map, but pixloc will be 0,0,0

/*
	REFERENCE COUNTING

	BYOND uses reference counting internally with IncRefCount() and
	DecRefCount() functions, so when an object runs out of references it will
	be garabge-collected. Byondapi keeps a separate reference count for
	temporary and persistent references.

	Results from most Byondapi calls, for instance the value that will be
	stored in the result pointer for Byond_ReadVar(), are refcounted.

	Temporary references are made when you call Byondapi on the main thread.
	They will be removed at the end of the current server tick. You can call
	Byond_IncRef() to make a persistent reference if you need to hold onto it
	for longer. You can also get rid of a temporary reference right away by
	calling ByondValue_DecTempRef(), which might trigger garbage collection if
	the object is no longer used.

	Byondapi calls made from other threads, OR during a Byond_ThreadSync()
	callback, create persistent references. Persistent references last until you
	call ByondValue_DecRef(). Calling ByondValue_IncRef() will also increase the
	persistent reference count.

	You MUST remember to call ByondValue_DecRef() to clean up any persistent
	references. Otherwise the objects will remain in memory until you call del()
	on them or the world reboots.

	NOTES:

	These only apply to refcounted types, not null or num. Any runtime errors
	that might happen when an object is garbage-collected as a result of
	ByondValue_DecRef() or ByondValue_DecTempRef() are ignored.

	Turfs are not refcounted in BYOND. Please note that whenever you resize
	the map, turf references change. For this reason, if you have a reference
	to a turf you're better off storing it as CByondXYZ coordinates instead.

	Calling del() from within BYOND will scan Byondapi's references and remove
	them. Any CByondValue structures you have in your library will not be
	cleared or changed by a garbage scan; they will simply become invalid.

	Byond_ThreadSync() is an exception to the temporary reference rule. When
	you call Byond_ThreadSync(), the callback function you supply will be called
	on BYOND's main thread, but any references created in Byondapi calls during
	that callback will be persistent. The reason for this is hopefully obvious:
	The only reason you'd call this function at all is because you're working
	with multiple threads and you would want all references to persist.
 */

/**
 * Increase the persistent reference count of an object used in Byondapi
 * Reminder: Calls only create temporary references when made on the main thread. On other threads, the references are already persistent.
 * Blocks if not on the main thread. 
 * @param src The object to incref
 */
DUNGPUB void ByondValue_IncRef(CByondValue const *src);

/**
 * Mark a persistent reference as no longer in use by Byondapi
 * This is IMPORTANT to call when you make Byondapi calls on another thread, since all the references they create are persistent.
 * This cannot be used for temporary references. See ByondValue_DecTempRef() for those.
 * Blocks if not on the main thread. 
 * @param src The object to decref
 */
DUNGPUB void ByondValue_DecRef(CByondValue const *src);



/**
 * Test if a reference-type CByondValue is valid
 * Blocks if not on the main thread. 
 * @param src Pointer to the reference to test; will be filled with null if the reference is invalid
 * @return True if ref is valid; false if not
 */
// Returns true if the ref is valid.
// Returns false if the ref was not valid and had to be changed to null.
// This only applies to ref types, not null/num/string which are always valid.
DUNGPUB bool Byond_TestRef(CByondValue *src);

/*
	Usage note for Byond_CRASH(): This will throw an exception in BYOND's server
	code which is caught like any other runtime error. It will NOT do any stack
	unwinding in the external library that calls it, so destructors and catch
	blocks will not be called.

	Best practice for using this function is to call it outside of a scope where
	any destructors might be needed.
 */

/**
 * Causes a runtime error to crash the current proc
 * Blocks if not on the main thread.
 * @param message Message to use as the runtime error
 */
DUNGPUB void Byond_CRASH(char const *message);

#ifdef __cplusplus
};	// extern "C"
#endif



#endif	// BYONDAPI_H
