/*****************************************************************************\
 **  pmix_io.h - PMIx non-blocking IO routines
 *****************************************************************************
 *  Copyright (C) 2014-2015 Artem Polyakov. All rights reserved.
 *  Copyright (C) 2015-2017 Mellanox Technologies. All rights reserved.
 *  Written by Artem Polyakov <artpol84@gmail.com, artemp@mellanox.com>.
 *
 *  This file is part of Slurm, a resource management program.
 *  For details, see <https://slurm.schedmd.com/>.
 *  Please also read the included file: DISCLAIMER.
 *
 *  Slurm is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2 of the License, or (at your option)
 *  any later version.
 *
 *  In addition, as a special exception, the copyright holders give permission
 *  to link the code of portions of this program with the OpenSSL library under
 *  certain conditions as described in each individual source file, and
 *  distribute linked combinations including the two. You must obey the GNU
 *  General Public License in all respects for all of the code used other than
 *  OpenSSL. If you modify file(s) with this exception, you may extend this
 *  exception to your version of the file(s), but you are not obligated to do
 *  so. If you do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source files in
 *  the program, then also delete it here.
 *
 *  Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
 *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 *  details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with Slurm; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
 \*****************************************************************************/

#ifndef PMIXP_IO_H
#define PMIXP_IO_H

#include <poll.h>
#include "pmixp_common.h"
#include "pmixp_utils.h"

typedef enum {
	PMIXP_IO_NONE = 0,
	PMIXP_IO_INIT,
	PMIXP_IO_OPERATING,
	PMIXP_IO_CONN_CLOSED,
	PMIXP_IO_FINALIZED
} pmixp_io_state_t;

typedef struct {
#ifndef NDEBUG
#define PMIXP_MSGSTATE_MAGIC 0xC0FFEEEE
	int magic;
#endif
	/* User supplied information */
	int sd;
	int error;
	pmixp_p2p_data_t h;
	pmixp_io_state_t io_state;
	/* receiver */
	uint32_t rcvd_hdr_offs;
	void *rcvd_hdr_net;
	void *rcvd_hdr_host;
	uint32_t rcvd_pay_size;
	uint32_t rcvd_pay_offs;
	void *rcvd_payload;
	uint32_t rcvd_pad_recvd;
	/* sender */
	pthread_mutex_t send_lock;
	void *send_current;
	uint32_t send_offs;
	uint32_t send_msg_size;
	void *send_msg_ptr;
	list_t *send_queue;
	list_t *complete_queue;
} pmixp_io_engine_t;

static inline int pmixp_io_fd(pmixp_io_engine_t *eng)
{
	return eng->sd;
}


static inline bool pmixp_io_rcvd_ready(pmixp_io_engine_t *eng)
{
	xassert(eng->magic == PMIXP_MSGSTATE_MAGIC);
	return (eng->rcvd_hdr_offs == eng->h.rhdr_net_size)
			&& (eng->rcvd_pay_size == eng->rcvd_pay_offs);
}

static inline bool pmixp_io_operating(pmixp_io_engine_t *eng)
{
	xassert(eng->magic == PMIXP_MSGSTATE_MAGIC);
	return (PMIXP_IO_OPERATING == eng->io_state);
}

static inline bool pmixp_io_conn_closed(pmixp_io_engine_t *eng)
{
	xassert(eng->magic == PMIXP_MSGSTATE_MAGIC);
	return (PMIXP_IO_CONN_CLOSED == eng->io_state);
}

static inline bool pmixp_io_enqueue_ok(pmixp_io_engine_t *eng)
{
	xassert(eng->magic == PMIXP_MSGSTATE_MAGIC);
	return (PMIXP_IO_OPERATING == eng->io_state) ||
			(PMIXP_IO_INIT == eng->io_state);
}

static inline bool pmixp_io_finalized(pmixp_io_engine_t *eng)
{
	xassert(eng->magic == PMIXP_MSGSTATE_MAGIC);
	return (PMIXP_IO_FINALIZED == eng->io_state);
}

static inline int pmixp_io_error(pmixp_io_engine_t *eng)
{
	xassert(eng->magic == PMIXP_MSGSTATE_MAGIC);
	return eng->error;
}

/* initialize all the data structures to prepare
 * engine for operation.
 * file descriptor needs to be provided to put it
 * to the operation mode
 */
void pmixp_io_init(pmixp_io_engine_t *eng,
		   pmixp_p2p_data_t header);

/* attach engine to the specific file descriptor */
static inline void
pmixp_io_attach(pmixp_io_engine_t *eng, int fd)
{
	/* Initialize general options */
	xassert(PMIXP_MSGSTATE_MAGIC == eng->magic);
	xassert(PMIXP_IO_INIT == eng->io_state);
	eng->sd = fd;
	eng->io_state = PMIXP_IO_OPERATING;
}

/* detach engine from the current file descriptor.
 * the `fd` is returned and can be used with other
 * engine if needed
 */
int pmixp_io_detach(pmixp_io_engine_t *eng);

/* cleanup all the data structures allocated by this
 * engine.
 * If engine wasn't detached, corresponding `fd` will
 * be also closed
 */
void pmixp_io_finalize(pmixp_io_engine_t *eng, int error);

/* Receiver */
void pmixp_io_rcvd_progress(pmixp_io_engine_t *eng);
void *pmixp_io_rcvd_extract(pmixp_io_engine_t *eng, void *header);
static inline void*
pmixp_io_recv_hdr_alloc_host(pmixp_io_engine_t *eng)
{
	xassert(eng->magic == PMIXP_MSGSTATE_MAGIC);
	return xmalloc(eng->h.rhdr_host_size);
}

static inline void*
pmixp_io_recv_hdr_alloc_net(pmixp_io_engine_t *eng)
{
	xassert(eng->magic == PMIXP_MSGSTATE_MAGIC);
	return xmalloc(eng->h.rhdr_net_size);
}

/* Transmitter */
/* thread-safe function, only calls Slurm list append */
int pmixp_io_send_enqueue(pmixp_io_engine_t *eng, void *msg);
int pmixp_io_send_urgent(pmixp_io_engine_t *eng, void *msg);
void pmixp_io_send_progress(pmixp_io_engine_t *eng);
bool pmixp_io_send_pending(pmixp_io_engine_t *eng);
void pmixp_io_send_cleanup(pmixp_io_engine_t *eng, pmixp_p2p_ctx_t ctx);

#endif /* PMIXP_IO_H */
