var Class = require('class-wrapper').Class;
var GraphNode = require('./src/GraphNode');

/**
 * Position node of interest
 *
 * @class
 */
var Position = Class(null, /** @lends Position.prototype */ {
	/**
	 * Node of interest
	 *
	 * @type {GraphNode}
	 */
	noi: null,

	/**
	 * Node boundaries for positioning NOI and other nodes
	 *
	 * @type {Object}
	 */
	boundaries: {
		height: 0,
		width: 0
	},

	/**
	 * Maximal allowed children in one row
	 *
	 * @type {Number}
	 */
	maxChildrenInRow: 6,

	/**
	 * Offset by Y axis of children node relative to the NOI
	 *
	 * @type {Number}
	 */
	childYOffset: 1,

	/**
	 * Offset by X axis of mixin node relative to the NOI
	 *
	 * @type {Number}
	 */
	mixinXOffset: 1,

	/**
	 * An array of parent nodes
	 *
	 * @type {Array}
	 */
	_parentNodes: [],

	/**
	 * An array of children nodes
	 *
	 * @type {Array}
	 */
	_childNodes: [],

	/**
	 * An array of mixin nodes
	 *
	 * @type {Array}
	 */
	_mixinNodes: [],

	/**
	 * Set NOI
	 *
	 * @param {GraphNode} noi - Node of interest
	 *
	 * @throws {TypeError} "noi" argument is expected to be an instance of GraphNode class
	 */
	setNOI: function(noi) {
		if (!(noi instanceof GraphNode)) {
			throw new TypeError('"noi" argument is expected to be an instance of GraphNode class');
		}

		this.noi = noi;
	},

	/**
	 * Main method that does the positioning of required nodes
	 *
	 * It executes in the sequence the folowing methods:
	 * 1. _calcBoundaries
	 * 1. positionNOI
	 * 1. positionParentNodes
	 * 1. positionChildNodes
	 * 1. positionMixinNodes
	 */
	position: function() {
		this._calcBoundaries();

		this.positionNOI();
		this.positionParentNodes();
		this.positionChildNodes();
		this.positionMixinNodes();
	},

	/**
	 * Position node of interest
	 */
	positionNOI: function() {
		this.noi.x = this.boundaries.width / 2;
		this.noi.y = this.boundaries.height;
	},

	/**
	 * Position parent nodes of the NOI
	 */
	positionParents: function() {
		this._parentNodes = this.noi.parentStack.map((node, index) => {
			node.x = this.noi.x;
			node.y = index;
		});
	},

	/**
	 * Position child nodes of the NOI
	 */
	positionChildNodes: function() {
		var y = this.boundaries.height + this._childXOffset;

		this._childNodes = this.noi.children.map((node, index) => {
			node.x = index % this._maxChildrenInRow;
			node.y = y + Math.floor(index / this._maxChildrenInRow);
		});
	},

	/**
	 * Position mixin nodes of the NOI
	 */
	positionMixinNodes: function() {
		var x = this.boundaries.width + this._mixinOffset;

		this._mixinNodes = this.noi.mixins.map((node, index) => {
			node.x = x;
			node.y = index;
		});
	},

	/**
	 * Define the base grid dimensions where nodes will be positioned
	 */
	_calcBoundaries: function() {
		this.boundaries.height = this.noi.parents.length;

		var children = this.noi.children.length;
		this.boundaries.width = (children >= this._maxChildrenInRow) ? this._maxChildrenInRow : children;
	}
});


@@ -114,16 +114,96 @@
 interest
+ and all its related nodes%0A *%0A * The position is relative to the top left corner
 %0A *%0A * @
@@ -357,442 +357,92 @@
 %09 * 
-Node boundaries for positioning NOI and other nodes%0A%09 *%0A%09 * @type %7BObject%7D%0A%09 */%0A%09boundaries: %7B%0A%09%09height: 0,%0A%09%09width: 0%0A%09%7D,%0A%0A%09/**%0A%09 * Maximal allowed children in one row%0A%09 *%0A%09 * @type %7BNumber%7D%0A%09 */%0A%09maxChildrenInRow: 6,%0A%0A%09/**%0A%09 * Offset by Y axis of children node relative to the NOI%0A%09 *%0A%09 * @type %7BNumber%7D%0A%09 */%0A%09childYOffset: 1,%0A%0A%09/**%0A%09 * Offset by X axis of mixin node relative to the NOI%0A%09 *%0A%09 * @type %7BNumber%7D%0A%09 */%0A%09mixinXOffset: 1
+Maximal allowed children in one row%0A%09 *%0A%09 * @type %7BNumber%7D%0A%09 */%0A%09maxChildrenInRow: 6
 ,%0A%0A%09
@@ -1154,31 +1154,8 @@
 ds:%0A
-%09 * 1. _calcBoundaries%0A
 %09 * 
@@ -1281,35 +1281,8 @@
 ) %7B%0A
-%09%09this._calcBoundaries();%0A%0A
 %09%09th
@@ -1478,33 +1478,48 @@
 x = 
-this.boundaries.wid
+Math.floor(this.noi.children.leng
 th / 2
+)
 ;%0A%09%09
@@ -1536,33 +1536,38 @@
  = this.
-boundaries.height
+noi.parents.length + 1
 ;%0A%09%7D,%0A%0A%09
@@ -1862,46 +1862,17 @@
 his.
-boundaries.height + this._childXOffset
+noi.y + 1
 ;%0A%0A%09
@@ -1953,93 +1953,31 @@
 ndex
- %25 this._maxChildrenInRow;%0A%09%09%09node.y = y + Math.floor(index / this._maxChildrenInRow)
+;%0A%09%09%09node.y = y + index
 ;%0A%09%09
@@ -2071,25 +2071,31 @@
 %7B%0A%09%09var 
-x
+yOffset
  = this.
 boundari
@@ -2090,44 +2090,54 @@
 his.
-boundaries.width + 
+noi.y - Math.floor(
 this.
-_
+noi.
 mixin
-Offset
+s.length / 2)
 ;%0A%0A%09
@@ -2207,17 +2207,18 @@
 ode.x = 
-x
+-1
 ;%0A%09%09%09nod
@@ -2215,32 +2215,42 @@
 -1;%0A%09%09%09node.y = 
+yOffset + 
 index;%0A%09%09%7D);%0A%09%7D,
@@ -2248,317 +2248,8 @@
 %09%09%7D)
-;%0A%09%7D,%0A%0A%09/**%0A%09 * Define the base grid dimensions where nodes will be positioned%0A%09 */%0A%09_calcBoundaries: function() %7B%0A%09%09this.boundaries.height = this.noi.parents.length;%0A%0A%09%09var children = this.noi.children.length;%0A%09%09this.boundaries.width = (children %3E= this._maxChildrenInRow) ? this._maxChildrenInRow : children
 ;%0A%09%7D

simplify the positioning of nodes