#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

/**
 * Version of the Intrepid SDK API.
 *
 * Format is `major.minor.patch.build`, where each number takes a single byte.
 */
#define IAI_VERSION 459010

/**
 * Defines the return value of the task.
 */
enum IAI_AgentTaskResult {
  IAI_AGENT_TASK_PENDING = 0,
  IAI_AGENT_TASK_FINISHED = 1,
  IAI_AGENT_TASK_ERROR = 2,
};
typedef uint8_t IAI_AgentTaskResult;

enum IAI_ConnectorState {
  IAI_STATE_OFFLINE = 0,
  IAI_STATE_CONNECTING = 1,
  IAI_STATE_CONNECTED = 2,
  IAI_STATE_ERROR = 3,
};
typedef uint8_t IAI_ConnectorState;

/**
 * Defines whether the message was sent successfully or not.
 *
 * Error message is available only if INVALID code is returned.
 */
enum IAI_EventChannelSendStatus {
  IAI_EVENTCHANNEL_SEND_INVALID = 0,
  IAI_EVENTCHANNEL_SEND_OK = 1,
  IAI_EVENTCHANNEL_SEND_FULL = 2,
  IAI_EVENTCHANNEL_SEND_CLOSED = 3,
};
typedef uint8_t IAI_EventChannelSendStatus;

enum IAI_LogLevel {
  IAI_LOGLEVEL_TRACE = 0,
  IAI_LOGLEVEL_DEBUG = 1,
  IAI_LOGLEVEL_INFO = 2,
  IAI_LOGLEVEL_WARN = 3,
  IAI_LOGLEVEL_ERROR = 4,
  IAI_LOGLEVEL_OFF = 5,
};
typedef uint8_t IAI_LogLevel;

/**
 * Defines the container for the data.
 */
enum IAI_PinSchemaContainer {
  /**
   * Single data of a given type.
   */
  IAI_PINSCHEMA_CONTAINER_SINGLE = 32,
  /**
   * Option of a given type (zero or one element).
   */
  IAI_PINSCHEMA_CONTAINER_OPTION = 33,
  /**
   * Array of a given type (zero or more elements).
   */
  IAI_PINSCHEMA_CONTAINER_ARRAY = 34,
  /**
   * Single, option or container; chosen by the user.
   */
  IAI_PINSCHEMA_CONTAINER_ANY = 35,
};
typedef uint8_t IAI_PinSchemaContainer;

/**
 * Defines the kind of data that a pin accepts.
 */
enum IAI_PinSchemaKind {
  /**
   * Pin accepts data of a specific type. That type should
   * be specified in the type_id field.
   */
  IAI_PINSCHEMA_KIND_CONCRETE = 16,
  /**
   * Pin accepts only flows.
   */
  IAI_PINSCHEMA_KIND_FLOW = 17,
  /**
   * Pin accepts data of any type, but that type
   * must be the same across all wildcards for one node.
   */
  IAI_PINSCHEMA_KIND_WILDCARD = 18,
  /**
   * Pin accepts any data except flow, its name and type are
   * determined by the user and not dependent on other pins.
   */
  IAI_PINSCHEMA_KIND_ANY = 19,
  /**
   * Pin accepts any data including flow, its name and type are
   * determined by the user and not dependent on other pins.
   */
  IAI_PINSCHEMA_KIND_ANY_OR_FLOW = 20,
};
typedef uint8_t IAI_PinSchemaKind;

/**
 * Defines how many pins belong to this schema.
 */
enum IAI_PinSchemaMultiplier {
  /**
   * Just a single pin.
   */
  IAI_PINSCHEMA_MULTIPLIER_ONE = 48,
  /**
   * Zero, one or more pins.
   */
  IAI_PINSCHEMA_MULTIPLIER_ZERO_OR_MORE = 49,
};
typedef uint8_t IAI_PinSchemaMultiplier;

/**
 * Is this resource guard valid, and if so, is it read or write.
 */
enum IAI_ResourceLockGuardKind {
  IAI_RESOURCE_LOCK_GUARD_INVALID = 0,
  IAI_RESOURCE_LOCK_GUARD_READ = 1,
  IAI_RESOURCE_LOCK_GUARD_WRITE = 2,
};
typedef uint8_t IAI_ResourceLockGuardKind;

/**
 * Agent executes the graph and sends feedback to the intrepid hub.
 */
typedef struct IAI_Agent IAI_Agent;

/**
 * Agent task is a user-defined async task that runs on the same thread as the agent.
 */
typedef struct IAI_AgentTask IAI_AgentTask;

/**
 * Runtime that can be used to run async tasks backed up by a thread pool.
 */
typedef struct IAI_AsyncRuntime IAI_AsyncRuntime;

typedef struct IAI_Error IAI_Error;

/**
 * Loaded execution graph.
 */
typedef struct IAI_Graph IAI_Graph;

/**
 * Execution graph state (variables, logs, etc.).
 */
typedef struct IAI_GraphState IAI_GraphState;

/**
 * Inspect properties of the graph before it's loaded into the engine.
 */
typedef struct IAI_Inspector IAI_Inspector;

/**
 * User-defined node type.
 */
typedef struct IAI_NodeType IAI_NodeType;

/**
 * Prototype for previously registered opaque data type.
 *
 * Having a prototype allows you to create instances of this data type
 * and get data from existing instances.
 */
typedef struct IAI_OpaqueDataSeed IAI_OpaqueDataSeed;

/**
 * User-defined data type that contains arbitrary data.
 *
 * User has to explicitly define all nodes that manipulate this data type,
 * as well as all necessary traits.
 *
 * Underlying data must be thread-safe.
 */
typedef struct IAI_OpaqueDataType IAI_OpaqueDataType;

/**
 * Portable storage for the info about nodes and types.
 */
typedef struct IAI_RegistryInfo IAI_RegistryInfo;

/**
 * User-defined data type that represents a struct, containing other data types.
 *
 * Nodes that manipulate this data type (`struct/make/xxx`, `struct/break/xxx`)
 * and various traits (default, serialize, etc.) will be automatically created when
 * this data type is registered.
 */
typedef struct IAI_StructDataType IAI_StructDataType;

/**
 * Owned string type, valid UTF-8 with a null terminator.
 *
 * Users should never create this type directly.
 *
 * If user receives it as a IAI function output, it must be later passed into
 * another IAI function (e.g. deallocated with `IAI_String_free`).
 */
typedef char *IAI_String;

/**
 * Manages the connection between all the agents and intrepid hub.
 */
typedef const void *IAI_Connector;

/**
 * A registry of all available nodes and types.
 */
typedef const void *IAI_Registry;

typedef const void *IAI_PluginRegistry;

/**
 * Unique identifier for a data type. Zero value means data type does not exist.
 */
typedef uint64_t IAI_DataTypeId;

/**
 * Reference to a single data value of a dynamic type.
 */
typedef struct IAI_DataRef {
  const void *_private[2];
} IAI_DataRef;

/**
 * Single data value of a dynamic type.
 */
typedef struct IAI_Data {
  struct IAI_DataRef _ref;
} IAI_Data;

/**
 * This event channel is used by async tasks to trigger event emitter nodes.
 */
typedef void *IAI_EventChannel;

/**
 * Handle for waking up a task by notifying its executor that it is ready to be run.
 */
typedef struct IAI_Waker {
  const void *_private[2];
} IAI_Waker;

/**
 * Function that is repeatedly called by the agent.
 *
 * Second argument is waker, which the task should call when new data is available.
 *
 * Return value is typically always 0 (pending) if task lives forever.
 * If a fatal error is occurred, task may choose to return 2 and report
 * error via `IAI_set_error`.
 */
typedef IAI_AgentTaskResult (*IAI_AgentTaskPoll)(void*, const struct IAI_Waker*);

/**
 * Unique identifier for specific node in a graph. Zero value means node does not exist.
 */
typedef uint64_t IAI_NodeId;

/**
 * Function that's called when connector state changes.
 */
typedef void (*IAI_Connector_OnStateChange)(IAI_ConnectorState, void*);

/**
 * A set of two coordinates which may be interpreted as a vector or point in 2d space.
 */
typedef struct IAI_Vec2 {
  double x;
  double y;
} IAI_Vec2;

/**
 * A set of three coordinates which may be interpreted as a point or vector in 3d space, or as a homogeneous 2d vector or point.
 */
typedef struct IAI_Vec3 {
  double x;
  double y;
  double z;
} IAI_Vec3;

/**
 * A set of four coordinates which may be interpreted as a point or vector in 4d space, or as a homogeneous 3d vector or point.
 */
typedef struct IAI_Vec4 {
  double x;
  double y;
  double z;
  double w;
} IAI_Vec4;

/**
 * A bivector (oriented area) in 2d space.
 */
typedef struct IAI_Bivec2 {
  double xy;
} IAI_Bivec2;

/**
 * A bivector (oriented area) in 3d space.
 */
typedef struct IAI_Bivec3 {
  double yz;
  double zx;
  double xy;
} IAI_Bivec3;

/**
 * A rotor (construct that describe and perform rotations) in 2d space.
 */
typedef struct IAI_Rotor2 {
  double s;
  double xy;
} IAI_Rotor2;

/**
 * A rotor (construct that describe and perform rotations) in 3d space.
 */
typedef struct IAI_Rotor3 {
  double s;
  double yz;
  double zx;
  double xy;
} IAI_Rotor3;

/**
 * Debug message (created by debug/log node or similar).
 */
typedef struct IAI_DebugMessage {
  IAI_NodeId node_id;
  const char *message;
} IAI_DebugMessage;

/**
 * List of resources inside current graph execution state.
 */
typedef const void *IAI_ResourceSet;

/**
 * Execution context for a node builder.
 *
 * It contains inputs types, outputs types, constant inputs
 * and other data needed for the node to be built.
 *
 * The context lives only for the duration of the node build, so
 * any pointers into it will become invalid after `builder` finishes.
 */
typedef struct IAI_BuildContext {
  IAI_NodeId node_id;
  const IAI_DataTypeId *input_types;
  const struct IAI_Data *input_values;
  const IAI_DataTypeId *output_types;
  uint32_t inflows_len;
  uint32_t outflows_len;
  uint32_t inputs_len;
  uint32_t outputs_len;
  IAI_Registry registry;
} IAI_BuildContext;

/**
 * Execution context for a node runner.
 *
 * It contains inputs, outputs, and other data needed for the node to run.
 *
 * The context lives only for the duration of the node execution, so
 * any pointers into it will become invalid after `runner` finishes.
 */
typedef struct IAI_RunContext {
  const struct IAI_Data *inputs;
  struct IAI_Data *outputs;
  uint32_t inputs_len;
  uint32_t outputs_len;
} IAI_RunContext;

/**
 * Schema for user-defined input or output pin.
 */
typedef struct IAI_PinSchema {
  /**
   * Defines the data type if kind is concrete (otherwise must be 0).
   */
  IAI_DataTypeId type_id;
  /**
   * Defines the kind of data that a pin accepts.
   */
  IAI_PinSchemaKind kind;
  /**
   * Defines the container for the data.
   */
  IAI_PinSchemaContainer container;
  /**
   * Defines how many pins belong to this schema.
   */
  IAI_PinSchemaMultiplier count;
  /**
   * If true, this pin must have a constant value determined at compile time.
   */
  bool is_const;
} IAI_PinSchema;

/**
 * Function that's called when the graph containing this node is built.
 *
 * This function is called once for every instance of this node in the graph.
 * It may return a user data pointer that will be passed to the runner
 * when this instance of the node is executed.
 */
typedef void (*IAI_NodeBuilder)(struct IAI_BuildContext*);

/**
 * Function that's called when the node is executed.
 *
 * When this function finishes executing, it must either:
 *  - set all the outputs of the node
 *  - or set the result to be an error
 */
typedef void (*IAI_NodeRunner)(struct IAI_RunContext*);

/**
 * Function that's called when graph runner needs to produce a default value
 * for this data type.
 *
 * For example, this happens if an input of this type is not connected to anything.
 */
typedef void *(*IAI_OpaqueDataTypeDefault)(void);

/**
 * Function that serializes this data type into a JSON representation.
 *
 * This function is required for example for networking nodes.
 *
 * Second argument must be set to a function that will be called to deallocate the returned string.
 */
typedef char *(*IAI_OpaqueDataTypeSerialize)(const void*, void(**)(char*));

/**
 * Function that deserializes this data type from a JSON representation.
 *
 * This function is required for example for networking nodes.
 */
typedef void *(*IAI_OpaqueDataTypeDeserialize)(const char*);

/**
 * Function that returns a debug representation of this data type.
 *
 * Arguments:
 *  - ptr: pointer to the data
 *  - alternate: if true, the function can return an alternate representation (e.g. with {:#?} in Rust instead of {:?})
 *  - dispose: must be set to a function that will be called to deallocate the returned string
 */
typedef char *(*IAI_OpaqueDataTypeDebug)(const void*, bool, void(**)(char*));

/**
 * Function that deallocates this data type.
 */
typedef void (*IAI_OpaqueDataTypeDispose)(void*);

/**
 * Reference to a resource in the set.
 */
typedef struct IAI_ResourceLockGuard {
  IAI_ResourceLockGuardKind kind;
  const void *_private[3];
} IAI_ResourceLockGuard;

/**
 * Single resource value of a dynamic type.
 */
typedef struct IAI_Resource {
  const void *_private[2];
} IAI_Resource;

typedef struct IAI_Uuid {
  uint8_t bytes[16];
} IAI_Uuid;

/**
 * Get the version of the Intrepid SDK API that this library was built with.
 *
 * If this library is dynamically linked, it can be useful to check compatibility.
 */
uint32_t IAI_version(void);

/**
 * Pretty-print version number.
 */
IAI_String IAI_format_version(uint32_t v);

/**
 * Create agent and load execution graph from a project in the background.
 *
 * All of its dependencies will be automatically registered. It is adviced
 * to free the registry immediately after this call (as agent keeps
 * reference-counted copy).
 *
 * If `graph` is null, the main graph of the project will be loaded.
 *
 * The `on_finish` callback will be called once when the graph is loaded
 * or failed to load. The `user_data` pointer will be passed to the callback,
 * and it's up to the caller to free it when it's no longer needed.
 *
 * Callback is called from another thread, so it should not block anything,
 * and user data must be thread-safe.
 */
void IAI_Agent_init(const IAI_Connector *connector,
                    int64_t agent_id,
                    const char *graph,
                    const IAI_Registry *registry,
                    const IAI_PluginRegistry *plugins,
                    void (*on_finish)(struct IAI_Agent*, void*),
                    void *user_data);

/**
 * Create agent and load execution graph from a project.
 *
 * See `IAI_Agent_init` for more information.
 *
 * The function will block until the agent is loaded or failed to load.
 */
struct IAI_Agent *IAI_Agent_init_sync(const IAI_Connector *connector,
                                      int64_t agent_id,
                                      const char *graph,
                                      const IAI_Registry *registry,
                                      const IAI_PluginRegistry *plugins);

/**
 * Send a debug message to the server.
 */
bool IAI_Agent_send_debug(struct IAI_Agent *agent, uint64_t timestamp, const char *message);

/**
 * Send an error message to the server.
 *
 * This function does not consume the error, so you'll have to free it manually.
 */
bool IAI_Agent_send_error(struct IAI_Agent *agent,
                          uint64_t timestamp,
                          const struct IAI_Error *error);

/**
 * Register command handler in the connector.
 */
bool IAI_Agent_register_command(struct IAI_Agent *agent,
                                const char *tag,
                                IAI_DataTypeId type_id,
                                bool (*callback)(struct IAI_Data, void*),
                                void *user_data,
                                void (*dispose)(void*));

/**
 * Register telemetry type in the connector.
 */
bool IAI_Agent_register_telemetry(struct IAI_Agent *agent, IAI_DataTypeId type_id);

/**
 * Send telemetry data to the server.
 *
 * Ownership of the data is transferred to this function.
 */
bool IAI_Agent_send_telemetry(struct IAI_Agent *agent,
                              uint64_t timestamp,
                              IAI_DataTypeId type_id,
                              struct IAI_Data data);

/**
 * Execute one cycle of the execution graph.
 *
 * This function implements step-by-step executor used in simulator.
 * Tick rate is given by the user, and timers are executed zero, one or more
 * times per tick.
 */
bool IAI_Agent_execute_tick(struct IAI_Agent *agent, uint64_t timestamp, uint64_t tick_rate);

/**
 * Execute the graph forever.
 *
 * This function implements continuous executor used in the real world.
 * Timers are executed exactly once when their time comes, and ticks are
 * executed at a given rate.
 *
 * Ownership of the agent is transferred to this function, so agent will be
 * automatically deallocated when the function returns.
 */
bool IAI_Agent_execute_forever(struct IAI_Agent *agent, uint64_t tick_rate);

/**
 * Get a reference to the execution graph from the agent.
 *
 * The graph is only valid for the lifetime of the agent.
 */
const struct IAI_Graph *IAI_Agent_get_graph(struct IAI_Agent *agent);

/**
 * Get a reference to the graph state from the agent.
 *
 * The graph state is only valid for the lifetime of the agent.
 */
struct IAI_GraphState *IAI_Agent_get_graph_state(struct IAI_Agent *agent);

/**
 * Create a new event channel that's used by async tasks to trigger event emitter nodes.
 */
IAI_EventChannel IAI_Agent_get_event_channel(struct IAI_Agent *agent);

/**
 * Add a user-defined async task that runs on the same thread as the agent.
 *
 * The ownership of the task is transferred to the agent.
 */
bool IAI_Agent_add_task(struct IAI_Agent *agent, struct IAI_AgentTask *task);

/**
 * Free the agent.
 */
void IAI_Agent_free(struct IAI_Agent *agent);

/**
 * Initialize a new agent task.
 */
struct IAI_AgentTask *IAI_AgentTask_new(IAI_AgentTaskPoll poll,
                                        void *user_data,
                                        void (*dispose)(void*));

/**
 * Try to send a message to trigger an event emitter node.
 *
 * Ownership of the arguments is always transferred to the event channel.
 */
IAI_EventChannelSendStatus IAI_EventChannel_send(IAI_EventChannel channel,
                                                 IAI_NodeId entry,
                                                 uint32_t argc,
                                                 struct IAI_Data *argv);

/**
 * Try to trigger an error on the event emitter node.
 */
IAI_EventChannelSendStatus IAI_EventChannel_send_error(IAI_EventChannel channel,
                                                       IAI_NodeId entry,
                                                       const char *error);

/**
 * Clone this event channel.
 */
IAI_EventChannel IAI_EventChannel_clone(IAI_EventChannel channel);

/**
 * Free the event channel.
 */
void IAI_EventChannel_free(IAI_EventChannel channel);

/**
 * Wake up the task associated with this Waker.
 */
void IAI_Waker_wake_by_ref(const struct IAI_Waker *waker);

/**
 * Create a copy of the Waker.
 *
 * Note that Wakers provided by the library are only valid for the duration
 * of that function, so you must clone a waker if you want to use it later.
 */
struct IAI_Waker IAI_Waker_clone(const struct IAI_Waker *waker);

/**
 * Free a previously created Waker.
 *
 * Note that you should only be freeing a Waker that you cloned previously.
 */
void IAI_Waker_free(struct IAI_Waker waker);

/**
 * Create a new async runtime.
 */
const struct IAI_AsyncRuntime *IAI_AsyncRuntime_new(void);

/**
 * Free the async runtime.
 */
void IAI_AsyncRuntime_free(const struct IAI_AsyncRuntime *runtime);

/**
 * Create connector and load the project from the given URL in the background.
 *
 * The `on_finish` callback will be called once when the project is loaded
 * or failed to load. The `user_data` pointer will be passed to the callback,
 * and it's up to the caller to free it when it's no longer needed.
 *
 * Callback is called from another thread, so it should not block anything,
 * and user data must be thread-safe.
 */
void IAI_Connector_init(const struct IAI_AsyncRuntime *rt,
                        const char *url,
                        void (*on_finish)(IAI_Connector, void*),
                        void *user_data);

/**
 * Create connector and load the project from the given URL.
 *
 * The function will block until the project is loaded or failed to load.
 */
IAI_Connector IAI_Connector_init_sync(const struct IAI_AsyncRuntime *rt, const char *url);

/**
 * DEPRECATED
 */
IAI_ConnectorState IAI_Connector_state(const IAI_Connector *_connector);

/**
 * DEPRECATED
 */
bool IAI_Connector_on_state_change(const IAI_Connector *_connector,
                                   IAI_Connector_OnStateChange _callback,
                                   void *_user_data,
                                   void (*_dispose)(void*));

void IAI_Connector_enable_network_via_hub(const IAI_Connector *_connector, bool _enable);

/**
 * Disconnect from the hub.
 */
void IAI_Connector_disconnect(const IAI_Connector *connector);

/**
 * Reconnect to the hub.
 */
void IAI_Connector_reconnect(const IAI_Connector *connector);

/**
 * Create a shallow copy of a connector (i.e. increment its reference count).
 */
IAI_Connector IAI_Connector_clone(const IAI_Connector *connector);

/**
 * Free the connector and stop all threads it created (or decrement its reference count).
 */
void IAI_Connector_free(IAI_Connector connector);

/**
 * Create a shallow copy of a data value (i.e. increment its reference count).
 */
struct IAI_Data IAI_Data_clone(const struct IAI_Data *data);

/**
 * Free a previously created data value or cloned copy (or decrement its reference count).
 */
void IAI_Data_free(struct IAI_Data data);

/**
 * Create a new data value from a boolean.
 */
struct IAI_Data IAI_Data_from_boolean(bool value);

/**
 * Create a new data value from a floating point.
 */
struct IAI_Data IAI_Data_from_float(double value);

/**
 * Create a new data value from an integer.
 */
struct IAI_Data IAI_Data_from_integer(int64_t value);

/**
 * Create a new data value from a string.
 */
struct IAI_Data IAI_Data_from_string(const char *value);

/**
 * Create a new data value from a text.
 */
struct IAI_Data IAI_Data_from_text(const char *value);

/**
 * Create a new data value from a prototype of an opaque struct.
 */
struct IAI_Data IAI_Data_from_opaque_ptr(void *ptr, const struct IAI_OpaqueDataSeed *proto);

/**
 * Get reference `IAI_DataRef` from `IAI_Data` that you can read data from.
 *
 * It is only valid as long as the original `IAI_Data` is valid.
 */
struct IAI_DataRef IAI_Data_ref(const struct IAI_Data *data);

/**
 * Create a new data value from a 2d vector.
 */
struct IAI_Data IAI_Data_from_vec2(struct IAI_Vec2 value);

/**
 * Create a new data value from a 3d vector.
 */
struct IAI_Data IAI_Data_from_vec3(struct IAI_Vec3 value);

/**
 * Create a new data value from a 4d vector.
 */
struct IAI_Data IAI_Data_from_vec4(struct IAI_Vec4 value);

/**
 * Create a new data value from a 2d bivector.
 */
struct IAI_Data IAI_Data_from_bivec2(struct IAI_Bivec2 value);

/**
 * Create a new data value from a 3d bivector.
 */
struct IAI_Data IAI_Data_from_bivec3(struct IAI_Bivec3 value);

/**
 * Create a new data value from a 2d rotor.
 */
struct IAI_Data IAI_Data_from_rotor2(struct IAI_Rotor2 value);

/**
 * Create a new data value from a 3d rotor.
 */
struct IAI_Data IAI_Data_from_rotor3(struct IAI_Rotor3 value);

/**
 * Check if a data value is a 2d vector.
 */
bool IAI_DataRef_is_vec2(struct IAI_DataRef data);

/**
 * Check if a data value is a 3d vector.
 */
bool IAI_DataRef_is_vec3(struct IAI_DataRef data);

/**
 * Check if a data value is a 4d vector.
 */
bool IAI_DataRef_is_vec4(struct IAI_DataRef data);

/**
 * Check if a data value is a 2d bivector.
 */
bool IAI_DataRef_is_bivec2(struct IAI_DataRef data);

/**
 * Check if a data value is a 3d bivector.
 */
bool IAI_DataRef_is_bivec3(struct IAI_DataRef data);

/**
 * Check if a data value is a 2d rotor.
 */
bool IAI_DataRef_is_rotor2(struct IAI_DataRef data);

/**
 * Check if a data value is a 3d rotor.
 */
bool IAI_DataRef_is_rotor3(struct IAI_DataRef data);

/**
 * Get the value of a 2d vector data.
 */
struct IAI_Vec2 IAI_DataRef_get_vec2(struct IAI_DataRef data);

/**
 * Get the value of a 3d vector data.
 */
struct IAI_Vec3 IAI_DataRef_get_vec3(struct IAI_DataRef data);

/**
 * Get the value of a 4d vector data.
 */
struct IAI_Vec4 IAI_DataRef_get_vec4(struct IAI_DataRef data);

/**
 * Get the value of a 2d bivector data.
 */
struct IAI_Bivec2 IAI_DataRef_get_bivec2(struct IAI_DataRef data);

/**
 * Get the value of a 3d bivector data.
 */
struct IAI_Bivec3 IAI_DataRef_get_bivec3(struct IAI_DataRef data);

/**
 * Get the value of a 2d rotor data.
 */
struct IAI_Rotor2 IAI_DataRef_get_rotor2(struct IAI_DataRef data);

/**
 * Get the value of a 3d rotor data.
 */
struct IAI_Rotor3 IAI_DataRef_get_rotor3(struct IAI_DataRef data);

/**
 * Get debug representation of this data value.
 *
 * If `alternate` is true, the representation will be pretty-printed (i.e. with {:#?} in Rust instead of {:?}).
 */
IAI_String IAI_DataRef_debug_fmt(struct IAI_DataRef data,
                                 bool alternate);

/**
 * Check if a data value is a boolean.
 */
bool IAI_DataRef_is_boolean(struct IAI_DataRef data);

/**
 * Check if a data value is a floating point.
 */
bool IAI_DataRef_is_float(struct IAI_DataRef data);

/**
 * Check if a data value is an integer.
 */
bool IAI_DataRef_is_integer(struct IAI_DataRef data);

/**
 * Check if a data value is a string.
 */
bool IAI_DataRef_is_string(struct IAI_DataRef data);

/**
 * Check if a data value is a text.
 */
bool IAI_DataRef_is_text(struct IAI_DataRef data);

/**
 * Get the boolean value of a data value, or false if it is not a boolean.
 */
bool IAI_DataRef_get_boolean(struct IAI_DataRef data);

/**
 * Get the floating point value of a data value, or 0 if it is not a floating point.
 */
double IAI_DataRef_get_float(struct IAI_DataRef data);

/**
 * Get the integer value of a data value, or 0 if it is not an integer.
 */
int64_t IAI_DataRef_get_integer(struct IAI_DataRef data);

/**
 * Get the string value of a data value, or null if it is not a string.
 */
IAI_String IAI_DataRef_get_string(struct IAI_DataRef data);

/**
 * Get the text value of a data value, or null if it is not a text.
 */
IAI_String IAI_DataRef_get_text(struct IAI_DataRef data);

/**
 * Get pointer to the opaque struct with a given prototype from a data value.
 *
 * The same value may be reused by multiple nodes, so data under this pointer should not be modified.
 */
const void *IAI_DataRef_get_opaque_ptr(struct IAI_DataRef data,
                                       const struct IAI_OpaqueDataSeed *proto);

/**
 * Assuming that data is a struct, get a reference to a specific field of that struct by name.
 *
 * The returned value is only valid for the lifetime of the data value.
 */
struct IAI_DataRef IAI_DataRef_get_field(struct IAI_DataRef data, const char *field);

/**
 * Assuming that data is a struct, get a reference to a specific field of that struct at given position.
 *
 * The returned value is only valid for the lifetime of the data value.
 */
struct IAI_DataRef IAI_DataRef_get_field_at(struct IAI_DataRef data,
                                            uint32_t index);

/**
 * Assuming that data is a struct, get the number of fields in that struct.
 *
 * If data is not a struct, returns 0.
 */
uint32_t IAI_DataRef_get_field_len(struct IAI_DataRef data);

/**
 * Clears and returns the last error message.
 *
 * If there is no error, or error was cleared previously, null is returned.
 */
struct IAI_Error *IAI_last_error(void);

/**
 * Set the last error message.
 *
 * This function should be called by plugins whenever error code is returned.
 */
void IAI_set_error(const char *message);

/**
 * Create a new error message.
 */
struct IAI_Error *IAI_Error_create(const char *message);

/**
 * Get error message.
 */
IAI_String IAI_Error_message(const struct IAI_Error *error);

/**
 * Get error backtrace.
 */
IAI_String IAI_Error_backtrace(const struct IAI_Error *error);

/**
 * Format error message.
 */
IAI_String IAI_Error_format(const struct IAI_Error *error);

/**
 * Free error.
 */
void IAI_Error_free(struct IAI_Error *error);

/**
 * Load an execution graph from a JSON string.
 */
struct IAI_Graph *IAI_Graph_init(const char *json, const IAI_Registry *registry);

/**
 * Run the execution graph starting from the given entry node.
 *
 * Returns true if the graph was executed successfully. In case of an error,
 * false is returned and the error message can be retrieved using `IAI_error`.
 *
 * `argc` and `argv` are used to pass arguments to the entry node.
 * Their ownership is transferred, so they should not be used after this call.
 */
bool IAI_Graph_run(const struct IAI_Graph *graph,
                   struct IAI_GraphState *state,
                   IAI_NodeId entry,
                   uint32_t argc,
                   struct IAI_Data *argv);

/**
 * Count the number of all nodes in the graph.
 */
uint32_t IAI_Graph_nodes_len(const struct IAI_Graph *graph);

/**
 * Count the number of entry nodes in the graph.
 */
uint32_t IAI_Graph_entry_nodes_len(const struct IAI_Graph *graph);

/**
 * Get the list of all nodes in the graph.
 *
 * Supplied buffer must be able to hold at least `IAI_Graph_nodes_len` elements.
 */
void IAI_Graph_nodes(const struct IAI_Graph *graph, IAI_NodeId *buf);

/**
 * Get the list of all entry nodes in the graph.
 *
 * Entry nodes are nodes that you can start the execution from
 * (these are either event nodes or a single function entry node).
 *
 * Supplied buffer must be able to hold at least `IAI_Graph_entry_nodes_len` elements.
 */
void IAI_Graph_entry_nodes(const struct IAI_Graph *graph, IAI_NodeId *buf);

/**
 * Check if graph contains a node with given id.
 */
bool IAI_Graph_node_exists(const struct IAI_Graph *graph, IAI_NodeId node);

/**
 * Retrieve the name of a node (typically UUID) by node id, or null if the node does not exist.
 */
IAI_String IAI_Graph_node_name(const struct IAI_Graph *graph, IAI_NodeId node);

/**
 * Retrieve the type of a node by node id, or null if the node does not exist.
 */
IAI_String IAI_Graph_node_type(const struct IAI_Graph *graph, IAI_NodeId node);

/**
 * Get the number of input flows for a node in the graph.
 */
uint32_t IAI_Graph_node_inflows_len(const struct IAI_Graph *graph, IAI_NodeId node);

/**
 * Get the number of output flows for a node in the graph.
 */
uint32_t IAI_Graph_node_outflows_len(const struct IAI_Graph *graph, IAI_NodeId node);

/**
 * Get the number of data inputs for a node in the graph.
 */
uint32_t IAI_Graph_node_inputs_len(const struct IAI_Graph *graph, IAI_NodeId node);

/**
 * Get the number of data outputs for a node in the graph.
 */
uint32_t IAI_Graph_node_outputs_len(const struct IAI_Graph *graph, IAI_NodeId node);

/**
 * Get the type of a data input for a node in the graph.
 */
IAI_DataTypeId IAI_Graph_node_input_type(const struct IAI_Graph *graph,
                                         IAI_NodeId node,
                                         uint32_t index);

/**
 * Get the type of a data output for a node in the graph.
 */
IAI_DataTypeId IAI_Graph_node_output_type(const struct IAI_Graph *graph,
                                          IAI_NodeId node,
                                          uint32_t index);

/**
 * Get the const value of an input for a node in the graph.
 */
struct IAI_Data IAI_Graph_node_input_const_value(const struct IAI_Graph *graph,
                                                 IAI_NodeId node,
                                                 uint32_t index);

/**
 * Trigger error as if it was thrown by a specific node.
 */
void IAI_Graph_trigger_error(const struct IAI_Graph *graph, IAI_NodeId node, const char *message);

/**
 * Get a copy of the registry this graph was created with.
 *
 * Registry is reference-counted, so lifetime is not tied to the graph.
 * The caller is responsible for freeing the returned copy.
 */
IAI_Registry IAI_Graph_get_registry(const struct IAI_Graph *graph);

/**
 * Free a previously created graph.
 */
void IAI_Graph_free(struct IAI_Graph *graph);

/**
 * Create a new execution state for a graph.
 */
struct IAI_GraphState *IAI_GraphState_init(const struct IAI_Graph *graph);

/**
 * Free a previously created execution state.
 */
void IAI_GraphState_free(struct IAI_GraphState *state);

/**
 * Get debug messages from the state.
 */
struct IAI_DebugMessage *IAI_GraphState_debug_messages_next(struct IAI_GraphState *state);

/**
 * Get a copy of the resource container.
 *
 * Resources are a part of graph state and are used to provide context
 * for the execution of the nodes.
 *
 * You must call `IAI_ResourceSet_free` to free the returned resource set.
 */
IAI_ResourceSet IAI_GraphState_get_resource_set(struct IAI_GraphState *state);

/**
 * Free a previously retrieved debug message.
 */
void IAI_DebugMessage_free(struct IAI_DebugMessage *message);

/**
 * Create a new empty graph.
 */
struct IAI_Inspector *IAI_Inspector_empty(void);

/**
 * Load an execution graph from a JSON string.
 */
struct IAI_Inspector *IAI_Inspector_load(const char *json);

/**
 * Get the number of nodes in the graph.
 */
uint32_t IAI_Inspector_nodes_len(const struct IAI_Inspector *inspector);

/**
 * Get the list of all nodes in the graph.
 *
 * Supplied buffer must be able to hold at least `IAI_Graph_nodes_len` elements.
 */
void IAI_Inspector_nodes(const struct IAI_Inspector *inspector, IAI_NodeId *buf);

/**
 * Check if graph contains a node with given id.
 */
bool IAI_Inspector_node_exists(const struct IAI_Inspector *inspector, IAI_NodeId node);

/**
 * Retrieve the name of a node (typically UUID) by node id, or null if the node does not exist.
 */
IAI_String IAI_Inspector_node_name(const struct IAI_Inspector *inspector, IAI_NodeId node);

/**
 * Retrieve the type of a node by node id, or null if the node does not exist.
 */
IAI_String IAI_Inspector_node_type(const struct IAI_Inspector *inspector, IAI_NodeId node);

/**
 * Get the number of data inputs for a node in the graph.
 */
uint32_t IAI_Inspector_node_inputs_len(const struct IAI_Inspector *inspector, IAI_NodeId node);

/**
 * Get the number of data outputs for a node in the graph.
 */
uint32_t IAI_Inspector_node_outputs_len(const struct IAI_Inspector *inspector, IAI_NodeId node);

/**
 * Get the name of an input for a node in the graph.
 */
IAI_String IAI_Inspector_node_input_name(const struct IAI_Inspector *inspector,
                                         IAI_NodeId node,
                                         uint32_t index);

/**
 * Get the name of an output for a node in the graph.
 */
IAI_String IAI_Inspector_node_output_name(const struct IAI_Inspector *inspector,
                                          IAI_NodeId node,
                                          uint32_t index);

/**
 * Get the type of an input for a node in the graph.
 */
IAI_String IAI_Inspector_node_input_type(const struct IAI_Inspector *inspector,
                                         IAI_NodeId node,
                                         uint32_t index);

/**
 * Get the type of a data output for a node in the graph.
 */
IAI_String IAI_Inspector_node_output_type(const struct IAI_Inspector *inspector,
                                          IAI_NodeId node,
                                          uint32_t index);

/**
 * Get the const value of an input for nth node in the graph.
 */
char *IAI_Inspector_node_input_const_value(const struct IAI_Inspector *inspector,
                                           IAI_NodeId node,
                                           uint32_t index);

/**
 * Trigger error as if it was thrown by a specific node.
 */
void IAI_Inspector_trigger_error(const struct IAI_Inspector *inspector,
                                 IAI_NodeId node,
                                 const char *message);

/**
 * Save loaded execution graph back to a JSON string.
 */
IAI_String IAI_Inspector_save(const struct IAI_Inspector *inspector);

/**
 * Free a previously created graph inspector.
 */
void IAI_Inspector_free(struct IAI_Inspector *inspector);

/**
 * Initialize the stdout logger.
 *
 * At this moment, it can only be initialized once.
 */
bool IAI_stdout_logger_init(IAI_LogLevel level);

/**
 * Get the user data for this node type.
 */
void *IAI_BuildContext_get_user_data(struct IAI_BuildContext *ctx);

/**
 * Set the user data for this specific instance of the node.
 */
void IAI_BuildContext_set_instance_data(struct IAI_BuildContext *ctx,
                                        void *ptr,
                                        void (*dispose)(void*));

/**
 * Get the id of the node being built. If it's inside a macro, path to the macro is included.
 */
IAI_String IAI_BuildContext_get_node_id(struct IAI_BuildContext *ctx);

/**
 * Get a reference to the resource container.
 *
 * Resources are a part of graph state and are used to provide context
 * for the execution of the nodes.
 *
 * You must call `IAI_ResourceSet_free` to free the returned resource set.
 */
IAI_ResourceSet IAI_BuildContext_get_resource_set(struct IAI_BuildContext *ctx);

/**
 * Set the result of the node execution to be an error.
 */
void IAI_BuildContext_set_error(struct IAI_BuildContext *ctx, const char *error);

/**
 * Create a new event channel that's used by async tasks to trigger event emitter nodes.
 */
IAI_EventChannel IAI_BuildContext_get_event_channel(struct IAI_BuildContext *ctx);

/**
 * Add a user-defined async task that runs on the same thread as the agent.
 *
 * The ownership of the task is transferred to the context.
 */
bool IAI_BuildContext_add_task(struct IAI_BuildContext *ctx, struct IAI_AgentTask *task);

/**
 * Get the user data for this specific instance of the node.
 */
void *IAI_RunContext_get_instance_data(struct IAI_RunContext *ctx);

/**
 * Emit a debug message from the node.
 */
void IAI_RunContext_debug_message(struct IAI_RunContext *ctx, const char *message);

/**
 * Get a reference to the resource container.
 *
 * Resources are a part of graph state and are used to provide context
 * for the execution of the nodes.
 *
 * You must call `IAI_ResourceSet_free` to free the returned resource set.
 */
IAI_ResourceSet IAI_RunContext_get_resource_set(struct IAI_RunContext *ctx);

/**
 * Continue the flow with the given flow index (0 by default).
 */
bool IAI_RunContext_continue_flow(struct IAI_RunContext *ctx, uint32_t flow_index);

/**
 * Set the result of the node execution to be an error.
 */
void IAI_RunContext_set_error(struct IAI_RunContext *ctx, const char *error);

/**
 * Create a new user-defined node type.
 *
 * This definition is created by the user and later inserted into a registry,
 * which automatically frees it when the registry is freed.
 */
struct IAI_NodeType *IAI_NodeType_new(const char *node_type);

/**
 * Set the label for a user-defined node type.
 */
void IAI_NodeType_set_label(struct IAI_NodeType *node, const char *label);

/**
 * Set the description for a user-defined node type.
 */
void IAI_NodeType_set_description(struct IAI_NodeType *node, const char *description);

/**
 * Add a new input with the given type, label and description.
 */
void IAI_NodeType_add_input(struct IAI_NodeType *node,
                            IAI_DataTypeId type_id,
                            const char *label,
                            const char *description);

/**
 * Add a new output with the given type, label and description.
 */
void IAI_NodeType_add_output(struct IAI_NodeType *node,
                             IAI_DataTypeId type_id,
                             const char *label,
                             const char *description);

/**
 * Add a schema for user-defined inputs.
 */
bool IAI_NodeType_add_input_schema(struct IAI_NodeType *node,
                                   struct IAI_PinSchema schema,
                                   const char *label,
                                   const char *description);

/**
 * Add a schema for user-defined outputs.
 */
bool IAI_NodeType_add_output_schema(struct IAI_NodeType *node,
                                    struct IAI_PinSchema schema,
                                    const char *label,
                                    const char *description);

/**
 * Set the user data for this node type.
 */
void IAI_NodeType_set_user_data(struct IAI_NodeType *node, void *ptr, void (*dispose)(void*));

/**
 * Set the builder function for a user-defined node type.
 */
void IAI_NodeType_set_builder(struct IAI_NodeType *node, IAI_NodeBuilder builder);

/**
 * Set user-defined type as an event emitter instead of a regular node.
 *
 * Event emitter nodes are not executed and don't have a runner. Instead,
 * their outputs are triggered directly with IAI_Graph_run.
 */
void IAI_NodeType_set_event_emitter(struct IAI_NodeType *node);

/**
 * Set the runner function for a user-defined node type.
 */
void IAI_NodeType_set_runner(struct IAI_NodeType *node, IAI_NodeRunner runner);

/**
 * Free a user-defined node type. Users usually pass ownership to the registry and never call this function.
 */
void IAI_NodeType_free(struct IAI_NodeType *node);

/**
 * Create a new user-defined opaque data type.
 *
 * This definition is created by the user and later inserted into a registry,
 * which automatically frees it when the registry is freed.
 */
struct IAI_OpaqueDataType *IAI_OpaqueDataType_new(const char *name);

/**
 * Set the description for a user-defined opaque data type.
 */
void IAI_OpaqueDataType_set_description(struct IAI_OpaqueDataType *data_type,
                                        const char *description);

/**
 * Set pre-allocated data type id from `IAI_Registry_allocate_type_id` function.
 *
 * If you don't call this function, a new id will be allocated when the type is registered.
 */
void IAI_OpaqueDataType_set_type_id(struct IAI_OpaqueDataType *data_type, IAI_DataTypeId type_id);

/**
 * Free a user-defined opaque data type. Users usually pass ownership to the registry and never call this function.
 */
void IAI_OpaqueDataType_free(struct IAI_OpaqueDataType *data_type);

/**
 * Set the default function for opaque data type.
 */
void IAI_OpaqueDataType_set_default(struct IAI_OpaqueDataType *data_type,
                                    IAI_OpaqueDataTypeDefault fn_default);

/**
 * Set the serialize function for opaque data type.
 */
void IAI_OpaqueDataType_set_serialize(struct IAI_OpaqueDataType *data_type,
                                      IAI_OpaqueDataTypeSerialize fn_serialize);

/**
 * Set the deserialize function for opaque data type.
 */
void IAI_OpaqueDataType_set_deserialize(struct IAI_OpaqueDataType *data_type,
                                        IAI_OpaqueDataTypeDeserialize fn_deserialize);

/**
 * Set the debug function for opaque data type.
 */
void IAI_OpaqueDataType_set_debug(struct IAI_OpaqueDataType *data_type,
                                  IAI_OpaqueDataTypeDebug fn_debug);

/**
 * Set the dispose function for opaque data type.
 */
void IAI_OpaqueDataType_set_dispose(struct IAI_OpaqueDataType *data_type,
                                    IAI_OpaqueDataTypeDispose fn_dispose);

/**
 * Free a prototype copy received from registry.
 */
void IAI_OpaqueDataSeed_free(struct IAI_OpaqueDataSeed *ptr);

/**
 * Create a new plugin registry.
 */
IAI_PluginRegistry IAI_PluginRegistry_new(void);

/**
 * Add an extension from a shared library.
 */
void IAI_PluginRegistry_add_dylib(const IAI_PluginRegistry *plugins, const char *path);

/**
 * Add an extension from a remote server.
 */
void IAI_PluginRegistry_add_http(const IAI_PluginRegistry *plugins, const char *url);

/**
 * Load all plugins into a registry in the background.
 *
 * The `on_finish` callback will be called once when all plugins are loaded
 * or failed to load. The `user_data` pointer will be passed to the callback,
 * and it's up to the caller to free it when it's no longer needed.
 */
void IAI_PluginRegistry_load_all(const IAI_PluginRegistry *plugins,
                                 const IAI_Registry *registry,
                                 const struct IAI_AsyncRuntime *rt,
                                 void (*on_finish)(bool, void*),
                                 void *user_data);

/**
 * Load all plugins into a registry.
 */
bool IAI_PluginRegistry_load_all_sync(const IAI_PluginRegistry *plugins,
                                      const IAI_Registry *registry);

/**
 * Create a shallow copy of a plugin registry (i.e. increment its reference count).
 */
IAI_PluginRegistry IAI_PluginRegistry_clone(const IAI_PluginRegistry *plugins);

/**
 * Free previously created plugin registry or cloned copy (or decrement its reference count).
 */
void IAI_PluginRegistry_free(IAI_PluginRegistry plugins);

/**
 * Create a new registry containing some default nodes (e.g. arithmetic, logic, etc.).
 */
IAI_Registry IAI_Registry_init(void);

/**
 * Create a shallow copy of a registry (i.e. increment its reference count).
 */
IAI_Registry IAI_Registry_clone(const IAI_Registry *registry);

/**
 * Free previously created registry or cloned copy (or decrement its reference count).
 */
void IAI_Registry_free(IAI_Registry registry);

/**
 * Add a new macro node into the registry.
 */
bool IAI_Registry_register_macro(const IAI_Registry *registry, const char *json);

/**
 * Add a new node type into the registry.
 *
 * Ownership of the node type is transferred to the registry, so this node must
 * not be used anywhere in user code afterwards.
 */
void IAI_Registry_register_node(const IAI_Registry *registry, struct IAI_NodeType *node);

/**
 * Get type id for a previously registered data type (e.g. `string`),
 * container (e.g. `array<string>`) or a special type (e.g. `flow`).
 */
IAI_DataTypeId IAI_Registry_get_type_id(const IAI_Registry *registry, const char *name);

/**
 * Pre-allocate a new data type id (useful for self-referential structs).
 */
IAI_DataTypeId IAI_Registry_allocate_type_id(const IAI_Registry *registry, const char *name);

/**
 * Add a new opaque data type into the registry.
 *
 * Ownership of the data type is transferred to the registry, so this node must
 * not be used anywhere in user code afterwards.
 */
IAI_DataTypeId IAI_Registry_register_opaque_type(const IAI_Registry *registry,
                                                 struct IAI_OpaqueDataType *data_type);

/**
 * Add a new struct data type into the registry.
 *
 * Ownership of the data type is transferred to the registry, so this node must
 * not be used anywhere in user code afterwards.
 */
IAI_DataTypeId IAI_Registry_register_struct_type(const IAI_Registry *registry,
                                                 struct IAI_StructDataType *data_type);

/**
 * Get prototype for previously registered opaque data type.
 */
struct IAI_OpaqueDataSeed *IAI_Registry_get_data_seed(const IAI_Registry *registry,
                                                      IAI_DataTypeId type_id);

/**
 * Format a data type name for a given type id.
 */
IAI_String IAI_Registry_format_type_name(const IAI_Registry *registry, IAI_DataTypeId type_id);

/**
 * Register network nodes. This is done automatically by Agent, so you probably don't ever need this.
 * DEPRECATED
 */
void IAI_Registry_add_network_nodes(const IAI_Registry *_registry);

/**
 * Create a new storage from a registry.
 *
 * `agent_type` is an optional argument specifying the type of the agent
 * that this registry was created by.
 */
struct IAI_RegistryInfo *IAI_RegistryInfo_init(const IAI_Registry *registry,
                                               const char *agent_type);

/**
 * Create a copy of the registry info storage.
 */
struct IAI_RegistryInfo *IAI_RegistryInfo_clone(const struct IAI_RegistryInfo *info);

/**
 * Merge two storages from two different agents into one.
 *
 * This operation automatically frees memory for both inputs, so the caller should not
 * use or free them after the call (will still need to free the output though).
 */
struct IAI_RegistryInfo *IAI_RegistryInfo_merge(struct IAI_RegistryInfo *info1,
                                                struct IAI_RegistryInfo *info2);

/**
 * Save the storage to a JSON string.
 */
char *IAI_RegistryInfo_save(const struct IAI_RegistryInfo *info);

/**
 * Free the memory of a registry info.
 */
void IAI_RegistryInfo_free(struct IAI_RegistryInfo *info);

/**
 * Get the user data for this mutex guard.
 *
 * This is the same pointer that was passed to `IAI_Resource_new`,
 * and it can be null if resource was created in a different way.
 */
void *IAI_ResourceLockGuard_get_user_data(const struct IAI_ResourceLockGuard *guard);

/**
 * Free a previously created resource lock guard.
 */
void IAI_ResourceLockGuard_free(struct IAI_ResourceLockGuard guard);

/**
 * Create a new resource value.
 */
struct IAI_Resource IAI_Resource_new(void *ptr, void (*dispose)(void*));

/**
 * Get the user data for this resource.
 *
 * This is the same pointer that was passed to `IAI_Resource_new`,
 * and it can be null if resource was created in a different way.
 */
void *IAI_Resource_get_user_data(struct IAI_Resource resource);

/**
 * Free a previously created resource value.
 */
void IAI_Resource_free(struct IAI_Resource resource);

/**
 * Check if a resource exists in the set.
 */
bool IAI_ResourceSet_contains(const IAI_ResourceSet *set, struct IAI_Uuid resource_id);

/**
 * Get a read-only reference to a resource from the set.
 *
 * Resource is identified by a pointer to something unique to the resource type (e.g. vtable).
 */
struct IAI_ResourceLockGuard IAI_ResourceSet_read(const IAI_ResourceSet *set,
                                                  struct IAI_Uuid resource_id);

/**
 * Get a mutable reference to a resource from the set.
 *
 * Resource is identified by a pointer to something unique to the resource type (e.g. vtable).
 */
struct IAI_ResourceLockGuard IAI_ResourceSet_write(const IAI_ResourceSet *set,
                                                   struct IAI_Uuid resource_id);

/**
 * Insert a new resource in the set.
 *
 * Resource is identified by a pointer to something unique to the resource type (e.g. vtable).
 *
 * If this resource already exists, it will be replaced, and the old one will be automatically freed.
 */
void IAI_ResourceSet_insert(const IAI_ResourceSet *set,
                            struct IAI_Uuid resource_id,
                            struct IAI_Resource resource);

/**
 * Remove a resource from the set.
 *
 * May deadlock if called when holding any sort of reference into the map.
 */
void IAI_ResourceSet_remove(const IAI_ResourceSet *set, struct IAI_Uuid resource_id);

/**
 * Create a shallow copy of a resource set (i.e. increment its reference count).
 */
IAI_ResourceSet IAI_ResourceSet_clone(const IAI_ResourceSet *set);

/**
 * Free previously created resource set or cloned copy (or decrement its reference count).
 */
void IAI_ResourceSet_free(IAI_ResourceSet set);

/**
 * Free a string previously allocated by this library.
 *
 * This method does nothing if the string is null.
 */
void IAI_String_free(IAI_String string);

/**
 * Create a new user-defined struct.
 *
 * This definition is created by the user and later inserted into a registry,
 * which automatically frees it when the registry is freed.
 */
struct IAI_StructDataType *IAI_StructDataType_new(const char *name);

/**
 * Set the description for a user-defined struct.
 */
void IAI_StructDataType_set_description(struct IAI_StructDataType *data_type,
                                        const char *description);

/**
 * Set pre-allocated data type id from `IAI_Registry_allocate_type_id` function.
 *
 * If you don't call this function, a new id will be allocated when the type is registered.
 */
void IAI_StructDataType_set_type_id(struct IAI_StructDataType *data_type, IAI_DataTypeId type_id);

/**
 * Free a user-defined struct data type. Users usually pass ownership to the registry and never call this function.
 */
void IAI_StructDataType_free(struct IAI_StructDataType *data_type);

/**
 * Add a new field to the user-defined struct.
 */
void IAI_StructDataType_add_field(struct IAI_StructDataType *data_type,
                                  const char *name,
                                  IAI_DataTypeId type_id);
