/**
 * @file buffer.h
 * Implements buffered IO
 */

#ifndef RSPAMD_BUFFER_H
#define RSPAMD_BUFFER_H

#include "config.h"
#include "mem_pool.h"
#include "fstring.h"

typedef gboolean (*dispatcher_read_callback_t)(rspamd_ftok_t *in, void *user_data);
typedef gboolean (*dispatcher_write_callback_t)(void *user_data);
typedef void (*dispatcher_err_callback_t)(GError *err, void *user_data);

/**
 * Types of IO handling
 */
enum io_policy {
	BUFFER_LINE,                                                    /**< call handler when we have line ready */
	BUFFER_CHARACTER,                                               /**< call handler when we have some characters */
	BUFFER_ANY                                                      /**< call handler whenever we got data in buffer */
};

/**
 * Buffer structure
 */
struct rspamd_buffer_buf {
	gsize size;
	gsize len;
	guchar *begin;
};

typedef struct rspamd_buffer_s {
	struct rspamd_buffer_buf *data;
	guchar *pos;                                                     /**< current position		*/
} rspamd_buffer_t;

struct rspamd_out_buffer_s {
	GString *data;
	gboolean allocated;
	struct rspamd_out_buffer_s *prev, *next;
};

typedef struct rspamd_io_dispatcher_s {
	rspamd_buffer_t *in_buf;                                        /**< input buffer			*/
	struct {
		guint pending;
		struct rspamd_out_buffer_s *buffers;
	} out_buffers;                                                  /**< output buffers chain	*/
	struct timeval *tv;                                             /**< io timeout				*/
	struct event *ev;                                               /**< libevent io event		*/
	rspamd_mempool_t *pool;                                         /**< where to store data	*/
	enum io_policy policy;                                          /**< IO policy				*/
	size_t nchars;                                                  /**< how many chars to read	*/
	gint fd;                                                            /**< descriptor				*/
	guint32 peer_addr;                                              /**< address of peer for debugging */
	gboolean wanna_die;                                             /**< if dispatcher should be stopped */
	dispatcher_read_callback_t read_callback;                       /**< read callback			*/
	dispatcher_write_callback_t write_callback;                     /**< write callback			*/
	dispatcher_err_callback_t err_callback;                         /**< error callback			*/
	void *user_data;                                                /**< user's data for callbacks */
	gulong default_buf_size;                                        /**< default size for buffering */
	off_t offset;                                                   /**< for sendfile use		*/
	size_t file_size;
	gint sendfile_fd;
	gboolean in_sendfile;                                           /**< whether buffer is in sendfile mode */
	gboolean strip_eol;                                             /**< strip or not line ends in BUFFER_LINE policy */
	gboolean is_restored;                                           /**< call a callback when dispatcher is restored */
	gboolean half_closed;                                           /**< connection is half closed */
	gboolean want_read;                                             /**< whether we want to read more data */
	struct event_base *ev_base;                                     /**< event base for io operations */
#ifndef HAVE_SENDFILE
	void *map;
#endif
} rspamd_io_dispatcher_t;

/**
 * Creates rspamd IO dispatcher for specified descriptor
 * @param fd descriptor to IO
 * @param policy IO policy
 * @param read_cb read callback handler
 * @param write_cb write callback handler
 * @param err_cb error callback handler
 * @param tv IO timeout
 * @param user_data pointer to user's data
 * @return new dispatcher object or NULL in case of failure
 */
rspamd_io_dispatcher_t * rspamd_create_dispatcher (struct event_base *base,
	gint fd,
	enum io_policy policy,
	dispatcher_read_callback_t read_cb,
	dispatcher_write_callback_t write_cb,
	dispatcher_err_callback_t err_cb,
	struct timeval *tv,
	void *user_data);

/**
 * Set new policy for dispatcher
 * @param d pointer to dispatcher's object
 * @param policy IO policy
 * @param nchars number of characters in buffer for character policy
 */
void rspamd_set_dispatcher_policy (rspamd_io_dispatcher_t *d,
	enum io_policy policy,
	size_t nchars);

/**
 * Write data when it would be possible
 * @param d pointer to dispatcher's object
 * @param data data to write
 * @param len length of data
 */
gboolean rspamd_dispatcher_write (rspamd_io_dispatcher_t *d,
	const void *data,
	size_t len, gboolean delayed,
	gboolean allocated) G_GNUC_WARN_UNUSED_RESULT;

/**
 * Send specified descriptor to dispatcher
 * @param d pointer to dispatcher's object
 * @param fd descriptor of file
 * @param len length of data
 */
gboolean rspamd_dispatcher_sendfile (rspamd_io_dispatcher_t *d,
	gint fd,
	size_t len) G_GNUC_WARN_UNUSED_RESULT;

/**
 * Pause IO events on dispatcher
 * @param d pointer to dispatcher's object
 */
void rspamd_dispatcher_pause (rspamd_io_dispatcher_t *d);

/**
 * Restore IO events on dispatcher
 * @param d pointer to dispatcher's object
 */
void rspamd_dispatcher_restore (rspamd_io_dispatcher_t *d);

/**
 * Frees dispatcher object
 * @param dispatcher pointer to dispatcher's object
 */
void rspamd_remove_dispatcher (rspamd_io_dispatcher_t *dispatcher);

/**
 * Cleanup dispatcher freeing all temporary data
 * @param dispatcher pointer to dispatcher's object
 */
void rspamd_dispacther_cleanup (rspamd_io_dispatcher_t *dispatcher);

#endif