/* 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/iostream.h"
#include "xstd/h/sstream.h"

#include "xstd/String.h"
#include "xstd/gadgets.h"
#include "pgl/PglRec.h"
#include "pgl/PglStringSym.h"
#include "pgl/PglNetAddrSym.h"
#include "pgl/PglArraySym.h"
#include "pgl/PglNetAddrRange.h"
#include "pgl/AddrSchemeSym.h"
#include "pgl/pglStrIs.h"



String AddrSchemeSym::TheType = "AddrScheme";

static String strKind = "kind";
static String strNet = "net";


AddrSchemeSym::AddrSchemeSym(const String &aType, PglRec *aRec): RecSym(aType, aRec), theBench(0) {
	theRec->bAdd(StringSym::TheType, strKind, 0);
}

bool AddrSchemeSym::isA(const String &type) const {
	return RecSym::isA(type) || type == TheType;
}

String AddrSchemeSym::kind() const {
	return getString(strKind);
}

ArraySym *AddrSchemeSym::robots(const BenchSym *bench, String &err) const {
	setBench(bench);
	ArraySym *res = 0; 
	err = robots(res);
	setBench(0);
	return res;
}

ArraySym *AddrSchemeSym::servers(const BenchSym *bench, String &err) const {
	setBench(bench);
	ArraySym *res = 0; 
	err = servers(res);
	setBench(0);
	return res;
}

ArraySym *AddrSchemeSym::proxies(const BenchSym *bench, String &err) const {
	setBench(bench);
	ArraySym *res = 0; 
	err = proxies(res);
	setBench(0);
	return res;
}

String AddrSchemeSym::proxies(ArraySym *&) const {
	return "address scheme does not support proxy-side addresses";
}

void AddrSchemeSym::setBench(const BenchSym *bench) const {
	theBench = bench;
}

// find min subnet that can fit maxAddrPerHost addresses
String AddrSchemeSym::minSubnet(int maxAddrPerHost, int &minSubnet) const {
	minSubnet = 32;
	int maxAddrPerSnet = 1;
	while (minSubnet > 0 && maxAddrPerSnet < maxAddrPerHost) {
		maxAddrPerSnet *= 2;
		--minSubnet;
	}
	if (maxAddrPerSnet < maxAddrPerHost || !minSubnet)
		return "not enough IP addresses";
	return String();
}

// mask.x-x.y-y
String AddrSchemeSym::ipRangeToStr(const NetAddrSym &mask, int minX, int xCnt, int minY, int yCnt) const {
	char bufMask[256];
	ofixedstream osMask(bufMask, sizeof(bufMask));

	if (mask.ifName())
		osMask << mask.ifName() << "::";

	osMask << mask.val() << ends;

	// leave the strNet part alone
	char *dot1 = strchr(bufMask, '.');
	Assert(dot1);
	char *dot2 = strchr(dot1+1, '.');
	Assert(dot2);
	dot2++;

	ofixedstream os(dot2, sizeof(bufMask) - (dot2-bufMask));
	printOctetRange(os, minX, xCnt);
	os << '.';
	printOctetRange(os, minY, yCnt);

	if (mask.val().port() >= 0)
		os << ':' << mask.val().port();

	int subnet;
	if (mask.subnet(subnet))
		os << '/' << subnet;

	os << ends;

	return bufMask;
}

void AddrSchemeSym::printOctetRange(ostream &os, int first, int count) const {
	const int last = first + count -1;
	if (first == last)
		os << first;
	else
		os << first << '-' << last;
}

void AddrSchemeSym::makeAddrSym(const NetAddrSym &mask, int x, int y, int subnet, NetAddrSym &nas) const {
	String str = ipRangeToStr(mask, x, 1, y, 1);
	NetAddr addr;
	Assert(pglIsNetAddr(str, addr));
	nas.val(addr);

	int newSnet = subnet;
	if (mask.subnet(newSnet)) {
		if (subnet > 0 && subnet != newSnet) {
			clog << mask.loc() << "warning: subnet in side's addr_mask (/" 
				<< newSnet << ") differs from computed subnet (/" 
				<< subnet << "); using /" << newSnet << endl;
		}
	}

	if (newSnet > 0) {
		nas.setSubnet(newSnet);
	}
}

// find minimum int X such that X >= n and X is divisible by factor
int AddrSchemeSym::singleDiv(int factor, double n) const {
	return (int)(factor * xceil(n, factor));
}

// find minimum int X such that X >= n/d and X is divisible by factor
// in other words,
// divide n into X groups, no more than d elements each,
// and make sure that X is divisible by factor
// return X
int AddrSchemeSym::doubleDiv(int factor, double n, double d) const {
	const double apx = xceil(n, d);
	return singleDiv(factor, apx);
}

void AddrSchemeSym::kind(const String &aKind) {
	SynSymTblItem *ki = 0;
	Assert(theRec->find(strKind, ki));
	Assert(!ki->sym());
	ki->sym(new StringSym(aKind));
}


syntax highlighted by Code2HTML, v. 0.9.1