#include "netbuffer"
#include "balancer/balancer"
#include "SocketHandling/socket/socket"

unsigned Netbuffer::netwrite (Socket &s, unsigned timeout) const {
    PROFILE("Netbuffer::netwrite");

    debugmsg("About to write " << buf_sz << " bytes to fd " << s.fd()
	     << ", timeout " << timeout << '\n');
    
    if (!buf_sz)
	return (0);

    // Log to dump directory if requested
    if (config.dumpdir().length()) {
	ostringstream of;
	of << config.dumpdir() << "/" << balancer.requestnr() << "."
	   << s.fd();
	FILE *f;
	if ( (!(f = fopen (of.str().c_str(), "a"))) &&
	     (!(f = fopen (of.str().c_str(), "w"))) ) {
	    warnmsg("Cannot write traffic log " << of.str() << ": " <<
		    strerror(errno) << '\n');
	} else {
	    fwrite (buf_data, 1, buf_sz, f);
	    fclose (f);
	}
    }

    // Send to the socket
    unsigned totwritten = 0, ntries = 0;
    while (totwritten < buf_sz) {
	// Don't go beyond 5 tries.
	if (++ntries > 4) {
	    ostringstream o;
	    o << "Network writing to fd " << s.fd() << " failed, "
	      << totwritten << " bytes sent of " << buf_sz;
	    throw Error(o.str());
	}
	
	// Wait for the socket to become writeable.
	if (timeout) {
	    Fdset set (timeout);
	    set.add (s);
	    set.wait_w();
	    if (! set.writeable(s)) {
		ostringstream o;
		o << "Fd " << s.fd() << " failed to become writable within "
		  << timeout << " sec";
		throw Error(o.str());
	    }
	}

	// Push bytes
	ssize_t nwritten;
	nwritten = write (s.fd(), buf_data + totwritten, buf_sz - totwritten);

	// If any bytes were written, we're ok
	// EINVAL / EINPROGRESS errors are handled as: retry
	// All other errors mean the link is broken
	if (nwritten >= 1) {
	    ntries = 0;
	    if (config.debug()) {
		ostringstream o;
		o << "Sent " << nwritten << " bytes to fd " << s.fd() << ": ";
		for (unsigned i = totwritten; i < totwritten + nwritten; i++)
		    o << printable(buf_data[i]);
		o << "\n";
		debugmsg(o.str());
	    }
	    totwritten += nwritten;
	} else if (errno == EINVAL || errno == EINPROGRESS) {
	    msg("Write warning: " << strerror(errno) << '\n');
	} else {
	    ostringstream o;
	    o << "Write/send failed: errno=" << int(errno) << ", "
	      << strerror(errno) << ", result=" << int(nwritten);
	    throw Error(o.str());
	}
    }

    return buf_sz;
}

