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

#include "pgl/pgl.h"

#include "xstd/h/sstream.h"

#include "xparser/TokenSym.h"
#include "xparser/ParsSym.h"

#include "pgl/PglPp.h"
#include "pgl/PglParser.h"

#include "pgl/PglTimeSym.h"
#include "pgl/PglIntSym.h"
#include "pgl/PglListSym.h"
#include "pgl/PglContainerSym.h"
#include "pgl/PglArraySym.h"
#include "pgl/AgentSym.h"
#include "pgl/NetPipeSym.h"
#include "pgl/AddrMapSym.h"
#include "pgl/SslWrapSym.h"
#include "pgl/MembershipMapSym.h"
#include "pgl/BenchSym.h"
#include "pgl/PhaseSym.h"
#include "pgl/StatsSampleSym.h"
#include "pgl/PglStaticSemx.h"


Array<AgentSym*> PglStaticSemx::TheAgentsToUse;
Array<NetPipeSym*> PglStaticSemx::TheNetPipesToUse;
Array<AddrMapSym*> PglStaticSemx::TheAddrMapsToUse;
Array<SslWrapSym*> PglStaticSemx::TheSslWrapsToUse;
Array<ContainerSym*> PglStaticSemx::TheAddrSubstsToUse;
Array<MembershipMapSym*> PglStaticSemx::TheMembershipsToUse;
BenchSym *PglStaticSemx::TheBench = 0;

Array<PhaseSym*> PglStaticSemx::TheSchedule;
Array<StatsSampleSym*> PglStaticSemx::TheSmplSchedule;

Time PglStaticSemx::TheWorkSetLen;
int PglStaticSemx::TheWorkSetCap = -1;

static const String strAddrArr = "addr[]";


template <class Item>
static
void PglStaticSemxDestroyArray(Array<Item> &arr) {
	while (arr.count()) delete arr.pop();
}

void PglStaticSemx::Destroy() {
	PglStaticSemxDestroyArray(TheAgentsToUse);
	PglStaticSemxDestroyArray(TheNetPipesToUse);
	PglStaticSemxDestroyArray(TheAddrMapsToUse);
	PglStaticSemxDestroyArray(TheSslWrapsToUse);
	PglStaticSemxDestroyArray(TheMembershipsToUse);
	PglStaticSemxDestroyArray(TheSchedule);
	PglStaticSemxDestroyArray(TheSmplSchedule);
	delete TheBench;
}

void PglStaticSemx::Interpret(const String &fname) {
	PglPp pp(fname);
	PglParser parser(&pp);

	if (const SynSym *s = parser.parse()) {
		PglStaticSemx semx;
		semx.interpret(*s);
	} else {
		cerr << here << "internal error: failed to interpret parsed " <<
			fname << endl << xexit;
	}
}

// default implementation complaints and exits
void PglStaticSemx::callProc(const String &cname, const ListSym &args) {
	if (cname == "use") {
		use(args);
	} else
	if (cname == "schedule") {
		schedule(args);
	} else
	if (cname == "note_substitutes") {
		noteSubstitutes(args);
	} else
	if (cname == "working_set_length") {
		checkArgs(cname, 1, args);
		const TimeSym &length = (const TimeSym&)
			extractArg(cname, 0, args, TimeSym::TheType);
		TheWorkSetLen = length.val();
	} else
	if (cname == "working_set_cap") {
		checkArgs(cname, 1, args);
		const IntSym &cap = (const IntSym&)
			extractArg(cname, 0, args, IntSym::TheType);
		TheWorkSetCap = cap.val();
	} else {
		PglSemx::callProc(cname, args);
	}
}

// register things that will be actually used
void PglStaticSemx::use(const ListSym &objects) {
	for (int i = 0; i < objects.count(); ++i) {
		if (objects[i]->isA(ListSym::TheType))
			use((ListSym &)objects[i]->cast(ListSym::TheType)); // flatten
		else
		if (AgentSym *a = (AgentSym*)objects[i]->clone(AgentSym::TheType))
			TheAgentsToUse.append(a);
		else
		if (NetPipeSym *p = (NetPipeSym*)objects[i]->clone(NetPipeSym::TheType))
			TheNetPipesToUse.append(p);
		else
		if (AddrMapSym *m = (AddrMapSym*)objects[i]->clone(AddrMapSym::TheType))
			TheAddrMapsToUse.append(m);
		else
		if (SslWrapSym *m = (SslWrapSym*)objects[i]->clone(SslWrapSym::TheType))
			TheSslWrapsToUse.append(m);
		else
		if (MembershipMapSym *g = (MembershipMapSym*)objects[i]->clone(MembershipMapSym::TheType))
			TheMembershipsToUse.append(g);
		else
		if (BenchSym *b = (BenchSym*)objects[i]->clone(BenchSym::TheType)) {
			if (TheBench) {
				cerr << objects.loc() << "warning: new bench selected with use()" << endl;
				cerr << b->loc() << "possible location of the new bench declaration" << endl;
				cerr << TheBench->loc() << "possible location of the old bench declaration" << endl;
				delete TheBench;
			}
			TheBench = b;
		} else {
			cerr << objects[i]->loc() << "entry of type '" << 
				objects[i]->type() << "' in 'use()' argument list" << 
				endl << xexit;
		}
	}
}

// register stat phases that will be actually used
void PglStaticSemx::schedule(const ListSym &items) {
	for (int i = 0; i < items.count(); ++i) {
		if (items[i]->isA(ListSym::TheType))
			schedule((ListSym &)items[i]->cast(ListSym::TheType)); // flatten
		else
		if (PhaseSym *p = (PhaseSym*)items[i]->clone(PhaseSym::TheType))
			TheSchedule.append(p);
		else
		if (StatsSampleSym *s = (StatsSampleSym*)items[i]->clone(StatsSampleSym::TheType))
			TheSmplSchedule.append(s);
		else {
			cerr << items[i]->loc() << "entry of type '" << 
				items[i]->type() << "' cannot be scheduled" << endl << xexit;
		}
	}
}

// register agents that will be actually used
void PglStaticSemx::noteSubstitutes(const ListSym &groups) {
	// note: the argument list is not flattened
	for (int i = 0; i < groups.count(); ++i) {
		const SynSym &gs = *groups[i];
		if (ArraySym *a = (ArraySym*)gs.clone(strAddrArr)) {
			TheAddrSubstsToUse.append(a);
		} else {
			cerr << gs.loc() << "entry of type '" << gs.type() << 
				"' in 'note_substitutes()' argument list" << endl << xexit;
		}
	}
}


syntax highlighted by Code2HTML, v. 0.9.1