#ifndef VALI_H
#define VALI_H

#include <stdbool.h>

struct json_object;

/**
 * A Varlink call error.
 *
 * Resources held by this struct can be released with vali_error_finish().
 */
struct vali_error {
	// The fully-qualified error name
	char *name;
	struct json_object *parameters;
};

/**
 * A call for a client.
 */
struct vali_client_call;

/**
 * Wait for a reply from the service.
 *
 * If out is NULL, the call result is discarded. If out is non-NULL, it's
 * filled with the call result on success, and the caller is responsible for
 * releasing the object.
 *
 * If err is NULL, any Varlink error is discarded. If err is non-NULL, it's
 * always filled: either reset to zero if there is no Varlink error, or
 * populated if the service returns an error. The caller is responsible for
 * releasing the error via vali_error_finish().
 *
 * true is returned on success, false is returned on error (either a Varlink
 * error, or a connection error).
 */
bool vali_client_call_wait(struct vali_client_call *call,
	struct json_object **out, struct vali_error *err);

/**
 * Destroy a client call.
 */
void vali_client_call_destroy(struct vali_client_call *call);

/**
 * Check whether no more replies are expected for this call.
 */
bool vali_client_call_is_done(struct vali_client_call *call);

/**
 * A Varlink client.
 */
struct vali_client;

/**
 * Connect to a Varlink service's Unix socket.
 */
struct vali_client *vali_client_connect_unix(const char *path);

/**
 * Create a new Varlink client from an existing FD.
 */
struct vali_client *vali_client_connect_fd(int fd);

/**
 * Destroy a Varlink client.
 */
void vali_client_destroy(struct vali_client *client);

/**
 * Call a Varlink method.
 *
 * This function blocks until the call reply has been received.
 *
 * The method argument needs to contain the fully qualified method name.
 *
 * in may be NULL if the method takes no parameters.
 *
 * If out is NULL, the call result is discarded. If out is non-NULL, it's
 * filled with the call result on success, and the caller is responsible for
 * releasing the object.
 *
 * If err is NULL, any Varlink error is discarded. If err is non-NULL, it's
 * always filled: either reset to zero if there is no Varlink error, or
 * populated if the service returns an error. The caller is responsible for
 * releasing the error via vali_error_finish().
 *
 * true is returned on success, false is returned on error (either a Varlink
 * error, or a connection error).
 */
bool vali_client_call(struct vali_client *client,
	const char *method, struct json_object *in, struct json_object **out,
	struct vali_error *err);

/**
 * Send a Varlink call request, and indicate that multiple replies are
 * expected.
 *
 * This function blocks until the request has been sent. Only a single call can
 * be pending at a time.
 *
 * The method argument needs to contain the fully qualified method name.
 *
 * in may be NULL if the method takes no parameters.
 *
 * NULL is returned on connection error.
 *
 * After calling this function, clients should call vali_client_call_wait()
 * while vali_client_call_has_more() returns true, then call
 * vali_client_call_destroy().
 */
struct vali_client_call *vali_client_call_more(struct vali_client *client,
	const char *method, struct json_object *in);

/**
 * Send a Varlink call request, and indicate that no reply is expected.
 *
 * See vali_client_call().
 */
bool vali_client_call_oneway(struct vali_client *client,
	const char *method, struct json_object *in);

/**
 * A call for a service.
 */
struct vali_service_call;

/**
 * Retrieve user-specified data attached to the call.
 */
void *vali_service_call_get_user_data(const struct vali_service_call *call);

/**
 * Attach user-specified data to the call.
 */
void vali_service_call_set_user_data(struct vali_service_call *call, void *user_data);

/**
 * Get the service from a call.
 */
struct vali_service *vali_service_call_get_service(struct vali_service_call *call);

/**
 * Get the connection a call was issued from.
 *
 * If the connection has been destroyed (e.g. because the client has
 * disconnected), NULL is returned.
 */
struct vali_service_conn *vali_service_call_get_conn(struct vali_service_call *call);

/**
 * Get the fully-qualified method name.
 */
const char *vali_service_call_get_method(const struct vali_service_call *call);

/**
 * Get the input parameters for a call.
 */
struct json_object *vali_service_call_get_parameters(const struct vali_service_call *call);

/**
 * Check if the client has requested the server to not reply.
 *
 * Call handlers still need to call vali_service_call_close_with_reply() (but
 * can pass in NULL parameters).
 */
bool vali_service_call_is_oneway(const struct vali_service_call *call);

/**
 * Check if the client has requested the server to send multiple replies.
 *
 * Services can use vali_service_call_reply() to send continued replies, and
 * must call vali_service_call_close_with_reply() for the final reply.
 */
bool vali_service_call_is_more(const struct vali_service_call *call);

/**
 * Send the final reply to a call.
 *
 * This transfers ownership of the parameters to this function.
 *
 * The struct vali_service_call pointer becomes invalid.
 */
void vali_service_call_close_with_reply(struct vali_service_call *call, struct json_object *params);

/**
 * Send an error to a call.
 *
 * This transfers ownership of the parameters to this function.
 *
 * The struct vali_service_call pointer becomes invalid.
 */
void vali_service_call_close_with_error(struct vali_service_call *call, const char *error, struct json_object *params);

/**
 * Send a continued reply to a call.
 *
 * This transfers ownership of the parameters to this function.
 *
 * The call must return true for vali_service_call_is_more().
 */
void vali_service_call_reply(struct vali_service_call *call, struct json_object *params);

/**
 * A Varlink service call handler.
 *
 * The user_data field is passed to the callback.
 *
 * A handler must eventually finish the passed-in call, by calling
 * vali_service_call_close_with_reply() or vali_service_call_close_with_error().
 * A handler may return before finishing the call (ie, may asynchronously
 * finish the call).
 */
struct vali_service_call_handler {
	void (*func)(struct vali_service_call *call, void *user_data);
	void *user_data;
};

/**
 * A Varlink service (ie, the server side).
 */
struct vali_service;

/**
 * Create a new Varlink service.
 */
struct vali_service *vali_service_create(void);

/**
 * Destroy a Varlink service.
 */
void vali_service_destroy(struct vali_service *service);

/**
 * Retrieve user-specified data attached to the service.
 */
void *vali_service_get_user_data(struct vali_service *service);

/**
 * Attach user-specified data to the service.
 */
void vali_service_set_user_data(struct vali_service *service, void *user_data);

/**
 * Get a FD signalling when the service needs to be dispatched.
 *
 * The returned FD becomes readable when vali_service_dispatch() needs to
 * be called. This is useful to integrate the service in another event loop.
 */
int vali_service_get_fd(struct vali_service *service);

/**
 * Set the service's call handler.
 *
 * A handler must be set before the service starts listening.
 *
 * Any previously set handler is replaced.
 */
void vali_service_set_call_handler(struct vali_service *service, struct vali_service_call_handler handler);

/**
 * Listen on a Unix socket.
 */
bool vali_service_listen_unix(struct vali_service *service, const char *path);

/**
 * Listen on an already-opened Unix socket FD.
 */
bool vali_service_listen_fd(struct vali_service *service, int fd);

/**
 * A connection for a service.
 *
 * This object is owned by the struct vali_service and is destroyed by
 * vali_service_dispatch() when the remote client is disconnected.
 */
struct vali_service_conn;

/**
 * Create a new connection to a service with an already-opened FD.
 */
struct vali_service_conn *vali_service_create_conn(struct vali_service *service, int fd);

/**
 * Disconnect a remote client.
 */
void vali_service_conn_disconnect(struct vali_service_conn *conn);

/**
 * Get the underlying connection FD.
 */
int vali_service_conn_get_fd(struct vali_service_conn *conn);

/**
 * Dispatch I/O events.
 */
bool vali_service_dispatch(struct vali_service *service);

/**
 * A Varlink interface registry.
 *
 * A registry is a helper to implement a Varlink service. It dispatches
 * incoming method calls to the right interface. It also implements the
 * "org.varlink.service" interface.
 *
 * To make a struct vali_service use a registry, pass the result of
 * vali_registry_get_call_handler() to
 * vali_service_set_call_handler().
 */
struct vali_registry;

struct vali_registry_options {
	const char *vendor;
	const char *product;
	const char *version;
	const char *url;
};

/**
 * Create a new registry.
 */
struct vali_registry *vali_registry_create(const struct vali_registry_options *options);

/**
 * Destroy a registry.
 */
void vali_registry_destroy(struct vali_registry *registry);

/**
 * Get a call handler for the registry.
 *
 * This can be passed to vali_service_set_call_handler() to dispatch
 * incoming requests to the registry.
 */
struct vali_service_call_handler vali_registry_get_call_handler(struct vali_registry *registry);

struct vali_registry_interface {
	const char *name;
	const char *definition;
};

/**
 * Register an interface.
 *
 * An interface can only be registered once. The "org.varlink.service"
 * interface cannot be registered.
 */
bool vali_registry_add(struct vali_registry *registry, const struct vali_registry_interface *iface, struct vali_service_call_handler handler);

/**
 * Release resources held by an error.
 *
 * This function can be called safely on a zero error.
 */
void vali_error_finish(struct vali_error *err);

/**
 * Set an error's name and parameters.
 *
 * This replaces any previously set error name and parameters.
 */
void vali_error_set(struct vali_error *err, const char *name,
	struct json_object *parameters);

#endif
