/* 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 "xstd/h/iomanip.h"

#include "xstd/Clock.h"
#include "runtime/Agent.h"
#include "runtime/Connection.h"
#include "runtime/Xaction.h"
#include "runtime/IcpXaction.h"
#include "runtime/PageInfo.h"
#include "base/StatIntvlRec.h"
#include "runtime/StatIntvl.h"

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


int StatIntvl::TheReportCat = lgcAll;
Array<bool> StatIntvl::IsActiveCat(lgcEnd);


void StatIntvl::ActiveCat(int cat) {
	Assert(0 <= cat && cat < lgcEnd);
	IsActiveCat.put(true, cat);
}

StatIntvl::StatIntvl() {
	Assert(TheConnOpenChannel);
	theChannels <<
		TheAgentBegChannel << TheAgentEndChannel <<
		TheConnOpenChannel << TheConnEstChannel << TheConnCloseChannel <<
		TheConnSslActiveChannel <<
		TheXactBegChannel << TheXactEndChannel <<
		TheXactErrChannel << TheXactRetrChannel <<
		TheIcpXactBegChannel << TheIcpXactEndChannel << TheIcpXactErrChannel <<
		ThePageEndChannel <<
		TheWaitBegChannel << TheWaitEndChannel
		;
}

void StatIntvl::setDuration(Time start) {
	const Time dur = TheClock.time() >= start ? TheClock - start : Time(0,0);
	for (int i = 0; i < IsActiveCat.count(); ++i) {
		if (IsActiveCat[i])
			getRec(i).theDuration = dur;
	}
}

void StatIntvl::storeAll(OLog &ol, int tag) const {
	for (int i = 0; i < IsActiveCat.count(); ++i) {
		if (IsActiveCat[i]) {
			ol << bege(tag, i);
			getRec(i).store(ol);
			ol << ende;
		}
	}
}

void StatIntvl::noteAgentEvent(BcastChannel *ch, const Agent *a) {
	Assert(a);
	StatIntvlRec &rec = getRec(a->logCat());

	if (ch == TheAgentBegChannel) {
		++rec.thePopulusLvl;
	} else
	if (ch == TheAgentEndChannel) {
		--rec.thePopulusLvl;
	} else {
		Assert(false);
	}

	checkpoint();
}

void StatIntvl::noteConnEvent(BcastChannel *ch, const Connection *conn) {
	Assert(conn);
	StatIntvlRec &rec = getRec(conn->logCat());

	if (ch == TheConnOpenChannel) {
		++rec.theOpenLvl;
		// only server-side SSL connections counted here
		if (conn->sslActive())
			++rec.theSslStat.connLevel();
	} else 
	if (ch == TheConnSslActiveChannel) {
		Should(conn->sslActive());
		++rec.theSslStat.connLevel();
	} else 
	if (ch == TheConnEstChannel) {
		++rec.theEstbLvl;
	} else {
		Assert(ch == TheConnCloseChannel);
		if (!conn->bad()) {
			rec.theConnLifeTm.record((TheClock - conn->openTime()).msec());
			rec.theConnUseCnt.record(conn->useCnt());

			// record greater-than-1 depths only to get pipelining probability later
			const int depth = conn->useLevelMax();
			if (depth > 1)
				rec.theConnPipelineDepth.record(depth);
		}
		if (conn->ioCnt())
			--rec.theEstbLvl;
		--rec.theOpenLvl;
		if (conn->sslActive())
			--rec.theSslStat.connLevel();
	}

	checkpoint();
}

void StatIntvl::noteXactEvent(BcastChannel *ch, const Xaction *x) {
	Assert(x);
	StatIntvlRec &rec = getRec(x->logCat());

	if (ch == TheXactBegChannel) {
		++rec.theXactLvl;
		if (x->sslConfigured())
			++rec.theSslStat.xactLevel();
	} else
	if (ch == TheXactEndChannel) {
		const ObjId &oid = x->oid();
		const Time repTime = x->lifeTime();
		const Size repSize = x->repSize().actual();

		// stats must be recorded in only one category for totals to work!
		if (oid.basic()) {
			// XXX: calculate and use "ideal" time here
			rec.theIdealHR.record(Time(0,0), repSize, oid.offeredHit());
			rec.theRealHR.record(repTime, repSize, oid.hit());
			rec.theChbR.record(repTime, repSize, oid.cachable());
			if (oid.fill())
				rec.theFill.record(repTime, repSize);
		} else
		if (oid.repToRedir())
			rec.theRepToRedir.record(repTime, repSize);
		else
		if (oid.rediredReq())
			rec.theRediredReq.record(repTime, repSize);
		else
		if (oid.imsAny()) // should we count 304s separately?
			rec.theIms.record(repTime, repSize);
		else
		if (oid.reload())
			rec.theReload.record(repTime, repSize);
		else
		if (oid.head())
			rec.theHead.record(repTime, repSize);
		else
		if (oid.post())
			rec.thePost.record(repTime, repSize);
		else
		if (oid.put())
			rec.thePut.record(repTime, repSize);
		else
		if (oid.aborted())
			rec.theAbort.record(repTime, repSize);
		else {
			Should(false); // all categories should be accounted for
		}

		rec.theXactCnt++;
		--rec.theXactLvl;

		if (x->sslConfigured()) {
			--rec.theSslStat.xactLevel();

			// comparison with pure HTTP misses will not be accurate
			// comparison with pure HTTP hits and not-hits will be accurate
			rec.theSslStat.doneXacts().record(repTime, repSize,
				oid.basic() && oid.hit());
		}
	} else
	if (ch == TheWaitBegChannel) {
		++rec.theWaitLvl;
	} else
	if (ch == TheWaitEndChannel) {
		--rec.theWaitLvl;
	} else
	if (ch == TheXactErrChannel) {
		rec.theXactErrCnt++;
		if (x && x->started()) {
			--rec.theXactLvl;
			if (x->sslConfigured()) {
				--rec.theSslStat.xactLevel();
				rec.theSslStat.recordXactError();
			}
		}
	} else
	if (ch == TheXactRetrChannel) {
		rec.theXactRetrCnt++;
		if (x && x->started()) {
			--rec.theXactLvl;
			if (x->sslConfigured())
				--rec.theSslStat.xactLevel();
		}
	} else {
		Assert(false);
	}


	checkpoint();
}

void StatIntvl::noteIcpXactEvent(BcastChannel *ch, const IcpXaction *x) {
	Assert(x);
	StatIntvlRec &rec = getRec(x->logCat());

	if (ch == TheIcpXactEndChannel) {
		if (x->timedout())
			rec.theIcpStat.recordTimeout();
		else
			rec.theIcpStat.record(x->lifeTime(), x->repSize(), x->hit());
	}

	checkpoint();
}

void StatIntvl::notePageEvent(BcastChannel *ch, const PageInfo *p) {
	Assert(p);
	StatIntvlRec &rec = getRec(lgcCltSide);
	if (ch == ThePageEndChannel)
		rec.thePage.record(p->lifeTime, p->size);
	checkpoint();
}

bool StatIntvl::checkpoint() {
	return false;
}


syntax highlighted by Code2HTML, v. 0.9.1