#include "fdset"

double Fdset::wait(bool wait_read, bool wait_write) {
    PROFILE("Fdset::wait");

    Timestamp start;
    struct timeval tv, *tvp;

    // No fd's? Nothing to wait for.
    if (set.size() < 1)
	throw Error("Internal jam in Fdset::wait(): no fd's to wait for");

    // Prepare select sets.
    FD_ZERO (&readset);
    FD_ZERO (&writeset);
    FD_ZERO (&exceptset);
    for (unsigned i = 0; i < set.size(); i++) {
	FD_SET (set[i], &readset);
	FD_SET (set[i], &writeset);
	FD_SET (set[i], &exceptset);
	debugmsg("About to wait for fd "<< set[i] << '\n');
    }

    // Prepare timout specifier.
    if (tsec) {
	tv.tv_sec = tsec;
	tv.tv_usec = 0;
	tvp = &tv;
	debugmsg("Waiting limitation: " << tsec << '\n');
    } else {
	tvp = 0;
	debugmsg("No waiting limitation\n");
    }

    // Run the select.
    if (select (FD_SETSIZE,
		wait_read ? &readset : 0,
		wait_write ? &writeset : 0,
		&exceptset, tvp) < 0) {
	if (errno != EINTR)
	    throw Error(string("Select failure: failed to wait: ") +
			strerror(errno));
	FD_ZERO(&readset);
	FD_ZERO(&writeset);
	FD_ZERO(&exceptset);
	return start.elapsed();
    }

    // Check for exceptions.
    for (unsigned i = 0; i < set.size(); i++)
	if (FD_ISSET (set[i], &exceptset)) {
	    ostringstream o;
	    o << "Exception on fd/socket " << int(set[i]);
	    throw Error(o.str());
	}

    // More debugging: What has become readable, what has become
    // writeable, also state if no change was seen
    if (config.debug()) {
	bool statechanged = false;
	for (unsigned int i = 0; i < FD_SETSIZE; i++) {
	    if (FD_ISSET(i, &readset)) {
		debugmsg("Fd " << i << " is readable\n");
		statechanged = true;
	    }
	    if (FD_ISSET(i, &writeset)) {
		debugmsg("Fd " << i << " is writeable\n");
		statechanged = true;
	    }
	}
	if (!statechanged) {
	    ostringstream o;
	    o << "Select timeout: neither of the fd's ";
	    for (unsigned int i = 0; i < set.size(); i++)
		o << set[i] << ' ';
	    o << "has shown activity in " << tsec << " sec\n";
	    debugmsg(o.str());
	}	    
    }

    // All done. Return microsecs since start.
    return start.elapsed();
}
