/*
 * Secure X Message Passing Library v2 implementation.
 * (sxmplv2) it superseed all versions before due to the:
 * - memory consumption
 * - new features such as pulse emitting
 * - performance optimization
 *
 * This is a proprietary software. See COPYING for further details.
 *
 * (c) Askele Group 2013-2015 <http://askele.com>
 *
 */

#ifndef __SXMP_SXMPLV2_H__
#define	__SXMP_SXMPLV2_H__

#include <stdint.h>
#include <time.h>
#include <sys/types.h>
#include <pthread.h>

#include <openssl/ssl.h>

#include <tdata/usrtc.h>
#include <tdata/idx_allocator.h>
#include <tdata/list.h>
#include <sexpr/sexp.h>
#include <sexpr/faststack.h>

#include <sxmp/errno.h>
#include <sxmp/version.h>

#define VERIFY_DEPTH  1  /* FIXME: */

typedef struct __session_context_type {
  char *login;
  char *passwd;
  uint64_t certid;
  struct in_addr *addr;
  void *priv;
} sxsession_ctx_t;

/* 8 byte header */
typedef struct __sxmplv2_head_type {
  uint16_t msgid;
  uint16_t payload_length;
  uint8_t attr;
  uint8_t opcode;
  uint16_t reserve;
}__attribute__((packed)) sxmplv2_head_t;

struct __sxhub_type;
struct __sxchannel_t;
struct __sxmsg_t;

/* flags for the connection link */
#define SXMP_BATCHMODE      (1 << 1)
#define SXMP_MESSAGINGMODE  (1 << 2)
#define SXMP_ALIVE          (1 << 3)
#define SXMP_CLOSED         (1 << 4)

/*
 * älä jätä kommentteja omalla kielellä! yksinkertaisia englanti sijaan!
 * i found somebody who write comments and messages in non-english,
 * it's a fucking practice - forget it.
 */
typedef struct __sxlink_t {
  /* General section */
  struct __sxhub_type *ssys; /* < hub subsystem */
  char *uuid; /** < uuid of the link */
  /* Channels section */
  idx_allocator_t idx_ch; /** < index allocation for channels */
  pthread_mutex_t idx_ch_lock; /** < mutex for allocating and deallocating channels */
  struct __sxchannel_t **channels; /** < channels O(1) storage */
  /* RPC section */
  usrtc_t *rpc_list; /** < search tree of possible RPC typed lists */
  /* SSL related section */
  SSL *ssl; /** < SSL connection */
  int ssl_data_index; /** < SSL index for the custom data */
  pthread_mutex_t sslinout[2]; /** < SSL related locks for in and out */
  /* Security section */
  sxsession_ctx_t *pctx; /** < higher layer authentification and session context */
  /* Messages section */
  struct __sxmsg_t **messages; /** < messages O(1) storage */
  idx_allocator_t idx_msg;
  pthread_mutex_t idx_msg_lock;
  list_head_t write_pending;  /** < list of messages waiting for write */
  pthread_mutex_t write_pending_lock;
  volatile uint8_t pending_messages; /** < pending message count */
  /* Other stuff */
  pthread_t thrd_poll[8];
  volatile uint8_t flags; /** < flags of the connection */
  volatile uint8_t usecount; /** < use count for the connection link */
  usrtc_node_t csnode; /** < node to store the link within list */
} sxlink_t;

#define sxlink_getpctx(c)     (c)->pctx
#define sxlink_getpriv(c)     (c)->pctx->priv
#define sxlink_setpriv(c, p)  (c)->pctx->priv = (void *)(p)

struct __link_rpc_list_type;

typedef struct __pp_msg_type { /* postponed message entry */
  struct __sxmsg_t *msg;
  list_node_t node;
} sxppmsg_t;

typedef struct __sxchannel_t {
  uint16_t cid; /** < ID of the channel */
  sxlink_t *link; /** < pointer to the underlying link */
  struct __link_rpc_list_type *rpc_list; /** < rpc functions list */
  int flags; /** < flags of the channel */
} sxchnl_t;

/* message flags */
#define SXMSG_OPEN      (1 << 1)
#define SXMSG_CLOSED    (1 << 2)
#define SXMSG_PROTO     (1 << 3)
#define SXMSG_LINK      (1 << 4)
#define SXMSG_REPLYREQ  (1 << 5)
#define SXMSG_PULSE     (1 << 6)
#define SXMSG_TIMEDOUT  (1 << 7)

/**
 * \brief Message used in sxmp message passing
 *
 * This structure used to manage a message within a channel
 * of the sxmp structure stack.
 */
typedef struct __sxmsg_t {
  sxchnl_t *pch; /** < channel of the message(if applicable) */
  pthread_mutex_t wait; /** < special wait mutex, used for pending list and sync */
  sxmplv2_head_t mhead; /** < last actual head of the message */
  void *payload; /** < payload */
} sxmsg_t;

#define sxmsg_payload(m)   (m)->payload
#define sxmsg_datalen(m)   (m)->mhead.payload_length
#define sxmsg_rapidbuf(m)  (m)->payload
#define sxmsg_retcode(m)   (m)->mhead.opcode
#define sxmsg_waitlock(m)  pthread_mutex_lock(&((m)->wait))
#define sxmsg_waitunlock(m)  pthread_mutex_unlock(&((m)->wait))

typedef struct __link_rpc_entry_type {
  char *name;
  int (*rpcf)(void *, sexp_t *);
  usrtc_node_t node;
} sxl_rpc_t;

typedef struct __link_rpc_list_type {
  usrtc_t *rpc_tree; /** < search tree for the rpc lookup */
  char *opt_version; /** < reserved for future implementations */
} sxl_rpclist_t;

/**
 * \brief Hub subsystem structure.
 *
 * This structure used for management and control a set of a
 * determined links with the same RPC lists and the same
 * mode (server, client).
 *
 */
typedef struct __sxhub_type {
  usrtc_t *links;
  pthread_rwlock_t rwlock;
  char *rootca, *certpem, *certkey; /* path name to the certificates */
  sxl_rpclist_t *system_rpc;
  /* special functions pointers */
  int (*validate_sslpem)(sxlink_t *); /** < this function used to validate SSL certificate while SSL handshake */
  int (*secure_check)(sxlink_t *); /** < this function authorize user to login,
                                  * and also should check SSL cert and user, and already made sessions */
  usrtc_t* (*get_rpc_typed_list_tree)(sxlink_t *); /** < this function is used to set RPC list of the functions */
  int (*set_typed_list_callback)(sxlink_t *, int, char *); /** < this function is a callback
                                                          * during setting up a typed channel */
  void (*on_destroy)(sxlink_t *); /** < callback on connection destroy  */
  void (*on_pulse)(sxlink_t *, sexp_t *);  /** < callback on pulse emit */
  SSL_CTX *ctx; /** < SSL context */
  void *priv;
} sxhub_t;

#define sxhub_set_sslvalidate(c, f)  (c)->validate_sslpem = (f)
#define sxhub_set_authcheck(c, f)  (c)->secure_check = (f)
#define sxhub_set_rpcvalidator(c, f)  (c)->get_rpc_typed_list_tree = (f)
#define sxhub_set_channelcall(c, f)  (c)->set_typed_list_callback = (f)
#define sxhub_set_ondestroy(c, f)  (c)->on_destroy = (f)
#define sxhub_set_onpulse(c, f)  (c)->on_pulse = (f)
#define sxhub_set_priv(c, p)  (c)->priv = (p)
#define sxhub_get_priv(c)  (c)->priv

typedef struct __rpc_typed_list_type {
  int type_id;
  char *description;
  sxl_rpclist_t *rpc_list;
  usrtc_node_t lnode;
} rpc_typed_list_t;

#ifdef __cplusplus
extern "C" {
#endif

/* API */
int sxmp_init(void);

void sxmp_finalize(void);

int sxhub_init(sxhub_t *ssys);

sxhub_t *sxhub_create(void);

int sxhub_destroy(sxhub_t *ssys);

int sxhub_free(sxhub_t *ssys);

int sxhub_setsslserts(sxhub_t *ssys, const char *rootca,
                      const char *certpem, const char *certkey);

/* create links */
sxlink_t *sxlink_master_accept(sxhub_t *ssys, int sck, struct in_addr *addr);
sxlink_t *sxlink_connect(sxhub_t *ssys, const char *host,
                         int port, const char *SSL_cert, const char *login,
                         const char *passwd);
int sxlink_close(sxlink_t *co);

/* channels */
sxchnl_t *sxchannel_open(sxlink_t *co, int type);
int sxchannel_close(sxchnl_t *channel);

/* messages */
/*
 * creates a message with a payload.
 * Will return a error code, and, if applicable, pointer to message
 */
int sxmsg_send(sxchnl_t *channel, const char *data, size_t datalen, sxmsg_t **msg);
/* the same - postponed message i.e. will be written to the queue - not to write immendatly */
int sxmsg_send_pp(sxchnl_t *channel, const char *data, size_t datalen, sxmsg_t **msg);
/* send a pulse message */
int sxmsg_pulse(sxlink_t *co, const char *data, size_t datalen);
int sxmsg_reply(sxmsg_t *msg, const char *data, size_t datalen);
int sxmsg_reply_pp(sxmsg_t *msg, const char *data, size_t datalen);
int sxmsg_rreply(sxmsg_t *msg, size_t datalen);
int sxmsg_return(sxmsg_t *msg, int opcode);
int sxmsg_return_pp(sxmsg_t *msg, int opcode);
void sxmsg_clean(sxmsg_t *msg);

#ifdef __cplusplus
}
#endif

/* RPC List API */
#define SXMP_FILTER_INC  0xa
#define SXMP_FILTER_EXC  0xb
#define SXMP_FILTER_END  -1

#ifdef __cplusplus
extern "C" {
#endif

int sxmp_rpclist_init(usrtc_t *tree);

int sxmp_rpclist_add(usrtc_t *tree, int type, const char *description,
                     const char *version);

int sxmp_rpclist_add_function(usrtc_t *tree, int type, const char *fu_name,
                              int (*rpcf)(void *, sexp_t *));

int sxmp_rpclist_filter(usrtc_t *source, usrtc_t **dest, int flag, int *filter);

#ifdef __cplusplus
}
#endif

#define blub(txt)  fprintf(stderr, "%s:%d in %s > %s\n", __FILE__, __LINE__, __FUNCTION__, txt)

#define dumphead(head) fprintf(stderr, "id: %d, opcode: %d, attr: %d, len = %d\n", head->msgid, head->opcode, head->attr, head->payload_length)

#endif /* __SXMP_SXMPLV2_H__ */