
//requires MooTools
//requires excanvas

var timeperiod = 10;
var damping = 0.55;
var repulse = 2;
var attract = 6;
var wallrepulse = 0.2;
var updateDisplayAfterNthIteration = 20;
var activeNode = null;
var mapArea = {x:ndiagramwidth, y:(screen.height)-100};
var minSpeed = 0.2;
var maxForce = 2.0;
var editormode = false;
var showSublines = true;
var repulsivewalls = true;
var nlinkingmode = 0;

function SpiderNode(el, parent, active) {
	var index = nodes.length;
	
	//el.innerHTML += index;
	el.innerHTML += '<div class="elLinker"><img src="img/netconn/link_after.png" valign="top" onclick="startlinking(this.parentNode,2);">'
				 +  '&nbsp;<img src="img/netconn/new_before.png" valign="top" onclick="gennewnode(this.parentNode,2);">'
				 +  '&nbsp;<img src="img/mm/remove.png" onclick="removenode(this.parentNode.parentNode.spiderObj.index);">'
				 +  '&nbsp;<img src="img/netconn/new_after.png" valign="top" onclick="gennewnode(this.parentNode,1);">'
				 +  '&nbsp;<img src="img/netconn/link_before.png" valign="top" onclick="startlinking(this.parentNode,1);"></div>';
	el.innerHTML += '<div class="elEditor"><input type="text" name="el'+index+'" onclick="this.focus();"/><img src="img/mm/save.png" onclick="savenode(this.parentNode);"></div>';
	//el.innerHTML += '<div class="elEditBtns"><img src="img/mm/remove.png" onclick="removenode(this.parentNode.parentNode.spiderObj.index);"></div>';
	
	this.el = el;
	this.el.spiderObj = this;
	//this.originalPos = this.el.getPosition();
	/*if (active) {
		activeNode = this;
		$(this.el).addClass('active');
	}*/
	if(this.el.getElement('a').getAttribute('bcompleted')=='t') {
		$(this.el).addClass('completednode');
	}
	else {
		$(this.el).addClass('node');
	}
	this.index = index;
	this.ntype = 5;
	this.visible = true;
	this.hasLayout = true; 
	
	this.level = 1;
	if(this.el.getElement('a').getAttribute('nx')>0 ) {
		this.x = this.el.getElement('a').getAttribute('nx').toInt();
		this.y = this.el.getElement('a').getAttribute('ny').toInt();
		this.ntype = this.el.getElement('a').getAttribute('ntype').toInt();
	}
	else {
		//alert(parent.parentNode.spiderObj.x);
		if(parent && parent.x>0) {
			this.x = parent.x - 50 + (Math.random()*100);
			this.y = parent.y + 10 + (Math.random()*50);
		}
		else {
			this.x = Math.random()*mapArea.x;
			this.y = Math.random()*mapArea.y;
		}
	}	
	
	setTimeout('nodes['+index+'].displayInPosition();',10);
	
	this.dx = 0;
	this.dy = 0;
	this.count = 0;
	var myDragInstance = new Drag(this.el,
		{
			snap: 1,
			onSnap: function(el){
				el.addClass('dragging');
			},
			onComplete: function(el){
				el.removeClass('dragging');
			}
		}
	);
	if (this.el.childNodes[0].tagName=='A') {
		this.el.href = this.el.childNodes[0].href;
	}
	this.el.onclick = function() {
		if (activeNode) activeNode.el.removeClass('active');
		activeNode = this.spiderObj;
		activeNode.el.addClass('active');
		if(nlinkingmode>0) if(this!=linkingnode) linknode(this);
		return false;
	}
	
	this.el.ondblclick = function() {
		if(editormode) {
			el.getElements('div').setStyle('display', 'block');
			//el.getElements('div.elLinker').setStyle('display', 'none');
			el.getElement('div.elLinker').setStyle('display', 'none');
			el.getElement('a').setStyle('display', 'none');
			el.getElement('input').value = el.getElement('a').innerHTML;
			return false;
		}
	}
	
}
SpiderNode.prototype.normalizePosition = function() {
	//move node to root (outside of parental positioning)
	if (this.parent!=null) {
		$$('#moobars>ul')[1].appendChild(this.el);
	}
}
// TODO WRITE METHOD
SpiderNode.prototype.layOutChildren = function() {
	//show my child nodes in an equally spaced group around myself, instead of placing them randomly
}
SpiderNode.prototype.getForceVector = function() {
	// for each item in nodes, calculate the force
	// repulsive force is proportional to 1/distance
	var fx = 0;
	var fy = 0;
	for (var i=0;i<nodes.length;i++) {
		if (i==this.index) continue;
		if ((showSublines && !nodes[i].hasLayout) || (!showSublines && !nodes[i].visible)) continue;
		// Repulsive force (coulomb's law)
		var x1 = (nodes[i].x - this.x);
		var y1 = (nodes[i].y - this.y);
//				$('debug1').innerHTML = x1;
		//adjust for variable node size
		//var nodewidths = ((nodes[i].el.getSize().x + this.el.getSize().x)/2)

/*
		if (x1<0) {
			if (Math.abs(x1)<nodewidths) x1=0.01;
				else x1 = x1 + nodewidths;
		} else {
			if (Math.abs(x1)<nodewidths) x1=0.01;
				else x1 = x1 - nodewidths;
		}
*/

		var xsign = x1/Math.abs(x1);
		var ysign = y1/Math.abs(y1);
		var dist = Math.sqrt((x1*x1) + (y1*y1));
//		x1 = x1 + (xsign*((nodes[i].el.getSize().x + this.el.getSize().x)/2));
//		y1 = y1 + (ysign*((nodes[i].el.getSize().y + this.el.getSize().y)/2));
		var theta = Math.atan(y1/x1);
		if (x1==0) {
			theta = Math.PI/2;
			xsign = 0;
		}
		// force is based on radial distance
		var f = (repulse*500)/(dist*dist);
		
		if (Math.abs(dist)<500) {
			fx += -f * Math.cos(theta)*xsign;
			fy += -f * Math.sin(theta)*xsign;
		}
	}
	
	if(repulsivewalls) {
		// add repulsive force of the "walls"
		
		//left wall
		var xdist = this.x;//+this.el.getSize().x;
		var f = (wallrepulse*5000)/(xdist*xdist);
		fx += Math.min(2, f);
		
		//right wall
		//var rightdist = (mapArea.x-xdist);
		//var f = -(wallrepulse*500)/(rightdist*rightdist);
		//fx += Math.max(-2, f);
		
		//top wall
		var f = (wallrepulse*3000)/(this.y*this.y);
		fy += Math.min(2, f);
		
		//botttom wall
		var bottomdist = (mapArea.y-this.y);
		var f = -(wallrepulse*3000)/(bottomdist*bottomdist);
		fy += Math.max(-2, f);
	}
		
		
	
	// for each line, of which I'm a part, add an attractive force.
	for (var i=0;i<lines.length;i++) {
		//var otherend = null;
		if (lines[i].start.index == this.index) {
			otherend = lines[i].end;
		} else if (lines[i].end.index == this.index) {
			otherend = lines[i].start;
		} else continue;
		// Attractive force (hooke's law)
		var x1 = (otherend.x - this.x);
		var y1 = (otherend.y - this.y);
		var dist = Math.sqrt(x1*x1 + y1*y1);
		var xsign = x1/Math.abs(x1);
		var theta = Math.atan(y1/x1);
		if (x1==0) {
			theta = Math.PI/2;
			xsign = 0;
		}
		// force is based on radial distance
		var f = (attract*dist)/20000;
		if (Math.abs(dist)>0) {
			fx += f * Math.cos(theta)*xsign;
			fy += f * Math.sin(theta)*xsign;
		}
	}
	
	// order
	for (var i=0;i<lines.length;i++) {
		//var otherend = null;
		if (lines[i].end.index == this.index) otherend = lines[i].start;
		else continue;
		// Attractive force (hooke's law)
		var x1 = (otherend.x + 250 - this.x);
		if (x1==0) continue;
		// force is based on radial distance
		var dist = Math.sqrt(x1*x1);
		var xsign = x1/Math.abs(x1);
		var f = (attract*dist)/5000;
		if (Math.abs(dist)>0) fx += f * xsign;
	}
	for (var i=0;i<lines.length;i++) {
		//var otherend = null;
		if (lines[i].start.index == this.index) otherend = lines[i].end;
		else continue;
		// Attractive force (hooke's law)
		var x1 = (otherend.x - 250 - this.x);
		if (x1==0) continue;
		// force is based on radial distance
		var dist = Math.sqrt(x1*x1);
		var xsign = x1/Math.abs(x1);
		var f = (attract*dist)/5000;
		if (Math.abs(dist)>0) fx += f * xsign;
	}
	
		// if I'm START, attract me to the centre left
		if(this.ntype==6) {
			var x1 = (60 - this.x);
			if (x1!=0) {
				// force is based on radial distance
				var dist = Math.sqrt(x1*x1);
				var xsign = x1/Math.abs(x1);
				var f = (attract*dist)/1000;
				if (Math.abs(dist)>0) fx += f * xsign;
			}
		}
		
		// if I'm END, attract me to the centre right
		if(this.ntype==0) {
			// Attractive force (hooke's law)
			var otherend = mapArea;
			var x1 = (otherend.x -50 - this.x);
			if (x1!=0) {
				// force is based on radial distance
				var dist = Math.sqrt(x1*x1);
				var xsign = x1/Math.abs(x1);
				var f = (attract*dist)/2000;
				if (Math.abs(dist)>0) fx += f * xsign;
			}
		}
	
	//	$('debug1').innerHTML = dist;
	if (Math.abs(fx) > maxForce) fx = maxForce*(fx/Math.abs(fx));
	if (Math.abs(fy) > maxForce) fy = maxForce*(fy/Math.abs(fy));
	return {x:fx, y:fy};
}
SpiderNode.prototype.getSpeedVector = function() {
	//if(this.dx==0 && this.dy==0) alert('stopped');
	return {x:this.dx, y:this.dy};
}

SpiderNode.prototype.displayInPosition = function() {
	var showx = this.x - (this.el.getSize().x / 2);
	var showy = this.y - (this.el.getSize().y / 2);
	this.el.style.left = showx + "px";
	this.el.style.top = showy + "px";
}

SpiderNode.prototype.updatePosition = function() {


	if (this.el.hasClass("dragging")) {
		this.x = parseInt(this.el.style.left);
		this.y = parseInt(this.el.style.top);
		this.dx = 0;
		this.dy = 0;
		return;
	}
	//apply accelerations
	var forces = this.getForceVector();
//			$('debug1').innerHTML = forces.x;
	this.dx += forces.x*timeperiod;
	this.dy += forces.y*timeperiod;

	//TODO: CAP THE FORCES
	
	//			this.el.childNodes[0].innerHTML = parseInt(this.dx)+' '+parseInt(this.dy);
	this.dx = this.dx*damping;
	this.dy = this.dy*damping;

	//TODO: ADD MINIMUM SPEEDS
	if (Math.abs(this.dx)<minSpeed) this.dx=0;
	if (Math.abs(this.dy)<minSpeed) this.dy=0;
	
	if(this.dx==0 && this.dy==0) return;
	nmovingnodes++;
	
	//apply velocity vector
	this.x += this.dx*timeperiod;
	this.y += this.dy*timeperiod;
	this.x = Math.min(mapArea.x,Math.max(1,this.x));
	this.y = Math.min(mapArea.y,Math.max(1,this.y));
	//only update the display after the thousanth iteration, so it's not too wild at the start
	this.count++;
	//if (this.count<updateDisplayAfterNthIteration) return;
	// display
	var showx = this.x - (this.el.getSize().x / 2);
	var showy = this.y - (this.el.getSize().y / 2);
	this.el.style.left = showx + "px";
	this.el.style.top = showy + "px";
	
}
//////////////////////////////////////////////////////
function SpiderLine(index, startSpiderNode, finSpiderNode) {
	this.index = index;
	this.start = startSpiderNode;
	//this.startindex = startSpiderNode.index;
	this.colour = "blue";
	this.size = "thick";
	this.end = finSpiderNode;
	//this.endindex = finSpiderNode.index;
	this.count=0;
}
SpiderLine.prototype.updatePosition = function() {
	if(!(this.start.x>0)) return;
	if(!(this.start.y>0)) return;
	if(!(this.end.x>0)) return;
	if(!(this.end.y>0)) return;
	
			//wyliczamy nozke na cel
			var Ax1 = this.start.x;
			var Ay1 = this.start.y;
			var Bx1 = this.end.x;
			var By1 = this.end.y;
			var mx = Bx1-Ax1;
			var my = By1-Ay1;
			var mz = Math.sqrt(mx*mx + my*my);
			var alfa = 0;
			if(mz!=0) alfa = Math.asin(mx/mz);
			if(my==0) my = -1;
			
			var space=60;
			do {
			xx = Math.ceil(space*Math.sin(alfa));
			yy = Math.ceil(space*Math.cos(alfa))*sgn(Math.ceil(my));
			Ax = Ax1 + xx;
			Ay = Ay1 + yy;
			Bx = Bx1 - xx;
			By = By1 - yy; 
			space-=20;
			} while(Ax>Bx && space>0);
			
			mx = Bx-Ax;
			my = By-Ay;
			if(my==0) my = -1;
			mz = Math.sqrt(mx*mx + my*my);
			alfa = 0;
			if(mz!=0) alfa = Math.asin(mx/mz);
			if(my==0) my = -1;
			
			var dlr = 10;
			var beta = Math.PI/8;
			var ax = Math.ceil(dlr * Math.sin(alfa - beta));
			var ay = Math.ceil(dlr * Math.cos(alfa - beta))*sgn(Math.ceil(my));
			var Cx = Bx - ax;
			var Cy = By - ay;
			ax = Math.ceil(dlr * Math.sin(alfa + beta));
			ay = Math.ceil(dlr * Math.cos(alfa + beta))*sgn(Math.ceil(my));
			var Dx = Bx - ax;
			var Dy = By - ay;
			
	ctx.beginPath();
	ctx.moveTo(Ax, Ay);
	ctx.lineTo(Bx, By);
	ctx.lineTo(Cx, Cy);
	ctx.lineTo(Dx, Dy);
	ctx.lineTo(Bx, By);
	
	ctx.stroke();
	ctx.closePath();
}

//////////////////////////////////////////////////////
function SpiderLoop() {
	nmovingnodes = 0;
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	//update node positions
	for (var i=0;i<nodes.length;i++) {
		//TODO: replace this temporary idea
		var childActive = true; //!editormode;	//RM: change to false to hide parts of the map
		var currentNode = activeNode;
		if(currentNode!=null)
		while (currentNode.parent && (currentNode=currentNode.parent)) {
			if (currentNode == nodes[i]) childActive = true;
		}
		if(activeNode!=null)
		if (childActive || activeNode==nodes[i] || activeNode==nodes[i].parent) {
			nodes[i].visible=true;
			nodes[i].hasLayout = true;
		} else {
			nodes[i].visible=false;
			if (nodes[i].parent && nodes[i].parent.parent && nodes[i].parent.parent==activeNode) {
				nodes[i].hasLayout = true;
			} else {
				nodes[i].hasLayout = false;
			}
		}
		if (nodes[i].visible) {
			nodes[i].el.style.display="block";
		} else {
			nodes[i].el.style.display="none";
		}
		if ((showSublines && !nodes[i].hasLayout) || (!showSublines && !nodes[i].visible)) continue;
		nodes[i].updatePosition();
	}
	//display lines
	for (var i=0;i<lines.length;i++) {
		lines[i].updatePosition();
	}
	
	if(nmovingnodes==0) setTimeout('SpiderLoop()',300);
	else setTimeout('SpiderLoop()',10);
}
//////////////////////////////////////////////////////
function addList(ul, parent) {
	var mylis = ul.childNodes;
	var thislist = [];
	var linecounter = 0;
	for (var li=0;li<mylis.length;li++) {
		if (mylis[li].tagName!='LI') continue;
		var nodeno = nodes.length;
		nodes[nodeno] = new SpiderNode(mylis[li], parent);

		thislist[thislist.length] = nodes[nodeno];
		var mylicontent = mylis[li].childNodes;
		for (var i=0;i<mylicontent.length;i++) {
			if (mylicontent[i].tagName!='UL') continue;
			addList(mylicontent[i], nodes[nodeno]);
		}

		//if (parent!=null) {
		//	var lineno = lines.length;
		//	lines[lineno] = new SpiderLine(lineno, nodes[nodeno], parent);
		//}

	}
}
//////////////////////////////////////////////////////
function addCons(ul) {
	var mylis = ul.childNodes;
	var thislist = [];
	var linecounter = 0;
	for (var li=0;li<mylis.length;li++) {
		if (mylis[li].tagName!='LI') continue;
		var lineno = lines.length;
		var startowy = null;
		var endowy = null;
		
		for (var i=0;i<nodes.length;i++) {
			var dbid = nodes[i].el.getElement('a').getAttribute('dbid');
			//alert(mylis[li].getAttribute('start'));
			if(dbid==mylis[li].getAttribute('start')) startowy=nodes[i];
			if(dbid==mylis[li].getAttribute('end')) endowy=nodes[i];
		}
		
		if(startowy!=null && endowy!=null)
		lines[lineno] = new SpiderLine(lineno, startowy, endowy);
	}
}

var nodes = new Array();
var lines = new Array();
var removes = new Array();
var activenode=null;
var nmovingnodes = 0;
onload = function() {
	
	// create a misc UL to store flattened nodes
	var miscUL = document.createElement("UL");
	$('moobars').appendChild(miscUL);

	var nodeno = nodes.length;
	var myul = $$('#moobars>ul')[0];
	addList(myul, nodes[nodeno]);
	var myul2 = $$('#moocons>ul')[0];
	addCons(myul2);
	for (var i=0;i<nodes.length;i++) {
		nodes[i].normalizePosition();
	}

	setTimeout('SpiderLoop()',50);
	
	$('moobars').addClass('moobars');
	
	//CANVAS
	canvas = document.getElementById("cv");
	ctx = canvas.getContext("2d");
	ctx.lineWidth = "2";
	ctx.strokeStyle = "rgb(100, 100, 100)";
}











