//global variables
maxIP=4294967296; //Math.pow(2,32);

//The NetworkRange object which does the network range & subnet calc stuff
function NetworkRange(saddr,eaddr,sip,eip){
		/*
		either {
			startAddr - text ip address or null
			endAddr - text ip address or null
		}or{
			startIP - decimal ip address (optional)
			endIP - decimal ip address (optional)
		}
		*/

		if (saddr!=null){
			this.startAddr=saddr;
			this.startIP=ipToDec(saddr);
			this.endAddr=eaddr;
			this.endIP=ipToDec(eaddr);

		}else{
			this.startIP=sip;
			this.startAddr=decToIP(sip);
			this.endIP=eip;
			this.endAddr=decToIP(eip);
		}
		ui.debug("-New NetworkRange: "+this.startAddr+" - "+this.endAddr+" ( "+this.startIP+" - "+this.endIP+" ) ");
		//should check here for NaN
		if (this.startAddr=="0.0.0.0" & this.endAddr=="0.0.0.0") throw new CustomError("Invalid Range",1006,"Range appears to be invalid [0.0.0.0-0.0.0.0]");
}

//really we don't need to store both text & decimal representations of the rnage addresses, but it's convenient
NetworkRange.prototype.startAddr; //text
NetworkRange.prototype.startIP; //decimal
NetworkRange.prototype.endAddr; //text
NetworkRange.prototype.endIP; //decimal
NetworkRange.prototype.subnets; //array of Subnet objects

NetworkRange.prototype.getStartAddrHex=function(){
	octs=this.startAddr.split('.');
	out='';
	for (var i=0;i<4;i++){
		out+=decToHex(octs[i]);
	}
	return out;
}
NetworkRange.prototype.getEndAddrHex=function(){
	octs=this.endAddr.split('.');
	out='';
	for (var i=0;i<4;i++){
		out+=decToHex(octs[i]);
	}
	return out;
}

NetworkRange.prototype.intersects=function(r2){
    //compares intersection with another range
    var schk1=(((r2.startIP>=this.startIP)&(r2.startIP<=this.endIP))==1);
    var schk2=schk1||(((this.startIP>=r2.startIP)&(this.startIP<=r2.endIP))==1);
    var echk1=schk2||(((r2.endIP>=this.startIP)&(r2.endIP<=this.endIP))==1);
    var echk2=echk1||(((this.endIP>=r2.startIP)&(this.endIP<=r2.endIP))==1);
    return echk2;
}

NetworkRange.prototype.isAdjacent=function(r2){
    //compares adjacency with another range
	//this can be run with intersects to check for overlapping and contiguous ranges.
    var chk1=((r2.startIP==(this.endIP+1))==1);
    var chk2=chk1||((this.startIP==(r2.endIP+1))==1);
    return chk2;
}

NetworkRange.prototype.getSubnets=function(){
	if (!this.subnets){
		//only do this once...
		//populates subnets with an array of Subnet objects
		var gotnets=new Array();
		if (this.startIP==this.endIP){
			gotnets.push(new Subnet(this.startIP,maxIP-1)); //single ip with mask 255.255.255.255
		}else{
			try{
				var snet=this.getFirstSubnet(this.startIP,this.endIP);
				gotnets.push(snet); //push the subnet into the array
				if (snet.endIP!=this.endIP){
					var tnets=(new NetworkRange(null,null,(snet.endIP) + 1, this.endIP)).getSubnets();
					for (var aa=0;aa<tnets.length;aa++)gotnets.push(tnets[aa]);
				}
			} catch(e) {
				throw new CustomError("Subnetting Error",1005,"Unable to create subnets for range "+this.startAddr+"-"+this.endAddr);
			}
		}
		this.subnets=gotnets;
	}
	return this.subnets;
}

NetworkRange.prototype.getFirstSubnet= function (startIP,endIP){
	//alert("findLargestSubnet("+decToIP(startIP)+","+decToIP(endIP)+")");
	//handle special cases
	if (startIP%2==1) return new Subnet(startIP,maxIP-1);
	for (var hh=1;hh<4;hh++){
		var zz=Math.pow(256,hh);
		if (startIP%zz>0){
			var adjEndIP=startIP+((zz-1)-(startIP%zz));
			if(adjEndIP<endIP)endIP=adjEndIP;
			hh=5;//get out of for loop
		}
	}
	var rangeSize=endIP-startIP+1;
	var netSizelog=parseInt(custLog(rangeSize,2)); //find nearest power of 2 that fits within subnet
	//var netSize=Math.pow(2,netSizelog);
	var netSize;
	//handle whether we can divide the ip by the netsize

	for (var ii=netSizelog;ii>0;ii--){
		netSize=Math.pow(2,ii);
		if (startIP%netSize==0){
			var netMask=maxIP-netSize;
			var netEndIP=startIP+netSize-1;
			return new Subnet(startIP,netMask);
		}
	}
}

function Subnet(startIP,netMask,netMaskbits){
	//data structure to hold a subnet
	//requires either netMask or netMaskbits
	this.startIP=startIP;
	if(netMask) this.netMask=netMask;
	if(netMaskbits) this.netMaskbits=parseInt(netMaskbits);

	if (netMask){
		var netSize=maxIP-netMask;
		//alert(decToIP(startIP)+"/"+decToIP(netMask)+" size:"+netSize);
		this.endIP=startIP+netSize-1;
		//calc the maskbits
		//netMaskbits = 32-log2(maxIP-netMask)
		this.netMaskbits=32-custLog(maxIP-netMask,2);
	}else if(netMaskbits){
		//calc the netmask
		//netmask = maxIP - ((32-netMaskbits)^2)
		this.netMask= maxIP-Math.pow(2,(32-netMaskbits));
		var netSize=maxIP-netMask;
		this.endIP=startIP+netSize-1;
	}else{
		throw new CustomError("Insufficient Parameters",1004,"Subnet::constructor - Netmask parameter not supplied");
		//throw "Subnet.constructor() not supplied with netmask";
	}
	ui.debug("--New Subnet: "+this.getStartAddr()+"/"+this.getMask()+" end:"+this.getEndAddr());
}

Subnet.prototype.startIP;
Subnet.prototype.endIP;
Subnet.prototype.netMask;
Subnet.prototype.netMaskbits;

Subnet.prototype.getStartAddr=function(){
	return decToIP(this.startIP);
}

Subnet.prototype.getEndAddr=function(){
	return decToIP(this.endIP);
}

Subnet.prototype.getMask=function(){
	return decToIP(this.netMask);
}

Subnet.prototype.getMaskbits=function(){
	return this.netMaskbits;
}

Subnet.prototype.getInverseMask=function(){
	var inv=((maxIP-1)-this.netMask);
	return decToIP(inv);
}

//helper functions

function custLog(x,base) {
	// Created 1997 by Brian Risk.  http://members.aol.com/brianrisk
	return (Math.log(x))/(Math.log(base));
}

function validateIP(inVal){
	var octs=inVal.split(".");
	for(i=0;i<4;i++) if (octs[i]>255) return false;
	return true;
}

function ipToDec(inVal){
	//inVal is ip address in text format
	var octs=inVal.split(".");
	var e=(parseInt(octs[0],10)*16777216)+(parseInt(octs[1],10)*65536)+(parseInt(octs[2],10)*256)+parseInt(octs[3],10);
	return e;
}

function decToIP(inVal){
	return (inVal>>>24)+'.'+((inVal>>>16)&255)+'.'+((inVal>>>8)&255)+'.'+(inVal&255);
}


function decToHex(strNum){
//takes a single octet and returns a two digit hex number
	base = strNum / 16;
	rem = strNum % 16;
	base = base - (rem / 16);
	baseS = d2h(base);
	remS = d2h(rem);
	return baseS + '' + remS;
}

function d2h(x) {
	if((x >= 0) && (x <= 9))
		return x;
	else {
		switch(x) {
			case 10: return "A";
			case 11: return "B";
			case 12: return "C";
			case 13: return "D";
			case 14: return "E";
			case 15: return "F";
		}
	}
}

//used for debugging only...
function decToBin(inVal){
	return d2b(parseInt(inVal>>>24))+"."+d2b(parseInt((inVal>>>16)&255))+"."+d2b(parseInt((inVal>>>8)&255))+"."+d2b(parseInt(inVal&255));
}

function d2b(decimal){
	var bit8=0,bit7=0,bit6=0,bit5=0,bit4=0,bit3=0,bit2=0,bit1=0;
	if (decimal & 128) { bit8 = 1 }
	if (decimal & 64) { bit7 = 1 }
	if (decimal & 32) { bit6 = 1 }
	if (decimal & 16) { bit5 = 1 }
	if (decimal & 8) { bit4 = 1 }
	if (decimal & 4) { bit3 = 1 }
	if (decimal & 2) { bit2 = 1 }
	if (decimal & 1) { bit1 = 1 }
	return (""+bit8+bit7+bit6+bit5+bit4+bit3+bit2+bit1);
}


