/* IP map client-side interface
 * Dylan Simon <dylan@dylex.net>
 */

const mapBits = 18;
const mapMask = (1 << mapBits) - 1;
const maxLevel = 2;
const imgLevel = 1;

var MapIMG, MarkIMG, YouAreBOX, HostBOX, HostINFO, HostCC, NetTAB, OutlineBOX;

var baseLevel, baseOffset, baseMask, baseValue, baseShift, baseName;
var imgMode = "map";
var curValue, youValue;

function pointIp(x, y) {
	if (x < 0 || x >= 512 || y < 0 || y >= 512)
		return;
	var p = bfrev(x, y);
	return ash(p, baseShift) + baseValue;
}

function ipPoint(p) {
	if (p === undefined)
		return;
	var xy = bfmap(ash(p, -baseShift) & mapMask);
	return [xy[0], xy[1]];
}

function inRange(v)
{
	return baseMask == 0 || v >>> (32 - baseMask) == baseOffset;
}

function mapImg()
{
	if (baseLevel <= imgLevel)
		return "img/" + imgMode + baseName + ".png";
	return "mapimg.cgi?area=" + encodeURIComponent(baseName) + "&mode=" + encodeURIComponent(imgMode);
}

function setMapImg()
{
	var img = mapImg();
	var newmap = MapIMG.cloneNode(false);
	newmap.src = img;
	MapIMG.parentNode.replaceChild(newmap, MapIMG);
	MapIMG = newmap;
	// MapIMG.src = img;
}

function buttonEnable(but, en)
{
	but.disabled = !en;
	but.style.visibility = en ? "visible" : "hidden";
}

function setBase(ip, level)
{
	if (!(level >= 0 && level <= maxLevel))
		return;
	var v = listOct(ip.split(ipDelim).slice(0, level));
	if (v === undefined)
		return;
	if (v == baseOffset && level == baseLevel)
		return;
	baseLevel = level;
	baseOffset = v;
	baseMask = 8*baseLevel;
	baseValue = baseOffset << (32 - baseMask);
	baseShift = 32 - (baseMask + mapBits);
	baseName = octList(baseOffset, baseLevel).join(ipDelim);

	document.getElementById("title").textContent = "IPv4:" + baseName + (baseLevel ? "." : "");
	buttonEnable(document.getElementById("zoominbut"), baseLevel < maxLevel);
	buttonEnable(document.getElementById("zoomoutbut"), baseLevel > 0);
	document.getElementById("helpnet").textContent = baseLevel ? baseName + "/" + baseMask : "entire";
	document.getElementById("helpsubnet").textContent = baseName + (baseLevel ? "." : "") + "n/" + (baseMask+8);
	setMapImg();
	ipChange();
}

function moveMark(p, you) {
	MarkIMG.style.visibility = "hidden";
	YouAreBOX.style.visibility = "hidden";
	if (!p)
		return;
	MarkIMG.style.left = (p[0] - (MarkIMG.offsetWidth-1)/2) + "px";
	MarkIMG.style.top = (p[1] - (MarkIMG.offsetHeight-1)/2) + "px";
	MarkIMG.style.visibility = "visible";
	if (you)
	{
		YouAreBOX.style.left = (p[0] - YouAreBOX.offsetWidth/2) + "px";
		YouAreBOX.style.top = (p[1] - MarkIMG.offsetHeight/2 - YouAreBOX.offsetHeight) + "px";
		YouAreBOX.style.visibility = "visible";
	}
}

function setIp(ip, you)
{
	var v = ipValue(ip);
	curValue = v;
	if (v === undefined)
	{
		moveMark();
		return;
	}
	if (you)
		youValue = v;
	var ip = valueIp(v);
	if (!inRange(v))
		setBase(ip, baseLevel);
	moveMark(ipPoint(v), you);
	HostBOX.value = ip;
	return ip;
}

function outlineNet(net)
{
	OutlineBOX.style.visibility = "hidden";
	if (!net)
		return;

	var ns = netSplit(net);
	if (!inRange(ns[0]) || !inRange(ns[0]+ns[1]))
		return;

	var bb = bfbox(ash(ns[0], -baseShift) & mapMask, ash(ns[1], -baseShift));

	OutlineBOX.style.left = bb[0]-1 + "px";
	OutlineBOX.style.top = bb[1]-1 + "px";
	OutlineBOX.style.width = bb[2]-bb[0]+1 + "px";
	OutlineBOX.style.height = bb[3]-bb[1]+1 + "px";
	OutlineBOX.style.visibility = "visible";
}

function clearHostInfo()
{
	HostINFO.textContent = "";
	HostCC.textContent = "";
	HostCC.title = "";
	HostCC.bgColor = null;
	NetTAB.innerHTML = "";
	outlineNet();
}

function gotHostInfoError(status, msg)
{
	HostINFO.textContent = "error getting host info (" + status + ")";
}

function gotHostInfo(info)
{
	clearHostInfo();

	info = info.documentElement;
	if (info.nodeName != "hostinfo") 
	{
		HostINFO.textContent = xmlText(info);
		return;
	}

	var you = xmlElem(info, "you");

	var ip = xmlElemText(info, "ip");
	setIp(ip, !!you);

	HostINFO.textContent = xmlElemText(info, "hostname") || ip;
	if (you)
		HostINFO.textContent += " (you)";
	var cc = xmlElem(info, "country");
	if (cc)
	{
		HostCC.textContent = xmlText(cc);
		HostCC.bgColor = cc.getAttribute("color");
		HostCC.title = cc.getAttribute("name");
	}

	for (var net = xmlElem(info, "nettab").firstChild; net; net = net.nextSibling)
	{
		if (net.nodeName != "netinfo")
			continue;
		var tr = NetTAB.insertRow(-1);
		var td;

		tr.bgColor = net.getAttribute("color");

		var n = document.createElement("a");
		n.href = xmlElemText(net, "link");

		td = tr.insertCell(0);
		td.className = "netnet";
		td.textContent = xmlElemText(net, "net");
		td.setAttribute("onmouseover", "outlineNet(\"" + xmlElemText(net, "net") + "\")");
		td.setAttribute("onmouseout", "outlineNet()");

		td = tr.insertCell(1);
		td.className = "netname";
		n.className = "netname";
		n.textContent = xmlElemText(net, "name");
		td.appendChild(n);

		td = tr.insertCell(2);
		td.className = "netnote";
		td.textContent = xmlElemText(net, "note");
	}
}

function getHostInfo(h) {
	var req = new XMLHttpRequest();
	req.open("GET", "hostinfo.cgi?host="+encodeURIComponent(h), true);
	req.onload = function (evt) {
		if (evt.target.responseXML)
			return gotHostInfo(evt.target.responseXML);
		return gotHostInfoError(evt.target.status, evt.target.responseText);
	};
	req.onerror = function (evt) {
		gotHostInfoError(evt.target.status, evt.target.responseText);
	};
	req.send(null);
}

function setHost(h)
{
	var old = curValue;
	var ip = setIp(h);
	if (ip)
		h = ip;
	if (!(old === undefined) && curValue === old)
		return;

	clearHostInfo();
	HostINFO.textContent = "looking up...";
	getHostInfo(h);
}

function mapClick(ev) {
	var mapoff = elemOff(MapIMG);
	var x = ev.pageX /* + document.body.scrollLeft */ - mapoff[0];
	var y = ev.pageY /* + document.body.scrollTop */ - mapoff[1];
	var v = pointIp(x, y);
	if (v === undefined)
		return true;
	setHost(valueIp(v));
	return false;
}

function ipChange(ev) {
	setHost(HostBOX.value);
	return false;
}

function zoomIn()
{
	if (baseLevel >= maxLevel)
		return true;
	if (curValue === undefined)
		return false;
	setBase(valueIp(curValue), baseLevel+1);
}

function zoomOut()
{
	if (baseLevel <= 0)
		return true;
	setBase(valueIp(baseValue), baseLevel-1);
}

function modeChange(ev)
{
	var mode = ev.target.value;
	if (!(mode == "map" || mode == "country"))
		return;
	imgMode = mode;
	if (mode == "map")
		document.getElementById("helpmode").textContent = "organizations in the rows below";
	else if (mode == "country")
		document.getElementById("helpmode").textContent = "countries in the top right below";
	setMapImg();
}

function init() {
	MarkIMG = document.getElementById("mark");
	MapIMG = document.getElementById("mapimg");
	YouAreBOX = document.getElementById("youarehere");
	OutlineBOX = document.getElementById("outline");

	HostBOX = document.getElementById("host");
	HostINFO = document.getElementById("hostinfo");
	HostCC = document.getElementById("hostcc");
	NetTAB = document.getElementById("nettab");

	document.getElementById("mode"+imgMode).checked = true;
	setBase("0.0.0.0", 0);
	ipChange();

	var broken = document.getElementById("broken");
	broken.parentNode.removeChild(broken);
}

