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

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

#ifndef _TABLE_H_
#define _TABLE_H_

#ifndef SIM_LOG_LEVEL
#define SIM_LOG_LEVEL 0
#endif

#define SIM_MODULE_API "api"
#define SIM_MODULE_MAIN "main"
#define SIM_MODULE_SYSTEM "system"
#define SIM_MODULE_TABLE "table"
#define SIM_MODULE_CRYPTO "crypto"
#define SIM_MODULE_CONTACT "contact"

#define LOG_CHECK_ANY_LEVEL_(module, level) ((level) >= SIM_LOG_LEVEL && log_get_level_ (module) <= (level))
#define LOG_CHECK_LEVEL_(level) LOG_CHECK_ANY_LEVEL_ (SIM_MODULE, level)

#define LOG_PROTECT_(module, level) ((level) >= SIM_LOG_LEVEL ? log_protect_ (module, level) : false)
#define LOG_UNPROTECT_() log_unprotect_ ()

#define LOG_CODE_(module, level, CODE) (LOG_PROTECT_ (module, level) ? ((CODE), LOG_UNPROTECT_ (), 0) : 0)
#define LOG_CODE_XTRA_(...) LOG_CODE_ (SIM_MODULE, SIM_LOG_XTRA, __VA_ARGS__)
#define LOG_CODE_DEBUG_(...) LOG_CODE_ (SIM_MODULE, SIM_LOG_DEBUG, __VA_ARGS__)
#define LOG_CODE_NOTE_(...) LOG_CODE_ (SIM_MODULE, SIM_LOG_NOTE, __VA_ARGS__)
#define LOG_CODE_INFO_(...) LOG_CODE_ (SIM_MODULE, SIM_LOG_INFO, __VA_ARGS__)
#define LOG_CODE_WARN_(...) LOG_CODE_ (SIM_MODULE, SIM_LOG_WARN, __VA_ARGS__)
#define LOG_CODE_ERROR_(...) LOG_CODE_ (SIM_MODULE, SIM_LOG_ERROR, __VA_ARGS__)

#define LOG_CODE_FUNCTION_(module, level, function, file, line, ...) \
  LOG_CODE_ (module, level, log_function (module, level, function, file, line, __VA_ARGS__))
#define LOG_CODE_ANY_(module, level, ...) LOG_CODE_ (module, level, log_any (module, level, __VA_ARGS__))

#define LOG_SIMTYPE_(module, level, ...) LOG_CODE_ (module, level, log_simtype (module, level, __VA_ARGS__))
#define LOG_SIMTYPES_(module, level, ...) LOG_CODE_ (module, level, log_simtypes (module, level, __VA_ARGS__))

#define LOG_API_DEBUG_(...) LOG_CODE_FUNCTION_ (SIM_MODULE_API, SIM_LOG_DEBUG, __FUNCTION__, NULL, 0, __VA_ARGS__)
#define LOG_API_INFO_(...) LOG_CODE_FUNCTION_ (SIM_MODULE_API, SIM_LOG_INFO, __FUNCTION__, NULL, 0, __VA_ARGS__)
#define LOG_API_WARN_(error) \
  ((error) == SIM_OK ? SIM_OK : LOG_CODE_ANY_ (SIM_MODULE_API, SIM_LOG_WARN, "%s error %d\n", __FUNCTION__, error))

#define LOG_PRINT_(level, CODE) (log_protect_ (NULL, level) ? ((CODE), log_unprotect_ (), 0) : 0)
#define LOG_PRINT_ANY_(level, ...) LOG_PRINT_ (level, log_any (NULL, level, __VA_ARGS__))

#define LOG_PRINT_XTRA_(...) LOG_PRINT_ANY_ (SIM_LOG_XTRA, __VA_ARGS__)
#define LOG_PRINT_DEBUG_(...) LOG_PRINT_ANY_ (SIM_LOG_DEBUG, __VA_ARGS__)
#define LOG_PRINT_INFO_(...) LOG_PRINT_ANY_ (SIM_LOG_INFO, __VA_ARGS__)
#define LOG_PRINT_NOTE_(...) LOG_PRINT_ANY_ (SIM_LOG_NOTE, __VA_ARGS__)
#define LOG_PRINT_WARN_(...) LOG_PRINT_ANY_ (SIM_LOG_WARN, __VA_ARGS__)
#define LOG_PRINT_ERROR_(...) LOG_PRINT_ANY_ (SIM_LOG_ERROR, __VA_ARGS__)
#define LOG_PRINT_INFO_SIMTYPE_(...) LOG_PRINT_ (SIM_LOG_INFO, log_simtype (NULL, SIM_LOG_INFO, __VA_ARGS__))

#define LOG_ANY_(level, ...) LOG_CODE_ANY_ (SIM_MODULE, level, __VA_ARGS__)
#define LOG_XTRA_(...) LOG_ANY_ (SIM_LOG_XTRA, __VA_ARGS__)
#define LOG_DEBUG_(...) LOG_ANY_ (SIM_LOG_DEBUG, __VA_ARGS__)
#define LOG_INFO_(...) LOG_ANY_ (SIM_LOG_INFO, __VA_ARGS__)
#define LOG_NOTE_(...) LOG_ANY_ (SIM_LOG_NOTE, __VA_ARGS__)
#define LOG_WARN_(...) LOG_ANY_ (SIM_LOG_WARN, __VA_ARGS__)
#define LOG_ERROR_(...) LOG_ANY_ (SIM_LOG_ERROR, __VA_ARGS__)

#if SIM_LOG_LEVEL <= SIM_LOG_FATAL
#define LOG_FATAL_(error, ...) log_fatal_ (SIM_MODULE, error, __VA_ARGS__) /* also sends fatal error event */
#define LOG_FATAL_ERROR_(...) log_any_ (SIM_MODULE, SIM_LOG_FATAL, __VA_ARGS__)
#else
#define LOG_FATAL_(error, ...) 0
#define LOG_FATAL_ERROR_(...) 0
#endif

#define LOG_XTRA_SIMTYPE_(...) LOG_SIMTYPE_ (SIM_MODULE, SIM_LOG_XTRA, __VA_ARGS__)
#define LOG_DEBUG_SIMTYPE_(...) LOG_SIMTYPE_ (SIM_MODULE, SIM_LOG_DEBUG, __VA_ARGS__)
#define LOG_INFO_SIMTYPE_(...) LOG_SIMTYPE_ (SIM_MODULE, SIM_LOG_INFO, __VA_ARGS__)
#define LOG_NOTE_SIMTYPE_(...) LOG_SIMTYPE_ (SIM_MODULE, SIM_LOG_NOTE, __VA_ARGS__)
#define LOG_WARN_SIMTYPE_(...) LOG_SIMTYPE_ (SIM_MODULE, SIM_LOG_WARN, __VA_ARGS__)
#define LOG_ERROR_SIMTYPE_(...) LOG_SIMTYPE_ (SIM_MODULE, SIM_LOG_ERROR, __VA_ARGS__)

#define LOG_XTRA_SIMTYPES_(...) LOG_SIMTYPES_ (SIM_MODULE, SIM_LOG_XTRA, __VA_ARGS__)
#define LOG_DEBUG_SIMTYPES_(...) LOG_SIMTYPES_ (SIM_MODULE, SIM_LOG_DEBUG, __VA_ARGS__)
#define LOG_INFO_SIMTYPES_(...) LOG_SIMTYPES_ (SIM_MODULE, SIM_LOG_INFO, __VA_ARGS__)
#define LOG_NOTE_SIMTYPES_(...) LOG_SIMTYPES_ (SIM_MODULE, SIM_LOG_NOTE, __VA_ARGS__)
#define LOG_WARN_SIMTYPES_(...) LOG_SIMTYPES_ (SIM_MODULE, SIM_LOG_WARN, __VA_ARGS__)
#define LOG_ERROR_SIMTYPES_(...) LOG_SIMTYPES_ (SIM_MODULE, SIM_LOG_ERROR, __VA_ARGS__)

#define nil() sim_nil () /* create a nil simtype */

#define number_new(number) sim_number_new (number) /* convert number to simtype */

/* convert string to SIMPOINTER. pointer_new assumes zero-termination.
   these strings are managed by the user (only pointers to them are stored) */

#define pointer_new_len(string, length) sim_pointer_new_length (string, length)
#define pointer_new(string) sim_pointer_new (string)

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

#define string_new(length) _sim_string_new (length, __FUNCTION__, __LINE__)          /* leaves space for terminating zero byte */
#define string_new_pointer(pointer, length) sim_string_new_pointer (pointer, length) /* pointer must have been returned by sim_new */
#define string_free(string) _sim_string_free (string, __FUNCTION__, __LINE__)
#define string_buffer_new(length) _sim_string_buffer_new (length, __FUNCTION__, __LINE__)   /* create SIMBUFFER (binary string) */
#define string_buffer_free(buffer) _sim_string_buffer_free (buffer, __FUNCTION__, __LINE__) /* free SIMBUFFER */

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

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

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

#define string_copy(string) _sim_string_copy (string, __FUNCTION__, __LINE__)
#define string_copy_len(string, length) _sim_string_copy_length (string, length, __FUNCTION__, __LINE__)
#define string_copy_string(string) _sim_string_copy_string (string, __FUNCTION__, __LINE__)

#define string_cat(string1, string2) _sim_string_cat (string1, string2, __FUNCTION__, __LINE__)
#define string_cat_len(string1, length1, string2, length2) \
  _sim_string_cat_length (string1, length1, string2, length2, __FUNCTION__, __LINE__)

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

/* check if two types differ */

#define string_check_diff_len(string1, string2, length) sim_string_check_diff_length (string1, string2, length)
#define string_check_diff(string1, string2) sim_string_check_diff (string1, string2)

#define type_check_diff(value1, value2) _sim_type_check_diff (value1, value2, __FUNCTION__, __LINE__)
#define table_check_diff(table1, table2) type_check_diff (table1, table2)
#define array_check_diff(array1, array2) type_check_diff (array1, array2)

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

#define array_new(length, type) _sim_array_new (length, type, __FUNCTION__, __LINE__)
#define array_new_type(value) _sim_array_new_type (value, 1, __FUNCTION__, __LINE__) /* create array of one element */
#define array_new_types(length, value) _sim_array_new_type (value, length, __FUNCTION__, __LINE__)
#define array_free(array) _sim_array_free (array, __FUNCTION__, __LINE__)

#define array_new_numbers(length) array_new (length, SIMNUMBER)
#define array_new_strings(length) array_new (length, SIMSTRING)
#define array_new_tables(length) array_new (length, SIMTABLE)
#define array_new_arrays(length) array_new (length, SIMARRAY)

/* return type and maximal number of array's elements. for array of array, type is SIMARRAY */

#define array_get_type(array) _sim_array_get_type (array, __FUNCTION__, __LINE__)
#define array_size(array) sim_array_size (array) /* return real (allocated) size of array */

#define array_get_name(array) (array).arr[0].ptr              /* for printf */
#define array_get_line(array) ((unsigned) (array).arr[0].typ) /* for printf */

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

#define array_append(array, value) _sim_array_append (array, value, __FUNCTION__, __LINE__)           /* consumes value */
#define 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 array_copy(array) _sim_array_copy (array, false, __FUNCTION__, __LINE__)                 /* recursive copy if necessary */
#define type_copy(value) _sim_type_copy (value, false, __FUNCTION__, __LINE__)                   /* use only if value type unknown */
#define 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 array_copy_strings(array) _sim_array_copy (array, true, __FUNCTION__, __LINE__)                 /* recursive copy if necessary */
#define type_copy_strings(value) _sim_type_copy (value, true, __FUNCTION__, __LINE__)                   /* use only if value type unknown */
#define 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 table_new(length) _sim_table_new (length, SIMTABLE_NORMAL, __FUNCTION__, __LINE__)
#define table_new_name(length, name) _sim_table_new (length, SIMTABLE_NORMAL, name, 0)
#define table_new_const(length, ...) _sim_table_new_const (length, SIMTABLE_CONST, __FUNCTION__, __LINE__, __VA_ARGS__)
#define table_new_const_name(length, name, ...) _sim_table_new_const (length, SIMTABLE_CONST, name, 0, __VA_ARGS__)
#define table_new_rand(length) _sim_table_new (length, SIMTABLE_RAND, __FUNCTION__, __LINE__)
#define table_new_long(length) _sim_table_new (length, SIMTABLE_LONG, __FUNCTION__, __LINE__)
#define table_new_long_const(length, ...) \
  _sim_table_new_const (length, SIMTABLE_CONST | SIMTABLE_LONG, __FUNCTION__, __LINE__, __VA_ARGS__)
#define table_new_short(length) _sim_table_new (length, SIMTABLE_SHORT, __FUNCTION__, __LINE__)

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

/* hash function */

unsigned table_init_hash (unsigned salt);
unsigned sim_table_hash (const void *pointer, unsigned length, unsigned salt);

/* return type of a table and (for printf) its name and number */

#define table_get_table_type(table) (table).tbl->valtype

#define table_get_name_elements(elements) (elements)->valptr
#define table_get_line_elements(elements) (elements)->vallen
#define table_get_name(table) table_get_name_elements ((table).tbl)
#define table_get_line(table) table_get_line_elements ((table).tbl)

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

#define 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 table_get_key_type(table, key, type) _sim_table_get_key_type (table, key, type, __FUNCTION__, __LINE__)
#else /* no type checking */
#define table_get_key_type(table, key, type) _sim_table_get_key (table, key, __FUNCTION__, __LINE__)
#endif

#define table_get_key_number(table, key) table_get_key_type (table, key, SIMNUMBER).num
#define table_get_key_pointer(table, key) table_get_key_type (table, key, SIMSTRING).ptr
#define table_get_key_string(table, key) table_get_key_type (table, key, SIMSTRING)
#define table_get_key_table(table, key) table_get_key_type (table, key, SIMTABLE)
#define table_get_key_array_number(table, key) table_get_key_type (table, key, SIMARRAY_NUMBER)
#define table_get_key_array_string(table, key) table_get_key_type (table, key, SIMARRAY_STRING)
#define table_get_key_array_table(table, key) table_get_key_type (table, key, SIMARRAY_TABLE)
#define table_get_key_array_array(table, key) 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 table_get(table, key) table_get_key (table, pointer_new (key))
#define table_get_type(table, key, type) table_get_key_type (table, pointer_new (key), type)

#define table_get_number(table, key) table_get_key_number (table, pointer_new (key))
#define table_get_pointer(table, key) table_get_key_pointer (table, pointer_new (key))
#define table_get_string(table, key) table_get_key_string (table, pointer_new (key))
#define table_get_table(table, key) table_get_key_table (table, pointer_new (key))
#define table_get_array_number(table, key) table_get_key_array_number (table, pointer_new (key))
#define table_get_array_string(table, key) table_get_key_array_string (table, pointer_new (key))
#define table_get_array_table(table, key) table_get_key_array_table (table, pointer_new (key))
#define table_get_array_array(table, key) table_get_key_array_array (table, 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 pointer_new or pointer_new_len to pass a pointer. */

/* 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 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 table_add_key(table, key, value) _sim_table_add_key (table, key, value, __FUNCTION__, __LINE__)

/* create and store value of specific type */
#define table_set_key_number(table, key, number) table_set_key (table, key, number_new (number))
#define table_add_key_number(table, key, number) table_add_key (table, key, number_new (number))
#define table_set_key_pointer(table, key, string) table_set_key (table, key, pointer_new (string))
#define table_add_key_pointer(table, key, string) table_add_key (table, key, pointer_new (string))
#define table_set_key_pointer_len(table, key, string, length) \
  table_set_key (table, key, pointer_new_len (string, length))
#define table_add_key_pointer_len(table, key, string, length) \
  table_add_key (table, key, pointer_new_len (string, length))

/* create and store value of specified type, where key is a pointer to zero-terminated (user-managed) string */
#define table_set_number(table, key, number) table_set_key_number (table, pointer_new (key), number)
#define table_add_number(table, key, number) table_add_key_number (table, pointer_new (key), number)
#define table_set_pointer(table, key, string) table_set_key_pointer (table, pointer_new (key), string)
#define table_add_pointer(table, key, string) table_add_key_pointer (table, pointer_new (key), string)
#define table_set_pointer_len(table, key, string, length) \
  table_set_key_pointer_len (table, pointer_new (key), string, length)
#define table_add_pointer_len(table, key, string, length) \
  table_add_key_pointer_len (table, 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 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 table_set(table, key, value) table_set_key (table, pointer_new (key), value)
#define table_add(table, key, value) table_add_key (table, pointer_new (key), value)
#define table_delete(table, key) table_delete_key (table, 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 table_detach_key(table, key) _sim_table_detach_key (table, key, __FUNCTION__, __LINE__)
#define table_detach(table, key) table_detach_key (table, 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 table_detach_key_type(table, key, type) _sim_table_detach_key_type (table, key, type, __FUNCTION__, __LINE__)
#else
#define table_detach_key_type(table, key, type) _sim_table_detach_key (table, key, __FUNCTION__, __LINE__)
#endif

#define table_detach_key_number(table, key) table_detach_key_type (table, key, SIMNUMBER).num
#define table_detach_key_pointer(table, key) table_detach_key_type (table, key, SIMSTRING).ptr
#define table_detach_key_string(table, key) table_detach_key_type (table, key, SIMSTRING)
#define table_detach_key_table(table, key) table_detach_key_type (table, key, SIMTABLE)
#define table_detach_key_array_number(table, key) table_detach_key_type (table, key, SIMARRAY_NUMBER)
#define table_detach_key_array_string(table, key) table_detach_key_type (table, key, SIMARRAY_STRING)
#define table_detach_key_array_table(table, key) table_detach_key_type (table, key, SIMARRAY_TABLE)
#define table_detach_key_array_array(table, key) table_detach_key_type (table, key, SIMARRAY_ARRAY)

#define table_detach_number(table, key) table_detach_key_number (table, pointer_new (key))
#define table_detach_pointer(table, key) table_detach_key_pointer (table, pointer_new (key))
#define table_detach_string(table, key) table_detach_key_string (table, pointer_new (key))
#define table_detach_table(table, key) table_detach_key_table (table, pointer_new (key))
#define table_detach_array_number(table, key) table_detach_key_array_number (table, pointer_new (key))
#define table_detach_array_string(table, key) table_detach_key_array_string (table, pointer_new (key))
#define table_detach_array_table(table, key) table_detach_key_array_table (table, pointer_new (key))
#define table_detach_array_array(table, key) table_detach_key_array_array (table, pointer_new (key))

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

/* remove all detached values from array in one sweep */
#define 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 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 */
#define table_walk_next(context, key) sim_table_walk_next (context, key)
#if SIM_TYPE_CHECKS || ! defined(SIM_TYPE_CHECKS) /* return nil if value not of specified type */
#define table_walk_next_type(context, key, type) \
  _sim_table_walk_next_type (context, key, type, __FUNCTION__, __LINE__)
#else /* no type checking */
#define table_walk_next_type(context, key, type) sim_table_walk_next (context, key)
#endif

#define table_walk_next_number(table, key) table_walk_next_type (table, key, SIMNUMBER)
#define table_walk_next_pointer(table, key) table_walk_next_type (table, key, SIMSTRING).ptr
#define table_walk_next_string(table, key) table_walk_next_type (table, key, SIMSTRING)
#define table_walk_next_table(table, key) table_walk_next_type (table, key, SIMTABLE)
#define table_walk_next_array_number(table, key) table_walk_next_type (table, key, SIMARRAY_NUMBER)
#define table_walk_next_array_string(table, key) table_walk_next_type (table, key, SIMARRAY_STRING)
#define table_walk_next_array_table(table, key) table_walk_next_type (table, key, SIMARRAY_TABLE)
#define table_walk_next_array_array(table, key) table_walk_next_type (table, key, SIMARRAY_ARRAY)

/* stop enumerating: table_walk_next will keep returning nil after table_walk_last is called */
#define table_walk_last(context) sim_table_walk_last (context)

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

#define 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 type_size(value) _sim_type_size (value, 0, __FUNCTION__, __LINE__)
#define table_size(value, version) _sim_type_size (value, version, __FUNCTION__, __LINE__)

/* convert byte stream to table. return nil if error occurred */
#define 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 table_write(table, writer, context, version) \
  _sim_table_write (table, writer, context, version, __FUNCTION__, __LINE__)

#define SIM_MAX_PACKET_SIZE 32767 /* accept packets no larger than this */

#endif
