#ifndef IO_FILE_DESCRIPTOR_SET_H
#define IO_FILE_DESCRIPTOR_SET_H

#ifndef _WIN32
#include <sys/select.h>
#else
#include <winsock.h>
#endif

namespace io
{

#ifdef _WIN32
	typedef SOCKET file_descriptor_type;
#else
	typedef int file_descriptor_type;
#endif

class file_descriptor_set : boost::noncopyable
{
public:
	typedef file_descriptor_type value_type;

	file_descriptor_set()
#ifndef _WIN32
		: max_( 0 )
#endif
	{
		FD_ZERO( &fd_set_ );
	}
	void insert( value_type fd )
	{
#ifndef _WIN32
		max_ = std::max( max_, fd );
#endif		
		FD_SET( fd, &fd_set_ );
	}
	void erase( value_type fd )
	{
		FD_CLR( fd, &fd_set_ );
#ifndef _WIN32
		if( fd == max_ )
		{
			--max_;
			recalc_max();
		}
#endif
	}
	bool count( value_type fd )
	{
		return (FD_ISSET( fd, &fd_set_ ) != 0) ? 1 : 0;
	}
	
private:
	fd_set fd_set_;
	friend int select( 
		file_descriptor_set & read_set, 
		file_descriptor_set & write_set, 
		file_descriptor_set & except_set,
		timeval *timeout = 0 )
	{
		int max_max = std::max( read_set.max(), std::max( write_set.max(), except_set.max() ) );
		int ret = ::select( max_max + 1, &read_set.fd_set_, &write_set.fd_set_, &except_set.fd_set_, timeout );
		read_set.recalc_max();
		write_set.recalc_max();
		except_set.recalc_max();
		return ret;
	}
	friend int select( 
		file_descriptor_set & read_set, 
		file_descriptor_set & write_set, 
		timeval *timeout = 0 )
	{
		int max_max = std::max( read_set.max(), write_set.max() );		
		int ret = ::select( max_max + 1, &read_set.fd_set_, &write_set.fd_set_, 0, timeout );
		read_set.recalc_max();
		write_set.recalc_max();
		return ret;
	}

	void recalc_max()
	{
#ifndef _WIN32
		while( max_ != 0 && count( max_ ) == 0 )
		{
			--max_;
		}
#endif
	}
	
#ifndef _WIN32
	value_type max_;
	int max() const { return max_; }
#else
	int max() const { return 0; }
#endif
};

}

#endif

