/* Copyright (c) 2020-2022 The Creators of Simphone

   See the file COPYING.LESSER.txt for copying permission.
*/

#ifndef _SIMAPI_H_
#define _SIMAPI_H_

#ifdef __cplusplus
extern "C" {
#endif

#include "error.h"
#include "logger.h"
#include "utils.h"
#include "contact.h"
#include "proto.h"
#include "api.h"
#include "console.h"

/* access simtype values */

#define sim_get_type(value) (value).typ               /* return value type */
#define sim_get_number(value) (value).num             /* return value of a number */
#define sim_get_length(value) (value).len             /* return string length or number of elements in an array */
#define sim_get_pointer(value) ((char *) (value).str) /* return string pointer */

/* access array elements */

#define sim_array_get(array, idx) (array).arr[idx]                  /* return array element as a simtype */
#define sim_array_set(array, idx, value) (array).arr[idx] = (value) /* set array element to simtype */

#define sim_array_get_number(array, idx) sim_get_number (sim_array_get (array, idx))
#define sim_array_get_pointer(array, idx) sim_get_pointer (sim_array_get (array, idx))

/* create dynamically allocated (and automatically freed) string */

#define sim_string_new(length) _sim_string_new (length, __FUNCTION__, __LINE__) /* leaves space for terminating zero byte */
#define sim_string_free(string) _sim_string_free (string, __FUNCTION__, __LINE__)
#define sim_string_buffer_new(length) _sim_string_buffer_new (length, __FUNCTION__, __LINE__)   /* create SIMBUFFER (binary string) */
#define sim_string_buffer_free(buffer) _sim_string_buffer_free (buffer, __FUNCTION__, __LINE__) /* free SIMBUFFER */

/* expand or truncate (reallocate) buffer to length */

#define sim_string_buffer_append(buffer, oldlen, addlen) \
  _sim_string_buffer_append (buffer, oldlen, addlen, __FUNCTION__, __LINE__)                                            /* return newlen */
#define sim_string_buffer_truncate(buffer, length) _sim_string_buffer_truncate (string, length, __FUNCTION__, __LINE__) /* return string */

/* memcpy. these functions add the terminating 0 but don't count it */

#define sim_string_copy(string) _sim_string_copy (string, __FUNCTION__, __LINE__)
#define sim_string_copy_length(string, length) _sim_string_copy_length (string, length, __FUNCTION__, __LINE__)
#define sim_string_copy_string(string) _sim_string_copy_string (string, __FUNCTION__, __LINE__)

#define sim_string_cat(string1, string2) _sim_string_cat (string1, string2, __FUNCTION__, __LINE__)
#define sim_string_cat_length(string1, length1, string2, length2) \
  _sim_string_cat_length (string1, length1, strgth2, length2, __FUNCTION__, __LINE__)

/* concatenate multiple strings; terminate list with NULL */
#define sim_string_concat(...) _sim_string_concat (__FUNCTION__, __LINE__, __VA_ARGS__)

/* check if two types differ */

#define sim_type_check_diff(value1, value2) _sim_type_check_diff (value1, value2, __FUNCTION__, __LINE__)
#define sim_table_check_diff(table1, table2) sim_type_check_diff (table1, table2)
#define sim_array_check_diff(array1, array2) sim_type_check_diff (array1, array2)

/* create or destroy dynamically allocated array of specified type */

#define sim_array_new(length, type) _sim_array_new (length, type, __FUNCTION__, __LINE__)
#define sim_array_new_type(value) _sim_array_new_type (value, 1, __FUNCTION__, __LINE__) /* create array of one element */
#define sim_array_free(array) _sim_array_free (array, __FUNCTION__, __LINE__)

#define sim_array_new_numbers(length) sim_array_new (length, SIMNUMBER)
#define sim_array_new_strings(length) sim_array_new (length, SIMSTRING)
#define sim_array_new_tables(length) sim_array_new (length, SIMTABLE)
#define sim_array_new_arrays(length) sim_array_new (length, SIMARRAY)

/* return type of array's elements. for array of array, return SIMARRAY */
#define sim_array_get_type(array) _sim_array_get_type (array, __FUNCTION__, __LINE__)

/* expand (reallocate) or delete elements from array */

#define sim_array_append(array, value) _sim_array_append (array, value, __FUNCTION__, __LINE__)           /* consumes value */
#define sim_array_delete(array, idx, count) _sim_array_delete (array, idx, count, __FUNCTION__, __LINE__) /* frees deleted elements */

/* return a full copy of array or table */

#define sim_array_copy(array) _sim_array_copy (array, false, __FUNCTION__, __LINE__)                 /* recursive copy if necessary */
#define sim_type_copy(value) _sim_type_copy (value, false, __FUNCTION__, __LINE__)                   /* use only if value type unknown */
#define sim_table_copy(table, length) _sim_table_copy (table, length, false, __FUNCTION__, __LINE__) /* recursive copy if necessary */

/* return a full copy of array or table (convert pointers to strings) */

#define sim_array_copy_strings(array) _sim_array_copy (array, true, __FUNCTION__, __LINE__)                 /* recursive copy if necessary */
#define sim_type_copy_strings(value) _sim_type_copy (value, true, __FUNCTION__, __LINE__)                   /* use only if value type unknown */
#define sim_table_copy_strings(table, length) _sim_table_copy (table, length, true, __FUNCTION__, __LINE__) /* recursive copy if necessary */

/* create or destroy hash table with length number of buckets */

#define sim_table_new(length) _sim_table_new (length, SIMTABLE_NORMAL, __FUNCTION__, __LINE__)
#define sim_table_new_const(length, ...) \
  _sim_table_new_const (length, SIMTABLE_CONST, __FUNCTION__, __LINE__, __VA_ARGS__)
#define sim_table_new_rand(length) _sim_table_new (length, SIMTABLE_RAND, __FUNCTION__, __LINE__)
#define sim_table_new_long(length) _sim_table_new (length, SIMTABLE_LONG, __FUNCTION__, __LINE__)

#define sim_table_free(table) _sim_table_free (table, __FUNCTION__, __LINE__)
#define sim_table_count(table) _sim_table_count (table, __FUNCTION__, __LINE__) /* return number of elements in a table */

/* lookup key in a table. return value or nil if not found.
   note sim_table_get_key_number and sim_table_get_number return zero if value not found */

#define sim_table_get_key(table, key) _sim_table_get_key (table, key, __FUNCTION__, __LINE__)
#if SIM_TYPE_CHECKS || ! defined(SIM_TYPE_CHECKS) /* return nil if value not of specified type */
#define sim_table_get_key_type(table, key, type) _sim_table_get_key_type (table, key, type, __FUNCTION__, __LINE__)
#else /* no type checking */
#define sim_table_get_key_type(table, key, type) _sim_table_get_key (table, key, __FUNCTION__, __LINE__)
#endif

#define sim_table_get_key_number(table, key) sim_table_get_key_type (table, key, SIMNUMBER).num
#define sim_table_get_key_pointer(table, key) ((char *) sim_table_get_key_type (table, key, SIMSTRING).str)
#define sim_table_get_key_string(table, key) sim_table_get_key_type (table, key, SIMSTRING)
#define sim_table_get_key_table(table, key) sim_table_get_key_type (table, key, SIMTABLE)
#define sim_table_get_key_array_number(table, key) sim_table_get_key_type (table, key, SIMARRAY_NUMBER)
#define sim_table_get_key_array_string(table, key) sim_table_get_key_type (table, key, SIMARRAY_STRING)
#define sim_table_get_key_array_table(table, key) sim_table_get_key_type (table, key, SIMARRAY_TABLE)
#define sim_table_get_key_array_array(table, key) sim_table_get_key_type (table, key, SIMARRAY_ARRAY)

/* lookup key, where key is a pointer to zero-terminated user-managed string. return value or nil if not found */

#define sim_table_get(table, key) sim_table_get_key (table, sim_pointer_new (key))
#define sim_table_get_type(table, key, type) sim_table_get_key_type (table, sim_pointer_new (key), type)

#define sim_table_get_number(table, key) sim_table_get_key_number (table, sim_pointer_new (key))
#define sim_table_get_pointer(table, key) sim_table_get_key_pointer (table, sim_pointer_new (key))
#define sim_table_get_string(table, key) sim_table_get_key_string (table, sim_pointer_new (key))
#define sim_table_get_table(table, key) sim_table_get_key_table (table, sim_pointer_new (key))
#define sim_table_get_array_number(table, key) sim_table_get_key_array_number (table, sim_pointer_new (key))
#define sim_table_get_array_string(table, key) sim_table_get_key_array_string (table, sim_pointer_new (key))
#define sim_table_get_array_table(table, key) sim_table_get_key_array_table (table, sim_pointer_new (key))
#define sim_table_get_array_array(table, key) sim_table_get_key_array_array (table, sim_pointer_new (key))

/* set values to table (possibly overwriting old values) or add values to table (not overwriting old ones).
   note that set and add functions "consume" dynamically allocated keys and values; if you don't want
   them to be automatically freed (by the table), use sim_pointer_new or sim_pointer_new_length to pass a copy. */

/* store value to key (replacing the old one if any). return nil if old value didn't exist, else
   free old value before returning - do not use the return value except for checking if its nil */
#define sim_table_set_key(table, key, value) _sim_table_set_key (table, key, value, __FUNCTION__, __LINE__)

/* store value to key only if key didn't exist. return nil if successful, else return old value */
#define sim_table_add_key(table, key, value) _sim_table_add_key (table, key, value, __FUNCTION__, __LINE__)

/* create and store value of specific type */
#define sim_table_set_key_number(table, key, number) sim_table_set_key (table, key, sim_number_new (number))
#define sim_table_add_key_number(table, key, number) sim_table_add_key (table, key, sim_number_new (number))
#define sim_table_set_key_pointer(table, key, string) sim_table_set_key (table, key, sim_pointer_new (string))
#define sim_table_add_key_pointer(table, key, string) sim_table_add_key (table, key, sim_pointer_new (string))
#define sim_table_set_key_pointer_length(table, key, string, length) \
  sim_table_set_key (table, key, sim_pointer_new_length (string, length))
#define sim_table_add_key_pointer_length(table, key, string, length) \
  sim_table_add_key (table, key, sim_pointer_new_length (string, length))

/* create and store value of specified type, where key is a pointer to zero-terminated (user-managed) string */
#define sim_table_set_number(table, key, number) sim_table_set_key_number (table, sim_pointer_new (key), number)
#define sim_table_add_number(table, key, number) sim_table_add_key_number (table, sim_pointer_new (key), number)
#define sim_table_set_pointer(table, key, string) sim_table_set_key_pointer (table, sim_pointer_new (key), string)
#define sim_table_add_pointer(table, key, string) sim_table_add_key_pointer (table, sim_pointer_new (key), string)
#define sim_table_set_pointer_length(table, key, string, length) \
  sim_table_set_key_pointer_length (table, sim_pointer_new (key), string, length)
#define sim_table_add_pointer_length(table, key, string, length) \
  sim_table_add_key_pointer_length (table, sim_pointer_new (key), string, length)

/* delete key from a table. return old value or nil if key didn't exist.
   if key existed, free old value before returning - do not use the return value except for checking if its nil */
#define sim_table_delete_key(table, key) _sim_table_delete_key (table, key, __FUNCTION__, __LINE__)

/* store or delete value, where key is a pointer to zero-terminated (user-managed, usually constant) string */
#define sim_table_set(table, key, value) sim_table_set_key (table, sim_pointer_new (key), value)
#define sim_table_add(table, key, value) sim_table_add_key (table, sim_pointer_new (key), value)
#define sim_table_delete(table, key) sim_table_delete_key (table, sim_pointer_new (key))

/* delete and return elements without freeing them */

/* detach value from table. frees key but not value. return value or nil if failed. */
#define sim_table_detach_key(table, key) _sim_table_detach_key (table, key, __FUNCTION__, __LINE__)
#define sim_table_detach(table, key) sim_table_detach_key (table, sim_pointer_new (key))

/* these free the value (and return nil) if not of the specified type */
#if SIM_TYPE_CHECKS || ! defined(SIM_TYPE_CHECKS)
#define sim_table_detach_key_type(table, key, type) \
  _sim_table_detach_key_type (table, key, type, __FUNCTION__, __LINE__)
#else
#define sim_table_detach_key_type(table, key, type) _sim_table_detach_key (table, key, __FUNCTION__, __LINE__)
#endif

#define sim_table_detach_key_number(table, key) sim_table_detach_key_type (table, key, SIMNUMBER).num
#define sim_table_detach_key_pointer(table, key) ((char *) sim_table_detach_key_type (table, key, SIMSTRING).str)
#define sim_table_detach_key_string(table, key) sim_table_detach_key_type (table, key, SIMSTRING)
#define sim_table_detach_key_table(table, key) sim_table_detach_key_type (table, key, SIMTABLE)
#define sim_table_detach_key_array_number(table, key) sim_table_detach_key_type (table, key, SIMARRAY_NUMBER)
#define sim_table_detach_key_array_string(table, key) sim_table_detach_key_type (table, key, SIMARRAY_STRING)
#define sim_table_detach_key_array_table(table, key) sim_table_detach_key_type (table, key, SIMARRAY_TABLE)
#define sim_table_detach_key_array_array(table, key) sim_table_detach_key_type (table, key, SIMARRAY_ARRAY)

#define sim_table_detach_number(table, key) sim_table_detach_key_number (table, sim_pointer_new (key))
#define sim_table_detach_pointer(table, key) sim_table_detach_key_pointer (table, sim_pointer_new (key))
#define sim_table_detach_string(table, key) sim_table_detach_key_string (table, sim_pointer_new (key))
#define sim_table_detach_table(table, key) sim_table_detach_key_table (table, sim_pointer_new (key))
#define sim_table_detach_array_number(table, key) sim_table_detach_key_array_number (table, sim_pointer_new (key))
#define sim_table_detach_array_string(table, key) sim_table_detach_key_array_string (table, sim_pointer_new (key))
#define sim_table_detach_array_table(table, key) sim_table_detach_key_array_table (table, sim_pointer_new (key))
#define sim_table_detach_array_array(table, key) sim_table_detach_key_array_array (table, sim_pointer_new (key))

/* detach value from array so it won't be freed with the array. return old value which is not freed */
#define sim_array_detach(array, idx) _sim_array_detach (array, idx, __FUNCTION__, __LINE__)

/* remove all detached values from array in one sweep */
#define sim_array_detach_end(array) _sim_array_detach_end (array, __FUNCTION__, __LINE__)

/* enumerate elements of a table */

/* start enumerating. return false if not a table */
#define sim_table_walk_first(context, table) _sim_table_walk_first (context, table, __FUNCTION__, __LINE__)

/* return next value (and key) or nil if no more elements. if key = NULL, don't return key */
#if SIM_TYPE_CHECKS || ! defined(SIM_TYPE_CHECKS) /* return nil if value not of specified type */
#define sim_table_walk_next_type(context, key, type) \
  _sim_table_walk_next_type (context, key, type, __FUNCTION__, __LINE__)
#else /* no type checking */
#define sim_table_walk_next_type(context, key, type) sim_table_walk_next (context, key)
#endif

#define sim_table_walk_next_number(table, key) sim_table_walk_next_type (table, key, SIMNUMBER)
#define sim_table_walk_next_pointer(table, key) ((char *) sim_table_walk_next_type (table, key, SIMSTRING).str)
#define sim_table_walk_next_string(table, key) sim_table_walk_next_type (table, key, SIMSTRING)
#define sim_table_walk_next_table(table, key) sim_table_walk_next_type (table, key, SIMTABLE)
#define sim_table_walk_next_array_number(table, key) sim_table_walk_next_type (table, key, SIMARRAY_NUMBER)
#define sim_table_walk_next_array_string(table, key) sim_table_walk_next_type (table, key, SIMARRAY_STRING)
#define sim_table_walk_next_array_table(table, key) sim_table_walk_next_type (table, key, SIMARRAY_TABLE)
#define sim_table_walk_next_array_array(table, key) sim_table_walk_next_type (table, key, SIMARRAY_ARRAY)

/* free value of any type - use only if type of value is unknown */

#define sim_type_free(value) _sim_type_free (value, __FUNCTION__, __LINE__)

/* convert table to and from byte stream */

/* return number of bytes in converted simtype without converting it */
#define sim_type_size(value) _sim_type_size (value, version, __FUNCTION__, __LINE__)
#define sim_table_size sim_type_size

/* convert byte stream to table. return nil if error occurred */
#define sim_table_read(proto, proto2, reader, context) \
  _sim_table_read (proto, proto2, reader, context, __FUNCTION__, __LINE__)

/* convert table to byte stream. return true if successfully converted, false if error occurred */
#define sim_table_write(table, writer, context, version) \
  _sim_table_write (table, writer, context, version, __FUNCTION__, __LINE__)

#ifdef __cplusplus
}
#endif

#endif
