/*
* Copyright Lars Op den Kamp 2009
* License LGPL
* 
* Beetje uitleg over deze tool:
* Dit is een projectje om een beetje te klooien met OO in javascript.
* Het origineel dat ik gemaakt had was wat minder fancy en geschreven in PHP.
*
* Dingen met TODO moeten nog gefixt worden
*/

var netNodes = [];

/* A network node */
var netNode = Class.create();
netNode.prototype =
{
  /**
    * Constructor
    * @param _id The id of the item to create
    * @param _type The type of this item
    * @param _name The name of this item
    */
  initialize: function(_id, _type, _name) {
    this._id  = _id;
    this._type = _type;
    this._name = _name;
    
    this._nodeDistance = 80;
    this._selected = false;
    this._scaled = false;
  },
  
  /* @retval int The id of this node */
  getId : function() {
  	return this._id;
  },
  
  /* @retval string The type of this node */
  getType : function() {
  	return this._type;
  },
  
  /* @retval string The name of this node */
  getName : function() {
  	return this._name;
  },
  
  selectNode : function(setTo) {
  	this._selected = setTo;
  },
  
  /* @retval int The width of this node */
  getWidth : function() {
  	return (this.getType() == 'internet') ? 50 : 25;
  },
  
  /* @retval 1|0 Visible or not */
  isVisible : function(checkCurrent) {
  	return (checkCurrent != undefined && checkCurrent == 1) ? $('net'+this.getId()).visible() : this._vis;
  },
  
  /* @retval int The number of hops till the root node */
  hopsToInternet : function() {
  	return (this._parent == undefined) ? 0 : 1 + this.getParentNode().hopsToInternet();
  },
  
  /* @retval netNode The parent node or null */
  getParentNode : function() {
  	return (this._parent == undefined) ? null : this._parent.getParentNode();
  },
  
  getUplinkStatus : function() {
  	return (this.getType() == 'internet') ? 'online' : this.getParentConnection().getUplinkStatus();
  },
  
  getStatus : function() {
  	return (this.getType() == 'internet') ? 'online' : this.getParentConnection().getStatus();
  },
  
  /* @retval netConnection The connection to the parent node or null */
  getParentConnection : function() {
  	return (this._parent == undefined) ? null : this._parent;
  },
  
  /* @retval array[netNode] The array with child nodes */
  getChildren : function() {
  	var retval = [];
  	
  	if (this._connections != undefined)
  	{
  		for (var i = 0; i < this._connections.length; i++)
  		{
  			if (this._connections[i].getParentNode().getId() == this.getId())
  			{
  				retval[retval.length] = this._connections[i].getChildNode();
  			}
  		}
  	}
  	
  	return retval;
  },
  
  /* @retval array[netConnection] The array with child connections */
  getChildConnections : function() {
  	var retval = [];
  	
  	if (this._connections != undefined)
  	{
  		for (var i = 0; i < this._connections.length; i++)
  		{
  			if (this._connections[i].getParentNode().getId() == this.getId())
  			{
  				retval[retval.length] = this._connections[i];
  			}
  		}
  	}
  	
  	return retval;
  },
  
  /* @retval array[netConnection] The array with child connections */
  getVpnConnections : function() {
  	var retval = [];
  	
  	if (this._vpn_connections != undefined)
  	{
  		for (var i = 0; i < this._vpn_connections.length; i++)
  		{
  			if (this._vpn_connections[i].getParentNode().getId() == this.getId())
  			{
  				retval[retval.length] = this._vpn_connections[i];
  			}
  		}
  	}
  	
  	return retval;
  },
  
  /* @retval int The X ordinate of the center of the image */
  getCenterX : function() {
  	return Math.round(this.getX() + (this.getWidth() / 2));
  },
  
  /* @retval int The Y ordinate of the center of the image */
  getCenterY : function() {
  	return Math.round(this.getY() + (this.getWidth() / 2));
  },
  
  /* redraw the tree with this node in the center */
  mouseClick : function() {
  	netDrawTree(lastDepth, this);
  },
  
  /* draw this node on the screen */
  draw : function() {
  	this._createDiv();
  	this._update();
  },
  
  /* Sets a new connection to another node */
  setConnection : function(conn) {
		if (conn.getParentNode().getId() != this._id) this._parent = conn;
		
		this._addConnection(conn);
	},
	
	/* Sets a new connection to another node */
  _setVpnConnection : function(conn) {
		if (this._vpn_connections != undefined)
		{
			// check if the connection was already added
			for (var index = 0; index < this._vpn_connections.length; index++)
			{
			  var item = this._vpn_connections[index];
			  if (item.getId() == conn.getId())
			  {
			  	return;
			  }
			}
			
			this._vpn_connections[this._vpn_connections.length] = conn;
		}else
		{
			this._vpn_connections = [conn];
		}
	},
	
	/* Set visibility */
  setVis : function(_vis) {
  	if (this._vis != _vis)
  	{
  		if (this._updatedVis == 1)
  		{
  			this._updatedVis = 0;
  		}else
  		{
  			this._updatedVis = 1;
  		}
  	}
  	
  	this._vis = _vis;
  },
  
  /* Force location */
  setLocation : function(loc) {
  	if (this.getType() == 'internet')
  	{
  		this._x = loc[0];
  		this._y = loc[1];
  		this._angle = loc[2];
  	}else
  	{
	  	var pa = this.getParentNode();
	  	
	  	if (pa != null)
	  	{
				var angle = loc[2] - pa._getChildAngle(this);
				
				while (angle >= 360) angle -= 360;
				while (angle < 0) angle += 360;
				
				pa.setLocation([
					Math.round(loc[0] - (this._nodeDistance * Math.cos(deg2rad(loc[2])))),
					Math.round(loc[1] - (this._nodeDistance * Math.sin(deg2rad(loc[2])))),
					angle
				]);
			}else
			{
				alert("child node without parent found: "+this.getName());
			}
		}
  },
  
  /* Get the X ordinate */
  getX : function() {
  	if (this.getParentNode() == null)
  	{
  		if (this._x == undefined)
  		{
  			alert("root node doesn't know it's location!");
  			return -1;
  		}else
  		{
  			return this._x;
  		}
  	}else
  	{
  		var loc = this.getParentNode()._getChildLocation(this);
  		return loc[0];
  	}
  },
  
	/* Get the Y ordinate */
  getY : function() {
  	if (this.getParentNode() == null)
  	{
  		if (this._y == undefined)
  		{
  			alert("root node doesn't know it's location!");
  			return -1;
  		}else
  		{
  			return this._y;
  		}
  	}else
  	{
  		var loc = this.getParentNode()._getChildLocation(this);
  		return loc[1];
  	}
  },
  
  /* Get the angle */
  getAngle : function() {
  	if (this.getParentNode() == null)
  	{
  		if (this._angle == undefined)
  		{
  			alert("root node doesn't know it's location!");
  			return -1;
  		}else
  		{
  			return this._angle;
  		}
  	}else
  	{
  		var loc = this.getParentNode()._getChildLocation(this);
  		return loc[2];
  	}
  },
  
  /* Get the location vector */
  getLocation : function() {
  	if (this.getParentNode() == null)
  	{
  		if (this._angle == undefined || this._x == undefined || this._y == undefined)
  		{
  			alert("root node doesn't know it's location!");
  			return -1;
  		}else
  		{
  			return [this._x, this._y, this._angle];
  		}
  	}else
  	{
  		var loc = this.getParentNode()._getChildLocation(this);
  		return loc;
  	}
  },
	
	/* Used by draw() to create the div */
  _createDiv: function() {
  	if ($('net' + this.getId()) != undefined) return;
  	
    this._div = new Element('div');
		this._div.setAttribute('id', 'net'+this.getId());
		
		if (this.isVisible() == 0)
		{
			this._div.setAttribute('style',
				'position : absolute;' +
				'z-index : 50;' +
				'left : ' + this.getX() + 'px;' +
				'top : ' + this.getY() + 'px;' +
				'display : none'
			);
		}else
		{
			this._div.setAttribute('style',
				'position : absolute;' +
				'z-index : 50;' +
				'left : ' + this.getX() + 'px;' +
				'top : ' + this.getY() + 'px;'
			);
		}
		
		var img = new Element('img');
		img.setAttribute('id', 'img_'+this.getId());
		img.setAttribute('alt', this.getType());
		img.setAttribute('src', 'images/net/'+this.getType()+'.png');
		img.setAttribute('width', this.getWidth());
		img.setAttribute('onclick', 'netGetNode('+this.getId()+').mouseClick()');
		img.setAttribute('style', 'cursor:pointer');
		img.setAttribute('border', 0);
		
		var txt = new Element('div');
		txt.setAttribute('id', 'txt_'+this.getId());
		txt.setAttribute('style', 'z-index : 51;');
		txt.setAttribute('class', 'net_node_text');
		txt.update(this.getName());
		
		this._div.update(img);
		this._div.insert(new Element('br'));
		this._div.insert(txt);
		
		$('mc1').insert(this._div);
  },
  
  locationChanged : function() {
  	var l = $('net'+this.getId()).getStyle('left');
  	
  	if (l == undefined || l != this.getX())
  	{
  		return 1;
  	}else
  	{
  		l = $('net'+this.getId()).getStyle('top');
  		if (dis == undefined || dis != this.getY())
	  	{
	  		return 1;
	  	}
  	}
  	
  	return 0;
  },
  
  /* Move the div */
  _update : function() {
  	if (this._selected && !this._scaled) {
  		this._scaled = true;
  		$('txt_'+this.getId()).setStyle('font-weight: bold; text-decoration: underline; font-size: 12px;');
  	}else if (!this._selected && this._scaled) {
  		this._scaled = false;
  		$('txt_'+this.getId()).setStyle('font-weight: normal; text-decoration: none; font-size: 10px;');
  	}
  	
  	if (this.isVisible() == 1)
  	{
  		if (this.locationChanged() == 1)
  		{
  			_netAddFX([ new Effect.Move(this._div, { x: this.getX(), y: this.getY(), mode: 'absolute' }) ]);
  		}
  		if (this.isVisible(1) == 0) 
  		{
  			_netAddFX([ new Effect.Appear(this._div) ]);
  		}
		}else
		{
			if (this.locationChanged() == 1)
  		{
  			_netAddFX([ new Effect.Move(this._div, { x: this.getX(), y: this.getY(), mode: 'absolute' }) ]);
  		}
			if (this.isVisible(1) == 1) 
			{
				_netAddFX([ new Effect.Fade(this._div) ]);
			}
		}
  },
  
  /* Add a connection */
	_addConnection : function(conn) {
		if (this._connections != undefined)
		{
			// check if the connection was already added
			for (var index = 0; index < this._connections.length; index++)
			{
			  var item = this._connections[index];
			  if (item.getId() == conn.getId())
			  {
			  	return;
			  }
			}
			
			this._connections[this._connections.length] = conn;
		}else
		{
			this._connections = [conn];
		}
	},
	
  _getChildNum : function(childnode) {
  	var children = this.getChildren();
  	
  	for (var i = 0; i < children.length; i++)
		{
			if (children[i].getId() == childnode.getId())
			{
				return i;
			}
		}
		
		return -1;
  },
  
  _getChildAngle : function (childnode) {
		var angle;
		
		if (this.getType() == 'internet')
		{
			angle = this._getChildNum(childnode) * (360 / this.getAmountChildren());
		}else if (this.getAmountChildren() == 1)
		{
			angle = 0;
		}else
		{
			var partSize = 180 / (this.getAmountChildren() + 1);
			angle = (this._getChildNum(childnode) + 1) * partSize - 90;
		}
		
		return angle;
	},
  
  _getChildLocation : function(childnode) {
  	var angle = this.getAngle() + this._getChildAngle(childnode);
		
		while (angle >= 360) angle -= 360;
		while (angle < 0) angle += 360;
				
		return [
				Math.round(this.getX() + (this._nodeDistance * Math.cos(deg2rad(angle)))),
				Math.round(this.getY() + (this._nodeDistance * Math.sin(deg2rad(angle)))),
				angle
		];
  },
  
  getAmountChildren : function() {
  	return this.getChildren().length;
  },
  
  getAmountChildrenStatus : function(status) {
  	var ch = this.getChildConnections();
  	var retval = 0;
  	
  	for (var i = 0; i < ch.length; i++)
  	{
  		if (ch[i].getStatus() == status)
  		{
  			retval++;
  		}
  	}
  	
  	return retval;
  },
  
  /**
    * Get the current depth of the tree
    */
  getDepth : function() {
  	if (this._depth == undefined)
  	{
  		return 0;
  	}else
  	{
  		return this._depth;
  	}
  },
  
  markTree : function(max_depth, depth, calledFrom) {
  	if (depth == undefined) depth = 1;
		this.depth = depth;
		
		this.setVis(1);
  	//this.draw();
  	
  	if (depth < max_depth)
  	{
  		var c = this.getChildConnections();
		
			for(var i = 0; i < c.length; i++)
			{
				if (calledFrom == undefined || c[i].getChildNode().getId() != calledFrom.getId())
				{
					//c[i].draw(); // draw connection line
					c[i].getChildNode().markTree(max_depth, depth + 1, this); // draw child
				}
			}
			
			if (this.getParentNode() != null && (calledFrom == undefined || this.getParentNode().getId() != calledFrom.getId()))
			{
				//this.getParentConnection().draw();
				this.getParentNode().markTree(max_depth, depth + 1, this); // draw parent
			}
		}
  },
  
  drawVPNConnections : function() {
  	var v = this.getVpnConnections();
			
		for(var j = 0; j < v.length; j++)
		{
			v[j].draw(); // draw connection line
		}
  },
  
  drawConnections : function(max_depth, depth, calledFrom) {
  	if (depth == undefined) depth = 1;
		this.depth = depth;
		
		this.drawVPNConnections();
		
		if (depth < max_depth)
  	{
  		var c = this.getChildConnections();
		
			for(var i = 0; i < c.length; i++)
			{
				if (calledFrom == undefined || c[i].getChildNode().getId() != calledFrom.getId())
				{
					c[i].draw(); // draw connection line
					c[i].getChildNode().drawConnections(max_depth, depth + 1, this); // draw child
				}
			}
			
			if (this.getParentNode() != null && (calledFrom == undefined || this.getParentNode().getId() != calledFrom.getId()))
			{
				this.getParentConnection().draw();
				this.getParentNode().drawConnections(max_depth, depth + 1, this); // draw parent
			}
		}
  }
};

/**
  * Convert degrees to radials
  */
function deg2rad(deg) {
	return (deg * (2*Math.PI) / 360);
}

/**
  * Gets a network node
  * @param _id The id of the node
  * @param _type The type of this node
  * @param _name The name of this node
  */
function netGetNode(_id, _type, _name) {
	var retval = null;
	
	for (var i = 0; i < netNodes.length; i++)
	{
		var item = netNodes[i];
		
		if (item.getId() == _id)
		{
			retval = item;
			break;
		}
	}
	
	if (retval == null)
	{
		if (_type != undefined && _name != undefined)
		{
			retval = new netNode(_id, _type, _name);
			netNodes[netNodes.length] = retval;
		}else
		{
			retval = null;
		}
	}
	
	return retval;
}
