/*
    Copyright (c) 2012-2014 Martin Sustrik  All rights reserved.
    Copyright (c) 2013 GoPivotal, Inc.  All rights reserved.

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom
    the Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    IN THE SOFTWARE.
*/

#ifndef NN_TRANSPORT_INCLUDED
#define NN_TRANSPORT_INCLUDED

#include "nn.h"

#include "aio/fsm.h"

#include "utils/list.h"
#include "utils/msg.h"
#include "utils/int.h"

#include <stddef.h>

/*  This is the API between the nanomsg core and individual transports. */

struct nn_sock;
struct nn_cp;

/******************************************************************************/
/*  Container for transport-specific socket options.                          */
/******************************************************************************/

struct nn_optset;

struct nn_optset_vfptr {
    void (*destroy) (struct nn_optset *self);
    int (*setopt) (struct nn_optset *self, int option, const void *optval,
        size_t optvallen);
    int (*getopt) (struct nn_optset *self, int option, void *optval,
        size_t *optvallen);
};

struct nn_optset {
    const struct nn_optset_vfptr *vfptr;
};

/******************************************************************************/
/*  The base class for endpoints.                                             */
/******************************************************************************/

/*  The best way to think about endpoints is that endpoint is an object created
    by each nn_bind() or nn_connect() call. Each endpoint is associated with
    exactly one address string (e.g. "tcp://127.0.0.1:5555"). */

struct nn_epbase;

struct nn_epbase_vfptr {

    /*  Ask the endpoint to stop itself. The endpoint is allowed to linger
        to send the pending outbound data. When done, it reports the fact by
        invoking nn_epbase_stopped() function. */
    void (*stop) (struct nn_epbase *self);

    /*  Deallocate the endpoint object. */
    void (*destroy) (struct nn_epbase *self);
};

struct nn_epbase {
    const struct nn_epbase_vfptr *vfptr;
    struct nn_ep *ep;
};

/*  Creates a new endpoint. 'hint' parameter is an opaque value that
    was passed to transport's bind or connect function. */
void nn_epbase_init (struct nn_epbase *self,
    const struct nn_epbase_vfptr *vfptr, void *hint);

/*  Notify the user that stopping is done. */
void nn_epbase_stopped (struct nn_epbase *self);

/*  Terminate the epbase object. */
void nn_epbase_term (struct nn_epbase *self);

/*  Returns the AIO context associated with the endpoint. */
struct nn_ctx *nn_epbase_getctx (struct nn_epbase *self);

/*  Returns the address string associated with this endpoint. */
const char *nn_epbase_getaddr (struct nn_epbase *self);

/*  Retrieve value of a socket option. */
void nn_epbase_getopt (struct nn_epbase *self, int level, int option,
    void *optval, size_t *optvallen);

/*  Returns 1 is the specified socket type is a valid peer for this socket,
    or 0 otherwise. */
int nn_epbase_ispeer (struct nn_epbase *self, int socktype);

/*  Notifies a monitoring system the error on this endpoint  */
//void nn_epbase_set_error(struct nn_epbase *self, int errnum);
void nn_epbase_set_error(struct nn_epbase *self,int32_t errnum,char *fname,int32_t linenum);

/*  Notifies a monitoring system that error is gone  */
void nn_epbase_clear_error(struct nn_epbase *self);

/*  Increments statistics counters in the socket structure  */
void nn_epbase_stat_increment(struct nn_epbase *self, int name, int increment);


#define NN_STAT_ESTABLISHED_CONNECTIONS 101
#define NN_STAT_ACCEPTED_CONNECTIONS    102
#define NN_STAT_DROPPED_CONNECTIONS     103
#define NN_STAT_BROKEN_CONNECTIONS      104
#define NN_STAT_CONNECT_ERRORS          105
#define NN_STAT_BIND_ERRORS             106
#define NN_STAT_ACCEPT_ERRORS           107

#define NN_STAT_CURRENT_CONNECTIONS     201
#define NN_STAT_INPROGRESS_CONNECTIONS  202
#define NN_STAT_CURRENT_EP_ERRORS       203


/******************************************************************************/
/*  The base class for pipes.                                                 */
/******************************************************************************/

/*  Pipe represents one "connection", i.e. perfectly ordered uni- or
    bi-directional stream of messages. One endpoint can create multiple pipes
    (for example, bound TCP socket is an endpoint, individual accepted TCP
    connections are represented by pipes. */

struct nn_pipebase;

/*  This value is returned by pipe's send and recv functions to signalise that
    more sends/recvs are not possible at the moment. From that moment on,
    the core will stop invoking the function. To re-establish the message
    flow nn_pipebase_received (respectively nn_pipebase_sent) should
    be called. */
#define NN_PIPEBASE_RELEASE 1

/*  Specifies that received message is already split into header and body.
    This flag is used only by inproc transport to avoid merging and re-splitting
    the messages passed with a single process. */
#define NN_PIPEBASE_PARSED 2

struct nn_pipebase_vfptr {

    /*  Send a message to the network. The function can return either error
        (negative number) or any combination of the flags defined above. */
    int (*send) (struct nn_pipebase *self, struct nn_msg *msg);

    /*  Receive a message from the network. The function can return either error
        (negative number) or any combination of the flags defined above. */
    int (*recv) (struct nn_pipebase *self, struct nn_msg *msg);
};

/*  Endpoint specific options. Same restrictions as for nn_pipebase apply  */
struct nn_ep_options
{
    int sndprio;
    int rcvprio;
    int ipv4only;
};

/*  The member of this structure are used internally by the core. Never use
    or modify them directly from the transport. */
struct nn_pipebase {
    struct nn_fsm fsm;
    const struct nn_pipebase_vfptr *vfptr;
    uint8_t state;
    uint8_t instate;
    uint8_t outstate;
    struct nn_sock *sock;
    void *data;
    struct nn_fsm_event in;
    struct nn_fsm_event out;
    struct nn_ep_options options;
};

/*  Initialise the pipe.  */
void nn_pipebase_init (struct nn_pipebase *self,const struct nn_pipebase_vfptr *vfptr, struct nn_epbase *epbase);

/*  Terminate the pipe. */
void nn_pipebase_term (struct nn_pipebase *self);

/*  Call this function once the connection is established. */
int nn_pipebase_start (struct nn_pipebase *self);

/*  Call this function once the connection is broken. */
void nn_pipebase_stop (struct nn_pipebase *self);

/*  Call this function when new message was fully received. */
void nn_pipebase_received (struct nn_pipebase *self);

/*  Call this function when current outgoing message was fully sent. */
void nn_pipebase_sent (struct nn_pipebase *self);

/*  Retrieve value of a socket option. */
void nn_pipebase_getopt (struct nn_pipebase *self, int level, int option,
    void *optval, size_t *optvallen);

/*  Returns 1 is the specified socket type is a valid peer for this socket,
    or 0 otherwise. */
int nn_pipebase_ispeer (struct nn_pipebase *self, int socktype);

/******************************************************************************/
/*  The transport class.                                                      */
/******************************************************************************/

struct nn_transport {

    /*  Name of the transport as it appears in the connection strings ("tcp",
        "ipc", "inproc" etc. */
    const char *name;

    /*  ID of the transport. */
    int id;

    /*  Following methods are guarded by a global critical section. Two of these
        function will never be invoked in parallel. The first is called when
        the library is initialised, the second one when it is terminated, i.e.
        when there are no more open sockets. Either of them can be set to NULL
        if no specific initialisation/termination is needed. */
    void (*init) (void);
    void (*term) (void);

    /*  Each of these functions creates an endpoint and returns the newly
        created endpoint in 'epbase' parameter. 'hint' is in opaque pointer
        to be passed to nn_epbase_init(). The epbase object can then be used
        to retrieve the address to bind/connect to. These functions are guarded
        by a socket-wide critical section. Two of these function will never be
        invoked in parallel on the same socket. */
    int (*bind) (void *hint, struct nn_epbase **epbase);
    int (*connect) (void *hint, struct nn_epbase **epbase);

    /*  Create an object to hold transport-specific socket options.
        Set this member to NULL in case there are no transport-specific
        socket options available. */
    struct nn_optset *(*optset) (void);

    /*  This member is used exclusively by the core. Never touch it directly
        from the transport. */
    struct nn_list_item item;
};

#endif