#ifndef __MTCP_API_H_
#define __MTCP_API_H_

#include <stdint.h>
#include <netinet/in.h>
#include <sys/uio.h>

#ifndef UNUSED
#define UNUSED(x)		(void)x
#endif

#ifndef INPORT_ANY
#define INPORT_ANY	(uint16_t)0
#endif

typedef unsigned char byte;

#ifdef __cplusplus
extern "C" {
#endif

/** mTCP context */
struct mtcp_context
{
	int cpu;
};

typedef struct mtcp_context *mctx_t;

/** mTCP signal handler type */
typedef void (*mtcp_sighandler_t)(int);

/** Socket types */
enum socket_type
{
	/** unused */
	MOS_SOCK_UNUSED,

	/* listening socket type */
	/** regular mTCP connection listen socket */
	MOS_SOCK_STREAM_LISTEN,
	/** MOS proxy socket listening socket */
	/* (pending implementation) */
	MOS_SOCK_PROXY_LISTEN,
	/** MOS monitor socket listening socket */
	MOS_SOCK_MONITOR_STREAM,

	/* stream socket type */

	/** regular mTCP connection socket */
	MOS_SOCK_STREAM,
	/** MOS proxy socket */
	/* (pending implementation) */
	MOS_SOCK_PROXY,
	/** MOS monitor socket */
	MOS_SOCK_MONITOR_STREAM_ACTIVE,
	/** MOS monitor socket (raw, stateless) */
	MOS_SOCK_MONITOR_RAW,
	
	/* for epoll */
	MOS_SOCK_EPOLL,
	/* for pipe */
	MOS_SOCK_PIPE,
};

/* Configurable mTCP attributes
 * This is automatically generated by mOS core after the
 * mtcp_init() function call. You can change this option
 * in your program using mtcp_get/set_conf().
 */
struct mtcp_conf
{
/** Maximum length of application name */
#define APP_NAME_LEN 			40
/** Maximum concurrently runable application */
#define MOS_APP 			20

	int num_cores;        /**< number of cores to use */
	int max_concurrency;  /**< number of concurrent flows per core*/
	uint64_t cpu_mask;

	int max_num_buffers;  /**< number of socket buffers */
	int rcvbuf_size;      /**< size of receive buffer */
	int sndbuf_size;      /**< size of send buffer */

	int tcp_timewait;     /**< time wait time in sec */
	int tcp_timeout;      /**< timeout in sec, -1 for not to check timeout */

#define MOS_APP_ARGC 20
	uint64_t app_cpu_mask[MOS_APP];
	char *app_argv[MOS_APP][MOS_APP_ARGC];
	int app_argc[MOS_APP];
	int num_app;                     /**< number of registered apps */
};

/* Target to modify */
struct app_context
{
	mctx_t mctx;              /**< mTCP context */
	int socket_id;            /**< listing socket ID */
	struct conn_filter *cf;
	int ep_id;
};

/* Application need to specify those parameters. */
/* NOTE: This structure is not used in mos_release 0.2a
 * This structure will be used by embedded mOS application using
 * mOS server.
 */
struct app_ops
{
	/** Initialization function which is called for only once per each
	 * application */
	void (*app_init)(int argc, char **argv);
	/** Initialization function which is called for each CPU core */
	void (*thread_init)(mctx_t mctx, void **app_ctx);
	/** Function contains code which will be executed after initialization
	 * phase */
	void (*run)(mctx_t mctx, void **app_ctx);
};


/** Initialize mOS context with parameters mentioned in the config file
 * @param [in] config_file: location of config file
 * @return 0 on success, -1 on error
 *
 * Setting dpdk devices, interface, load configuration from config file,
 * routing table, arp table, etc.
 */
int
mtcp_init(const char *config_file);

/** Destroy the global mOS context
 * @return 0 on success, -1 on error
 */
int
mtcp_destroy();

/** Load current mtcp configuration in *conf
 * @param [out] conf: configurations
 * @return 0 on success, -1 on error
 */
int
mtcp_getconf(struct mtcp_conf *conf);

/** Update mOS base with parameters mentioned in *conf
 * @param [in] conf: configurations to set
 * @return 0 on success, -1 on error
 */
int
mtcp_setconf(const struct mtcp_conf *conf);

/** Bind a thread to a specific CPU core
 * @param [in] cpu: CPU ID
 * @return 0 on success, -1 on error
 */
int
mtcp_core_affinitize(int cpu);

/** Create mOS/mtcp context thread based on the parameters passed
 * by mtcp_init() & mtcp_setconf() functions
 * @param [in] cpu: Core id to affinitize new mtcp thread
 * @return mtcp context, NULL on error
 */
mctx_t
mtcp_create_context(int cpu);

/** Destory mtcp context that was created by mOS/mTCP thread
 * @param [in] mctx: mtcp context
 * @return 0 on success, -1 on error
 */
int
mtcp_destroy_context(mctx_t mctx);

/** Register signal handler (mtcp_sighandler_t handler )
 * for int signum
 * @param [in] signum: Signal number to handle
 * @param [in] handler: Handler function
 * @return Same handler as input
 */
mtcp_sighandler_t
mtcp_register_signal(int signum, mtcp_sighandler_t handler);

/** Create pipe
 * @param [in] mctx: mtcp context
 * @param [out] pipeid: created pipe
 * @return 0 on success, -1 on error
 */
int
mtcp_pipe(mctx_t mctx, int pipeid[2]);

/** Get socket options
 *
 * <ol>
 * 
 * <li> Please see http://mos.kaist.edu/man/mtcp_getsockopt.html for
 * the further informations. </li>
 *
 * <li> MOS_FRAGINFO_CLIBUF and MOS_FRAGINFO_SVRBUF \n
 * Gives back offsets to fragments (non-contiguous data segments) currently
 * stored in client’s TCP ring buffer. The optval is an array of
 * `struct tcp_ring_fragment`
 * where offset flow data starting from client’s TCP SYN sequence number,
 * and len is the length of the tcp_ring_fragment. The optval holds the size
 * of the array (in terms of the number of elements). </li>
 *
 * <li> MOS_INFO_CLIBUF and MOS_INFO_SVRBUF \n
 * Returns meta-data regarding the client’s TCP ring buffer. This information
 * is returned in the form of optval which is passed as struct tcp_buf_info. </li>
 *
 * <li> MOS_TCP_STATE_CLI and MOS_TCP_STATE_SVR \n
 * Returns the current emulated state of the client. The optval argument is
 * a pointer to an int whereas the optlen argument contains the sizeof(int).
 * The optval returns a value of type enum tcpstate which can carry any one
 * of the following states. </li>
 * </ol>
 *
 * @param [in] mctx: mtcp context
 * @param [in] sock: socket id
 * @param [in] level: SOL_MONSOCKET
 * @param [in] optname: __variable__
 * @param [out] optval: value of getting option
 * @param [out] optlen: length of getting option
 * @return Zero on success, -1 on error
 */
int
mtcp_getsockopt(mctx_t mctx, int sock, int level,
		int optname, void *optval, socklen_t *optlen);

/** Set socket options
 *
 * <ol>
 *
 * <li> MOS_CLIBUF and MOS_SVRBUF \n
 * Dynamically adjust the size of the TCP receive ring buffer of the
 * emulated client/server stack. The optval contains the size of the buffer
 * that needs to be set as int, while optlen is equal to sizeof(int). </li>
 * 
 * <li> MOS_CLIOVERLAP and MOS_SVROVERLAP \n
 * Dynamically determine the policy on content overlap (e.g., overwriting
 * with the retransmitted payload or not) for the client-side buffer. The
 * optval can be either MOS_OVERLAP_POLICY_FIRST (to take the first data
 * and never overwrite the buffer) or MOS_OVERLAP_POLICY_LAST (to always
 * update the buffer with the last data), and optlen is equal to sizeof(int). </li>
 *
 * <li> MOS_STOP_MON \n
 * Dynamically stop monitoring a flow for the specific side. This option can
 * be used only with a MOS_SOCK_MONITOR_ACTIVE socket, which is given as a
 * parameter in callback functions for every flow. The optval contains a side
 * variable (MOS_SIDE_CLI, MOS_SIDE_SVR, or MOS_SIDE_BOTH), while optlen is
 * equal to sizeof(int). </li>
 *
 * </ol>
 *
 * @param [in] mctx: mtcp context
 * @param [in] sock: socket id
 * @param [in] level: SOL_MONSOCKET
 * @param [in] optname: __variable__
 * @param [in] optval: value of setting option
 * @param [in] optlen: length of setting option
 * @return Zero on success, -1 on error
 */
int
mtcp_setsockopt(mctx_t mctx, int sock, int level,
		int optname, const void *optval, socklen_t optlen);

/** Set socket as nonblock
 * @param [in] mctx: mtcp context
 * @param [in] sock: socket id
 * @return Zero on success, -1 on error
 *
 * DEPRECATED
 */
int
mtcp_setsock_nonblock(mctx_t mctx, int sock);

/** Control socket
 * @param [in] mctx: mtcp context
 * @param [in] sock: socket id
 * @param [in] request: requested operation
 * @param [in,out] argp: pointer to argument
 * @return Zero on success, -1 on error
 *
 * Operations | Description
 * ---------- | -----------
 * FIONREAD   | number of data bytes in receive buffer
 * FIONBIO    | for non-blocking I/O
 */
int
mtcp_ioctl(mctx_t mctx, int sock, int request, void *argp);

/** Create a socket
 * @param [in] mctx: mtcp context
 * @param [in] domain: AF_INET, Not used
 * @param [in] type: Any of the following keyword
 *             - MOS_SOCK_STREAM (End TCP)
 *             - MOS_SOCK_MONITOR_RAW (Packet-level monitor)
 *             - MOS_SOCK_MONITOR_STREAM (Flow-level monitor)
 *              (MOS_SOCK_PROXY: To be supported)
 * @param [in] protocol: NULL, Not used
 * @return Zero on success, -1 on error
 */
int
mtcp_socket(mctx_t mctx, int domain, int type, int protocol);

/** Bind a socket, same as `bind()`
 * @return Zero on success, -1 on error
 */
int
mtcp_bind(mctx_t mctx, int sock,
		const struct sockaddr *addr, socklen_t addrlen);

/** Listen a listening socket, same as `listen()`
 * @return Zero on success, -1 on error
 *
 * Only MOS_SOCK_LISTENER, MOS_SOCK_PROXY are allowed
 */
int
mtcp_listen(mctx_t mctx, int sock, int backlog);

/** Accept new flow, same as `accept()`
 * @param [in] mctx: mtcp context
 * @param [in] sock: socket id for MOS_SOCK_STREAM
 * @param [in] addr: address of the peer host(s)
 * @param [in] addrlen: length of `addr`
 * @return Zero on success, -1 on error
 */
int
mtcp_accept(mctx_t mctx, int sock, struct sockaddr *addr, socklen_t *addrlen);

/** Initialize RSS pool for decide appropriate port numbers
 * @param [in] mctx: mtcp context
 * @param [in] saddr_base: source IP address base
 * @param [in] num_addr: number of source IP address to use
 * @param [in] daddr: destination IP address
 * @param [in] dport: destination port number
 * @return Zero on success, -1 on error
 *
 * w.r.t source IP addr, destination IP addr, destination port
 */
int
mtcp_init_rss(mctx_t mctx, in_addr_t saddr_base, int num_addr,
		in_addr_t daddr, in_addr_t dport);

/** Connect new flow, same as `connect()`
 * @return Zero on success, -1 on error
 */
int
mtcp_connect(mctx_t mctx, int sock,
		const struct sockaddr *addr, socklen_t addrlen);

/** Close flow, same as `close()`
 * @return Zero on success, -1 on error
 */
int
mtcp_close(mctx_t mctx, int sock);

/** Abort flow by sending RST
 * @return Zero on success, -1 on error
 *
 * Different from `mtcp_close()` which processes 4way handshake by FIN
 */
int
mtcp_abort(mctx_t mctx, int sock);

/** Returns the current address to which the socket sockfd is bound
 * @param [in] mctx: mtcp context
 * @param [in] addr: address buffer to be filled
 * @param [in] addrlen: amount of space pointed to by addr
 * @return 0 on success, -1 on error
 */
int
mtcp_getsockname(mctx_t mctx, int sock, struct sockaddr *addr, socklen_t *addrlen);

/** Read byte stream from flow, same as `recv()`
 * For now, it can only accept flags 0 (default aka mtcp_read()) & MSG_PEEK
 * @return number of bytes read on success, -1 on error
 */
ssize_t
mtcp_recv(mctx_t mctx, int sockid, char *buf, size_t len, int flags);

/** Read byte stream from flow, same as `read()`
 * @return number of bytes read on success, -1 on error
 */
inline ssize_t
mtcp_read(mctx_t mctx, int sock, char *buf, size_t len);

/* readv should work in atomic */
/** Read byte stream from flow in vector, same as `readv()`
 * @return number of bytes read on success, -1 on error
 */
ssize_t
mtcp_readv(mctx_t mctx, int sock, const struct iovec *iov, int numIOV);

/** Write byte stream to flow, same as `write()`
 * @return number of bytes write on success, -1 on error
 */
ssize_t
mtcp_write(mctx_t mctx, int sock, const char *buf, size_t len);

/* writev should work in atomic */
/** Write byte stream to flow in vector, same as `writev()`
 * @return number of bytes write on success, -1 on error
 */
ssize_t
mtcp_writev(mctx_t mctx, int sock, const struct iovec *iov, int numIOV);

/** Get concurrent flow count of the underlying mtcp manager context (per-thread)
 * @param [in] mctx: mtcp context
 * @return concurrent flow count
 */
uint32_t
mtcp_get_connection_cnt(mctx_t mctx);

#ifdef __cplusplus
};
#endif

#endif /* __MTCP_API_H_ */
