#ifndef LTRSRV_WAIT_H
#define LTRSRV_WAIT_H

#include <config.h>
#include <stdint.h>

#if defined _WIN32
    #include <winsock2.h>
    #include <Ws2ipdef.h>
    #include <Windows.h>
    #define L_INVALID_SOCKET INVALID_SOCKET
    #define L_SOCKET_ERROR  SOCKET_ERROR

    typedef SOCKET t_socket;
    typedef int socklen_t;


    #define L_SOCK_LAST_ERR_BLOCK() (WSAEWOULDBLOCK == WSAGetLastError())

    #define SOCK_SET_NONBLOCK(sock, err) do { \
            ULONG nonblocking = 1; \
            if (SOCKET_ERROR == ioctlsocket(sock, FIONBIO, &nonblocking)) \
                err = LTRSRV_ERR_SOCKET_SET_NBLOCK; \
        } while(0)

    #define SHUT_RDWR SD_BOTH
    #define MSG_NOSIGNAL 0

    int inet_pton(int af, const char *src, void *dst);


    #define L_SOCK_ERR_RESET(err) (err == WSAECONNRESET)


#else

    typedef int t_socket;

    #define L_INVALID_SOCKET -1
    #define L_SOCKET_ERROR   -1

    #define closesocket(sock) close(sock)

    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/select.h>
    #include <netinet/in.h>
    #include <netinet/tcp.h>
    #include <unistd.h>
    #include <netdb.h>

    #include <arpa/inet.h>
    #include <errno.h>

#ifndef HAVE_SOCKLEN_T
    typedef size_t socklen_t;
#endif

    #define L_SOCK_LAST_ERR_BLOCK() ((EAGAIN==errno) || (EWOULDBLOCK==errno))
#ifdef ECONNRESET
    #define L_SOCK_ERR_RESET(err) (err == ECONNRESET)
#else
    #define L_SOCK_ERR_RESET(err) (0)
#endif

#ifdef ECONNREFUSED
    #define L_SOCK_ERR_REFUSED(err) (err == ECONNREFUSED)
#endif

#ifdef EHOSTUNREACH
    #define L_SOCK_ERR_HOST_NOR_REACH(err) (err == EHOSTUNREACH)
#endif

    #define SOCK_SET_NONBLOCK(sock, err) do { \
            int n = fcntl(sock, F_GETFL, 0); \
            if (fcntl(sock, F_SETFL, n|O_NONBLOCK)==-1) \
            { \
                err = LTRSRV_ERR_SOCKET_SET_NBLOCK; \
            } \
        } while(0)


#endif

#ifndef MSG_NOSIGNAL
    #define MSG_NOSIGNAL 0
#endif



/** флаги при добавлении соекта в очередь ожидания */
typedef enum {
    LTRSRV_WAITFLG_SEND                 = 0x0001,
    LTRSRV_WAITFLG_RECV                 = 0x0002,
    LTRSRV_WAITFLG_TOUT                 = 0x0004,
    LTRSRV_WAITFLG_AUTODEL              = 0x0008,
    LTRSRV_WAITFLG_DELETE               = 0x0010,
    LTRSRV_WAITFLG_TIMER_HANDLE         = 0x0020,
    LTRSRV_WAITFLG_RAW_EVENT            = 0x0040,
    LTRSRV_WAITFLG_CLOSED               = 0x0080,
    /* Указание, что при добавлении таймера, который уже есть, остается таймер
     * с наименьшим оставшимся временем */
    LTRSRV_WAITFLG_TIMER_ONLY_EARLIER   = 0x0100
} t_ltrsrv_wait_flags;

/** типы событий, которые вывели скет из состояния ожидания
    (передаются в зарегистрированный callback) */
typedef enum {
    LTRSRV_EVENT_SEND_RDY  = 0x0001,
    LTRSRV_EVENT_RCV_RDY   = 0x0002,
    LTRSRV_EVENT_EXCEPTION = 0x0004,
    LTRSRV_EVENT_TOUT      = 0x0008,
    LTRSRV_EVENT_SRV_CLOSE = 0x0010,
    LTRSRV_EVENT_RAW_EVT   = 0x0020,
} t_ltrsrv_wait_event;




/** тип callback функции, вызываемой при регистрации возникновении события для
   ожидаемого сокета
   @param[in] sock   Сокет, для которого произошло событие
   @param[in] event  Флаги из t_ltrsrv_wait_event, определяющие тип произошедших событий
   @param[in] data   Привантные данные, указанные при регистрации сокета */
typedef void (*t_ltrsrv_wait_cb)(t_socket sock, int event, void* data);



int ltrsrv_wait_init(void);

/** Регистрация сокета для ожидания события (пока он станет доступен для чтения или
    записи. Если сокет уже был зарегистрирован, то данные обновляются на новые.
    @param[in] sock   Сокет, на котором будет ожидаться событие
    @param[in] flags  Флаги из t_ltrsrv_wait_flags - определяют какие события будем ожидать
    @param[in] cb     Функция, которая вызовется, когда событие произойдет
    @param[in] tout   Таймаут на событие в мс, действителен, если в flags был
                      указан LTRSRV_WAITFLG_TOUT
    @param[in] data   Приватные данные, которые будут переданы в callback как есть */
int ltrsrv_wait_add_sock(t_socket sock, int flags,
                    unsigned int tout, t_ltrsrv_wait_cb cb, void* data);

/** Помечает запись для указанного сокета, что ее нужно удалить
    @param[in] sock  Сокет, запись которого нужно удалить */
void ltrsrv_wait_remove_sock(t_socket sock);

void ltrsrv_wait_remove_tmr(const void *hnd);

int ltrsrv_wait_add_tmr(const void *hnd, int flags, unsigned int tout, t_ltrsrv_wait_cb cb, void* data);

#ifdef _WIN32
     int ltrsrv_wait_add_evt(HANDLE event, int flags, unsigned int tout, t_ltrsrv_wait_cb cb, void* data);
     void ltrsrv_wait_remove_evt(HANDLE event);
#endif


/** Функция ожидает события на всех сокетах, поставленных в очередь.
    @param[in] poll_tout    Время, через которое функция вернется, даже если
                            не произошло ни одного собыитя
    @return                 Код ошибки*/
int ltrsrv_wait_event(uint32_t poll_tout);

/** Закрытие очереди сокетов: Функция вызывает callback с событием
    #LTRSRV_EVENT_SRV_CLOSE всем зарегисрированным сокетам */
void ltrsrv_wait_close(void);


int ltrsrv_addr_to_string(const struct sockaddr *saddr, size_t addr_len, char* str, size_t str_len);


int ltrsrv_sock_recv(t_socket sock, void *data, int len, int *recvd_len);
int ltrsrv_sock_conv_err(int sockerr, int def_err_code);
int ltrsrv_sock_con_done_check(t_socket sock);



#endif
