/* Web Polygraph       http://www.web-polygraph.org/
 * (C) 2003-2006 The Measurement Factory
 * Licensed under the Apache License, Version 2.0 */

#include "base/polygraph.h"

#include "base/OLog.h"
#include "runtime/LogComment.h"
#include "runtime/SharedOpts.h"
#include "runtime/StatIntvl.h"
#include "runtime/XactFarm.h"

#include "client/AsyncClt.h"
#include "client/SyncClt.h"
#include "client/PassClt.h"
#include "client/CltOpts.h"
#include "client/ServerRep.h"

#include "server/Server.h"
#include "server/SrvOpts.h"

#include "cache/Cache.h"
#include "cache/DistrPoint.h"
#include "icp/IcpProxy.h"
#include "proxy/PxyCltXact.h"
#include "proxy/PxySrvXact.h"
#include "proxy/PolyPxy.h"

#include "pgl/CacheSym.h"
#include "pgl/RobotSym.h"
#include "pgl/ServerSym.h"
#include "pgl/ProxySym.h"

#include "base/polyLogCats.h"
#include "xstd/gadgets.h"
#include "xstd/rndDistrs.h"
#include "runtime/globals.h"

typedef XactFarmT<CltXact, PxyCltXact> PxyCltXactFarm;
typedef XactFarmT<SrvXact, PxySrvXact> PxySrvXactFarm;


PolyPxy::PolyPxy() {
	theAgentType = "Proxy";
}

Agent *PolyPxy::makeAgent(const AgentSym &agent, const NetAddr &address) {
	const ProxySym *pcfg = &(const ProxySym&)agent.cast("Proxy");
	int icpSrv = -1, icpClt = -1; // port numbers
	Client *client = 0;
	Server *server = 0;
	Cache *cache = 0;

	if (const CacheSym *ccfg = pcfg->cache()) {
		cache = new Cache;
		cache->configure(ccfg);
		ccfg->icpPort(icpSrv);
	} else {
		cerr << pcfg->loc() << "must configure cache for the proxy" << endl;
		exit(-3);
	}

	if (const RobotSym *rcfg = pcfg->client()) {
		RndDistr *iad = 0;
		if (rcfg->reqInterArrival(iad)) {
			if (iad)
				client = new AsyncClt(iad);
			else
				client = new PassClt();
		} else {
			client = new SyncClt();
		}

		client->configure(rcfg, address);
		addAgent(client);
		rcfg->icpPort(icpClt);
	}

	if (const ServerSym *scfg = pcfg->server()) {
		server = new Server();
		server->configure(scfg, address);
		addAgent(server);
	}

	IcpClient *icpClient = 0;
	if (icpClt == icpSrv && icpSrv >= 0) {
		IcpProxy *i = new IcpProxy;
		i->configure(NetAddr(address.addrN(), icpSrv));
		i->cache(cache);
		addIcpAgent(i);
		icpClient = i;
	} else {
		if (icpSrv >= 0) {
			IcpServer *i = new IcpServer;
			i->configure(NetAddr(address.addrN(), icpSrv));
			i->cache(cache);
			addIcpAgent(i);
		}
		if (icpClt >= 0) {
			IcpClient *i = new IcpClient;
			i->configure(NetAddr(address.addrN(), icpClt));
			addIcpAgent(i);
			icpClient = i;
		}
	}

	if (client) {
		client->cache(cache);
		client->icpClient(icpClient);
	}

	if (server)
		server->cache(cache);

	cache->client(client);

	return server; // XXX: could be NULL and we ignore the client
}

void PolyPxy::addIcpAgent(IcpBase *icpAgent) {
	Assert(icpAgent);
	theIcpAgents.append(icpAgent);
}

void PolyPxy::configure() {
	PolyApp::configure();

	Client::Farm(new PxyCltXactFarm);
	Server::Farm(new PxySrvXactFarm);

	IcpCltXact::TheTimeout = TheCltOpts.theIcpTout;
	StatIntvl::ActiveCat(lgcCltSide);
	StatIntvl::ActiveCat(lgcSrvSide);
	StatIntvl::TheReportCat = lgcCltSide;
}

void PolyPxy::getOpts(Array<OptGrp*> &opts) {
	PolyApp::getOpts(opts);
	opts.append(&TheCltOpts);
	opts.append(&TheSrvOpts);
}

void PolyPxy::reportCfg() {
	PolyApp::reportCfg();
}

const String PolyPxy::sideName() const {
	return "proxy";
}

void PolyPxy::logState(OLog &log) {
	PolyApp::logState(log);
	Client::LogState(log);
}

void PolyPxy::startAgents() {
	Comment(1) << "starting " << theIcpAgents.count() << " ICP agents..." << endc;
	for (int i = 0; i < theIcpAgents.count(); ++i)
		theIcpAgents[i]->start();
	PolyApp::startAgents();
}

void PolyPxy::step() {
	PolyApp::step();
	DistrPoint::CallAll();
}

int PolyPxy::logCat() const {
	return lgcCltSide;
}

int main(int argc, char *argv[]) {
	PolyPxy app;
	return app.run(argc, argv);
}


syntax highlighted by Code2HTML, v. 0.9.1