#include "globus_common.h"
#include "globus_xio.h"
#include "globus_xio_tcp_driver.h"
#include "globus_xio_file_driver.h"
#include "globus_gfork.h"

#define GFORK_CROWDED_MESSAGE "421 Too busy!\r\n"


#if !defined(GFORK_I_H)
#define GFORK_I_H 1

#ifdef __GNUC__
#define GForkFuncName(func) static const char * _gfork_func_name __attribute__((__unused__)) = #func
#else
#define GForkFuncName(func) static const char * _gfork_func_name = #func
#endif

#define GForkErrorErrno(_msg, _errno) \
    globus_error_put(GForkErrorObjErrno(_msg, _errno))

#define GForkErrorObjErrno(_msg, _errno)                                    \
        globus_error_wrap_errno_error(                                      \
            GLOBUS_GFORK_CHILD_MODULE,                                      \
            (_errno),                                                       \
            GLOBUS_GFORK_ERROR_ERRNO,                                       \
            __FILE__,                                                       \
            _gfork_func_name,                                               \
            __LINE__,                                                       \
            "System error in %s",                                           \
            _msg)

#define GForkErrorStr(_msg) \
    globus_error_put(GForkErrorObjStr(_msg))

#define GForkErrorObjStr(str)                                               \
        globus_error_construct_error(                                       \
            GLOBUS_GFORK_CHILD_MODULE,                                      \
            NULL,                                                           \
            GLOBUS_GFORK_ERROR_STR,                                         \
            __FILE__,                                                       \
            _gfork_func_name,                                               \
            __LINE__,                                                       \
            "GFork error: %s",                                              \
            (str))

GlobusDebugDeclare(GLOBUS_GFORK);

#define GlobusGForkDebugPrintf(level, message)                              \
    GlobusDebugPrintf(GLOBUS_GFORK, level, message)

#define GlobusGForkDebugEnter()                                             \
    GlobusGForkDebugPrintf(                                                 \
        GLOBUS_GFORK_DEBUG_TRACE,                                           \
        ("[%s] Entering\n", _xio_name))

#define GlobusGForkDebugExit()                                              \
    GlobusGForkDebugPrintf(                                                 \
        GLOBUS_GFORK_DEBUG_TRACE,                                           \
        ("[%s] Exiting\n", _xio_name))

#define GlobusGForkDebugExitWithError()                                     \
    GlobusGForkDebugPrintf(                                                 \
        GLOBUS_GFORK_DEBUG_TRACE,                                           \
        ("[%s] Exiting with error\n", _xio_name))

#define GlobusGForkDebugState(_old, _new, _event)                           \
    GlobusGForkDebugPrintf(                                                 \
        GLOBUS_GFORK_DEBUG_STATE,                                           \
        ("State Change from %s to %s when %s\n", _old, _new, _event))

enum
{
    GLOBUS_GFORK_ERROR_ERRNO = 1,
    GLOBUS_GFORK_ERROR_STR
};

typedef enum gfork_i_msg_type_e
{
    GLOBUS_GFORK_MSG_OPEN = 'O',
    GLOBUS_GFORK_MSG_CLOSE = 'C',
    GLOBUS_GFORK_MSG_DATA = 'D'
} gfork_i_msg_type_t;

typedef struct gfork_i_msg_header_s
{
    uint64_t                size;
    pid_t                   from_pid;
    pid_t                   to_pid;
    gfork_i_msg_type_t      type;
} gfork_i_msg_header_t;

typedef struct gfork_i_msg_data_s
{
    int                                 ref;
    globus_byte_t                       buffer[1];
} gfork_i_msg_data_t;

typedef struct gfork_i_msg_s
{
    gfork_i_msg_header_t                header;
    struct gfork_i_child_handle_s *     to_kid;
    struct gfork_i_child_handle_s *     from_kid;
    void *                              user_arg;
    globus_xio_iovec_t *                iov;
    int                                 iovc;
    globus_size_t                       nbytes;
    globus_xio_iovec_t                  write_iov[2];
    globus_xio_iovec_callback_t         client_cb;
    globus_xio_iovec_callback_t         cb;
    gfork_i_msg_data_t *                data;
    globus_byte_t *                     buffer;
    struct gfork_i_lib_handle_s *       lib_handle;
} gfork_i_msg_t;

typedef enum gfork_i_state_e
{
    GFORK_STATE_NONE = 0,
    GFORK_STATE_OPEN,
    GFORK_STATE_OPENING,
    GFORK_STATE_OPENING_AND_CLOSING,
    GFORK_STATE_CLOSING,
    GFORK_STATE_CLOSED,
    GFORK_STATE_COUNT
} gfork_i_state_t;

typedef enum gfork_i_events_s
{
    GFORK_EVENT_NONE = 0,
    GFORK_EVENT_ACCEPT_CB,
    GFORK_EVENT_OPEN_RETURNS,
    GFORK_EVENT_SIGCHILD,
    GFORK_EVENT_CLOSE_RETURNS,
    GFORK_EVENT_COUNT
} gfork_i_events_t;

typedef struct gfork_i_options_s
{
    char *                              id;
    globus_list_t *                     protocol_list;
    char *                              server;
    globus_list_t *                     server_arg_list;
    int                                 port;
    int                                 instances;
    int                                 nice;
    char *                              interfajce;
    globus_list_t *                     env_list;
    uid_t                               master_user;
    char *                              master;
    int                                 master_nice;
    globus_list_t *                     master_arg_list;
    void *                              user_arg;
    globus_bool_t                       quiet;
    char *                              conf_file;
    int                                 log_level;
    FILE *                              log_fptr;

    char *                              crowded_msg;
    int                                 crowded_msg_len;

    globus_list_t *                     master_list;
} gfork_i_options_t;

typedef struct gfork_i_handle_s
{
    globus_xio_stack_t                  stack;
    char **                             server_argv;
    char **                             master_argv;
    globus_list_t *                     loaded_drivers;
    globus_xio_driver_t                 tcp_driver;
    globus_xio_server_t                 server_xio;
    gfork_i_options_t *                 opts;

    globus_list_t *                     master_list;
} gfork_i_handle_t;

typedef struct gfork_i_child_handle_s
{
    pid_t                               pid;
    int                                 write_fd;
    int                                 read_fd;
    globus_xio_handle_t                 write_xio_handle;
    globus_xio_handle_t                 read_xio_handle;
    gfork_i_handle_t *                  whos_my_daddy;
    void *                              user_arg;
    gfork_i_state_t                     state;
    globus_bool_t                       dead;
    globus_fifo_t                       write_q;
    globus_bool_t                       writting;
    globus_bool_t                       master;
} gfork_i_child_handle_t;

typedef struct gfork_i_lib_handle_s
{
    globus_xio_handle_t                 read_xio;
    globus_xio_handle_t                 write_xio;
    gfork_i_msg_header_t                header;
    globus_byte_t *                     data;
    globus_gfork_incoming_cb_t          incoming_cb;
    globus_gfork_open_func_t            open_cb;
    globus_gfork_closed_func_t          close_cb;
    globus_gfork_error_func_t           error_cb;
    globus_bool_t                       master;
    void *                              user_arg;
    globus_mutex_t                      mutex;
    gfork_i_state_t                     state;
    globus_fifo_t                       write_q;
    globus_bool_t                       writing;
    globus_object_t *                    error_obj;
} gfork_i_lib_handle_t;

typedef struct gfork_i_master_program_ent_s
{
    char *                              master;
    uid_t                               master_uid;
    int                                 master_nice;
    globus_list_t *                     master_arg_list;    
    globus_list_t *                     master_env;
} gfork_i_master_program_ent_t;

globus_result_t
gfork_i_make_xio_handle(
    globus_xio_handle_t *               xio_handle,
    int                                 fd);

void
gfork_i_state_init();

gfork_i_state_t
gfork_i_state_next(
    gfork_i_state_t                 current_state,
    gfork_i_events_t                event);

globus_result_t
globus_i_opts_to_handle(
    gfork_i_options_t *                 opts,
    gfork_i_handle_t *                  handle);

extern globus_xio_stack_t               gfork_i_file_stack;
extern globus_xio_attr_t                gfork_i_file_attr;
extern globus_xio_driver_t              gfork_i_file_driver;

extern globus_options_entry_t           gfork_l_opts_table[];

#endif
