#include "balancer"
#include "Dispatchers/tcpdispatcher/tcpdispatcher"
#include "Dispatchers/httpdispatcher/httpdispatcher"

// #define SHOWDEBUG

void Balancer::serve() {
    // Start up wakeup/checkup handlers. These are always started - even
    // when config.wakeupsec() and config.checkupsec() are not defined
    // and have value 0. Via the web interface, the values can be later
    // changed, but we want to have the checkers running always.
    if (!config.foregroundmode() && config.sport()) {
	msg("Starting wakeup thread.\n");
	Wakeupthread *wt = new Wakeupthread();
	if (!wt)
	    throw Error("Memory fault in Balancer::serve");
	wt->start();
	
	msg("Starting checkup thread.\n");
	Checkupthread *ct = new Checkupthread();
	if (!ct)
	    throw Error("Memory fault in Balancer::serve");
	ct->start();
    }

    // Write the PID file.
    if (config.pidfile() != "") {
	FILE *f;
	if (! (f = fopen(config.pidfile().c_str(), "w")) )
	    throw Error(string("Cannot write pid file ") +
			config.pidfile() + ": " + strerror(errno));
	fprintf(f, "%u\n", getpid());
	fclose(f);
    }

    // Wait for activity, serve it.
    msg("Awaiting activity on fd " << server_fd.fd() << '\n');
    MEM(Memory::mem_mark("Balancer start"));
    MEM(Memory::mem_follow(true));
    while (true) {
	MEM(Memory::mem_display());
	Fdset fdset(0);
	fdset.add(server_fd);
	fdset.wait_r();

	if (! fdset.readable(server_fd)) {
	    // We caught a signal. Either a request to report status,
	    // or to terminate.
	    msg("Interrupt seen\n");
	    if (terminate()) {
		msg("Termination requested, XR will stop.\n");
		break;
	    } else if (report()) {
		msg("Report requested\n");
		reportmsg("*** XR STATUS REPORT STARTS ***\n");
		for (unsigned i = 0; i < nbackends(); i++) {
		    reportmsg("Back end " << i << ": " <<
			      backend(i).description() << ", weight " <<
			      backend(i).weight() << '\n');
		    reportmsg("  Status: " << 
			      backend(i).availablestr() << ", " <<
			      backend(i).livestr() << '\n');
		    reportmsg("  Connections: " << 
			      backend(i).connections() <<
			      " (max " << backend(i).maxconn() << ")\n");
		    reportmsg("  Served:" << backend(i).bytesserved() <<
			      " bytes, " << backend(i).clientsserved() <<
			      " clients\n");
		}
		report(false);
		reportmsg("*** XR STATUS REPORT ENDS ***\n");
		continue;
	    } else if (restart()) {
		msg("Restart requested\n");
		config.restart();
	    } else {
		msg("Non-meaningful interrupt or select timeout, "
		     "resuming\n");
		continue;
	    }
	}

	// Got activity! Check total # of connections.
	msg("Got activity on fd " << server_fd.fd() << '\n');
	request_nr++;
	if (config.maxconn() && connections() >= config.maxconn()) {
	    msg("Not serving connection: already " <<  connections() <<
		 " connection(s) (max " << config.maxconn() << ")\n");
	    continue;
	}

	if (server_fd.fd()) {
	    // In daemon mode (server_fd > 0): Accept, serve and loop again
	    Socket clsock;
	    try {
		clsock = server_fd.accept();
	    } catch (Error const &e) {
		warnmsg(e.what() << '\n');
		clsock.close();
		continue;
	    }
	    
	    msg("Accepted connection from " <<
		inet2string(clsock.clientaddr().sin_addr) <<
		" as client fd " << clsock.fd() << '\n');	

	    // Show how we look
	    if (config.verbose()) {
		ostringstream o;
		msg("Balancer is serving " << connections() << " clients\n");
		msg("Current back end states:\n");
		for (unsigned i = 0; i < nbackends(); i++) {
		    msg("  Back end " << backend(i).description() << ": " <<
			backend(i).connections()  << " connections, max " <<
			backend(i).maxconn() << ", status " <<
			backend(i).availablestr()  << ", anticipated " <<
			IPStore::anticipated(i) << '\n');
		}
	    }

	    Dispatcher *d;
	    switch (config.stype()) {
	    case Servertype::t_tcp:
		d = new TcpDispatcher(clsock);
		break;
	    case Servertype::t_http:
		d = new HttpDispatcher(clsock);
		break;
	    default:
		throw Error("Internal error, can't choose dispatcher");
		break;
	    }

	    if (!d)
		throw Error("Memory fault: cannot instantiate dispatcher\n");

	    // Allocation boundary printout
	    if (config.debug()) {
		void *mem = malloc(16);
		free(mem);
		debugmsg("Allocation boundary at dispatcher start: " <<
			 mem << '\n');
	    }
	    #ifdef SHOWDEBUG
	    void *mem = malloc(16);
	    free(mem);
	    cout << "XR allocation at dispatcher start: " << mem << '\n';
	    #endif

	    d->start();
	} else {
	    // If fd-serving, serve and close. Don't thread it up.
	    TcpDispatcher *d;
	    
	    switch (config.stype()) {
	    case Servertype::t_tcp:
		d = new TcpDispatcher(server_fd);
		break;
	    case Servertype::t_http:
		d = new HttpDispatcher(server_fd);
		break;
	    default:
		throw Error("Internal error, can't choose dispatcher");
		break;
	    }
	    if (!d)
		throw Error("Memory fault in Balancer::serve");
	    d->execute();
	    break;
	}

	// If we exceed the max # of requests, stop..
	if (config.quitafter()) {
	    msg("Request " << requestnr() << " underway of max " <<
		 config.quitafter() << '\n');
	    if (requestnr() >=  (long)config.quitafter()) {
		msg("Max requests served, will stop.\n");
		break;
	    }
	}
    }

    // We're stopping now. If a PID stamp was created, remove it.
    if (config.pidfile() != "")
	unlink(config.pidfile().c_str());

    // Wait for running threads to die off.
    delete webinterface;
    unsigned prev_conn = 0x19081962;
    while (1) {
	unsigned curr_conn = balancer.connections();
	if (!curr_conn)
	    break;
	if (curr_conn != prev_conn) {
	    msg("There are still " << curr_conn << " connections\n");
	    prev_conn = curr_conn;
	}
	sleep(1);
    }
    msg("XR is idle, stopping.\n");
}
