/*
 * Copyright(C) 2010 Крипто-Про, ООО
 *
 * Этот файл содержит информацию, являющуюся
 * собственностью компании Крипто-Про.
 *
 * Любая часть этого файла не может быть скопирована,
 * исправлена, переведена на другие языки,
 * локализована или модифицирована любым способом,
 * откомпилирована, передана по сети с или на
 * любую компьютерную систему без предварительного
 * заключения соглашения с компанией Крипто-Про.
 */

/*!
 * \file $RCSfile$
 * \version $Revision: 127051 $
 * \date $Date:: 2015-09-09 15:08:20 +0300#$
 * \author $Author: pav $
 *
 * \brief Реализация POSIX poll()
 *
 */

#ifndef sup_poll_h_
#define sup_poll_h_

#ifdef UNIX
    #include <poll.h>
#else
    #include <Winsock2.h>
#endif
#include "reader/support.h"
#include "reader/sup_sys_types.h"

#ifdef __cplusplus
extern "C" {
#endif

#ifdef UNIX
    typedef struct pollfd support_pollfd_t;
    typedef nfds_t support_nfds_t;

    #define SUP_POLLIN		POLLIN
    #define SUP_POLLRDNORM	POLLRDNORM
    #define SUP_POLLRDBAND	POLLRDBAND
    #define SUP_POLLPRI		POLLPRI
    #define SUP_POLLOUT		POLLOUT
    #define SUP_POLLWRNORM	POLLWRNORM
    #define SUP_POLLWRBAND	POLLWRBAND
    #define SUP_POLLERR		POLLERR
    #define SUP_POLLHUP		POLLHUP
    #define SUP_POLLNVAL	POLLNVAL
#else
    typedef struct support_pollfd_ {
	SOCKET fd;	// The following descriptor being polled. 
	short  events;	// The input event flags (see below). 
	short  revents;	// The output event flags (see below). 
    } support_pollfd_t;
    typedef int support_nfds_t;

    #define SUP_POLLIN		(SUP_POLLRDNORM|SUP_POLLRDBAND)
    #define SUP_POLLRDNORM	((short)1)
    #define SUP_POLLRDBAND	((short)1)
    #define SUP_POLLPRI		((short)1)
    #define SUP_POLLOUT		((short)8)
    #define SUP_POLLWRNORM	(SUP_POLLOUT)
    #define SUP_POLLWRBAND	((short)8)
    #define SUP_POLLERR		((short)32)
    #define SUP_POLLHUP		((short)32)
    #define SUP_POLLNVAL	((short)32)
#endif

static SUP_INLINE int 
support_poll(support_pollfd_t fds[], support_nfds_t nfds, int timeout);

#ifdef UNIX
    static SUP_INLINE int 
	support_poll(support_pollfd_t fds[], support_nfds_t nfds, int timeout)
    {
	return poll(fds, nfds, timeout);
    }
#else
    static SUP_INLINE int 
	support_poll(support_pollfd_t fds[], support_nfds_t nfds, int timeout)
    {
	int rc = -1;
	int i;
	fd_set read_fd;
	fd_set write_fd;
	fd_set excl_fd;
	struct timeval tv;
	struct timeval *ptv = NULL;
	SOCKET maxfd = 0;

	if(timeout != -1) {
	    tv.tv_usec = (timeout%1000)*1000;
	    tv.tv_sec = timeout/1000;
	    ptv = &tv;
	}
	// TODO: В Windows fd_set не является битовой маской,
	// а практически совпадает с support_pollfd_t
	FD_ZERO(&read_fd);
	FD_ZERO(&write_fd);
	FD_ZERO(&excl_fd);
	for(i = 0; i < nfds; i++) {
	    fds[i].revents = 0;
	    #ifdef _MSC_VER
	     	#pragma warning (push)
	     	#pragma warning (disable: 4127)
	    #endif
		if((SUP_POLLIN|SUP_POLLRDNORM|
		    SUP_POLLRDBAND|SUP_POLLPRI)&fds[i].events) {
			FD_SET(fds[i].fd, &read_fd);
			FD_SET(fds[i].fd, &excl_fd);
		} else if((SUP_POLLOUT|SUP_POLLWRNORM|
		    SUP_POLLWRBAND)&fds[i].events) {
			FD_SET(fds[i].fd, &write_fd);
			FD_SET(fds[i].fd, &excl_fd);
		}
	    #ifdef _MSC_VER
	     	#pragma warning (pop)
	    #endif
	    if(maxfd < fds[i].fd) {
		maxfd = fds[i].fd;
	    }
	}
	rc = select((int)(maxfd+1), &read_fd, &write_fd, &excl_fd, ptv);
	if(0 < rc) {
	    for(i = 0; i < nfds; i++) {
		if(FD_ISSET(fds[i].fd, &read_fd)) {
		    fds[i].revents = (SUP_POLLIN|SUP_POLLRDNORM|
			SUP_POLLRDBAND|SUP_POLLPRI)&fds[i].events;
		} else if(FD_ISSET(fds[i].fd, &write_fd)) {
		    fds[i].revents = (SUP_POLLOUT|SUP_POLLWRNORM|
			SUP_POLLWRBAND)&fds[i].events;
		} else if(FD_ISSET(fds[i].fd, &excl_fd)) {
		    fds[i].revents = SUP_POLLERR|SUP_POLLHUP|
			SUP_POLLNVAL;
		}
	    }	    
	}
	return rc;
    }
#endif

#ifdef __cplusplus
}
#endif
#endif /* sup_poll_h_ */
