
#ifndef _INCLUDED_session_private
#define _INCLUDED_session_private

#include "stdapi.h"
#include "dbgalloc.h"
#include "dbll.h"
#include "prlock.h"
#include "prinrval.h"
#include "GDisp.h"
#include "hashtable.h"
#include "config.h"

#include "gap_log.h"
#include "gap_session.h"
#include "gap_connection.h"

typedef enum session_state {
    GAP_SESSION_STATE_ACTIVE = 1,
    GAP_SESSION_STATE_ACCEPTED = 2,
    GAP_SESSION_STATE_INACTIVE = 3,
    GAP_SESSION_STATE_CLOSING = 4
} session_state_t;

typedef enum session_role {
    GAP_SESSION_ROLE_SERVER = 1,
    GAP_SESSION_ROLE_CLIENT = 2
} session_role_t;

struct gapcx_struct;
struct gap_transaction_struct;

struct gap_session_struct {
    DBLL_DECLARE(struct gap_session_struct);
    char id[256];
    unsigned int idlen;
    char host[256];
    unsigned short port;
    char sport[8];
    session_role_t role;
    session_state_t state;
    int ntransactions;
    int current_transaction_id;

    /* unsollicited connection close callback */
    void (*connection_close_cb)(void *);
    void *connection_close_arg;

    /* unsollicited session close callback */
    void (*session_close_cb)(void *);
    gap_session_is_open_t session_open_cb;
    void *session_close_arg;

    gap_session_recv_request_t recv_request;

    conn_t *conn;
    hashtable_t *transactions;
    hashtable_t *private;

    struct gap_transaction_struct *current_transaction;

    struct gapcx_struct *cx;
    PRLock *lock;
    GDisp *worker;
};

struct gapcx_struct {
    struct DBLL_HANDLE sessions;
    PRLock *sessions_lock;
    GDispCx *gdc;
    gap_accept_cb_t session_accepted;
    hashtable_t *login_requests;
    PRLock *new_session_lock;
    hashtable_t *session_table;
    unsigned int nsessions;
    int shutdown;
    void *uctx;
    gap_conn_ctx_t *conn_ctx;
    unsigned short listen_port;
};


/*****************************************************
 *		  transactions
 *****************************************************/
#define MAX_RETRIES 4
#define CIDMAXLEN 32

typedef enum gap_transaction_type_s {
    TXN_UNKNOWN = 0,
    TXN_ONEWAY = 1,
    TXN_TWOWAY = 2
} gap_transaction_type_t;

typedef enum gap_transaction_role_s {
    TXN_INITIATOR = 1,
    TXN_RECEIVER = 2
} transaction_role_t;

typedef enum gap_transaction_state_s {
    RR_REQUEST_SENT		= 0x01,
    RR_REPLY_SENT		= 0x02,
    RR_REQUEST_RECEIVED		= 0x04,
    RR_REPLY_RECEIVED		= 0x08,
    RR_LOGIN_REQUEST_SENT	= 0x10,
    RR_LOGIN_REQUEST_RECEIVED	= 0x20,
    RR_LOGIN_REPLY_SENT		= 0x40,
    RR_LOGIN_REPLY_RECEIVED	= 0x80
} transaction_state_t;

struct gap_transaction_struct {
    gap_session_t *session;
    char *sid;
    char id[CIDMAXLEN+1];
    gap_session_got_ack_t transport_ack;
    gap_session_got_reply_t protocol_ack;
    gap_session_is_open_t isopen_ack;
    void *ack_ctx;
    int rc;
    transaction_state_t state;
    hashtable_t *table;		/* table where this transaction is stored */

    PRIntervalTime start_time;
    int sendack_received;
    int complete;
    PRLock *lock;

    transaction_role_t role;

    gap_transaction_type_t type;

    /*
     * Sent data
     */
    char *buf;
    unsigned int buflen;
    int ntries;			/* number of retries left */

    /*
     * received data
     */
    char *message;		/* base of the message data */
    char *message_ptr;		/* where to write the rest of the message */
    unsigned int message_len;	/* total length of the message data */
    char *address;		/* URL */
    unsigned int address_len;	/* and its length */
    unsigned int lover_len;	/* number of unread bytes in this message */
};


/* terminates a transaction if the send ack was received */
void transaction_end(gap_transaction_t *rr);

/* terminates a transaction */
void transaction_abort(gap_transaction_t *rr);

/* records the fact that the send ack has been received
 * terminates the transaction if complete
 */
void transaction_ack_received(gap_transaction_t *rr);

/* starts a transaction */
gap_transaction_t *transaction_begin(gap_session_t *s,
				 transaction_state_t state,
				 gap_transaction_type_t type);

/* creates a transaction object after having received 
 * a request.
 */
gap_transaction_t *transaction_accept(gap_session_t *s,
				  transaction_state_t state,
				  char *rid);

/* continues existing transaction */
gap_transaction_t *transaction_step_initiator(gap_session_t *s, char *rid);
void transaction_step_receiver(gap_transaction_t *rr);

gap_transaction_t *transaction_new(gap_session_t *s,
				   transaction_state_t state,
				   gap_transaction_type_t type,
				   transaction_role_t role);

void transaction_delete(void *arg);

/* deletes the transaction table for the session */
void transactions_delete(gap_session_t *s);

/* globals init and cleanup */
void transactions_init(gap_session_t *s);


/*****************************************************
 *		session table calls
 *****************************************************/
void session_table_init(gapcx_t *env, void (*delete_cb)(void *));
void session_table_exit(gapcx_t *env);
int session_table_new(gap_session_t *s);
int session_table_remove(gap_session_t *s);
int session_table_add(gap_session_t *s);
gap_session_t *session_table_get(gapcx_t *env, const char *id);


/*****************************************************
 *	        session data formatting 
 *****************************************************/
gap_transaction_t *session_parse(gap_session_t *s,
				 gap_transaction_t *ongoing_transaction,
				 char **in, unsigned int *inlen,
				 int *partial, int *more);

gap_transaction_t *session_format_request(gap_session_t *s,
				      char *address, unsigned int address_len,
				      char *message, unsigned int message_len,
				      gap_session_got_ack_t transport_ack,
				      gap_session_got_reply_t protocol_ack,
				      void *ctx);

gap_transaction_t *session_format_login_request(gap_session_t *s,
						char *address,
						unsigned int address_len,
						gap_session_is_open_t ack,
						void *ctx);

gap_transaction_t *session_format_reply(gap_session_t *s, 
					gap_transaction_t *rr,
					char *address,
					unsigned int address_len,
					char *message,
					unsigned int message_len,
					gap_session_got_ack_t transport_ack,
					void *ctx);

gap_transaction_t *session_send_async_request(gap_session_t *s,
					      char *address,
					      unsigned int address_len,
					      char *message,
					      unsigned int message_len,
					      gap_session_got_ack_t transport_ack,
					      gap_session_got_reply_t protocol_ack,
					      void *ctx);

gap_transaction_t *session_send_async_login_request(gap_session_t *s,
						    char *address,
						    unsigned int address_len,
						    gap_session_is_open_t ack,
						    void *ctx);

void session_send_async_reply(gap_session_t *s, 
			      gap_transaction_t *rr,
			      char *address,
			      unsigned int address_len,
			      char *message,
			      unsigned int message_len,
			      gap_session_got_ack_t transport_ack,
			      void *ctx);

/*
 * transport ack 
 */
void gap_session_tcpack(void *arg, int rc);


#endif /* _INCLUDED_session_private */

