var __extends = this.__extends || function (d, b) {
	for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
	function __() { this.constructor = d; }
	__.prototype = b.prototype;
	d.prototype = new __();
};

var FD;
(function (FD) {
	var TreeMapVisualisation = (function (_super) {
		__extends(TreeMapVisualisation, _super);
		function TreeMapVisualisation(debug, fluidDiagram) {
			_super.call(this, debug, fluidDiagram);
			this.defaultNode = null;
			this.rootNode = null;
		}
		
		TreeMapVisualisation.prototype.visualise = function() {
			this.backgroundColor = 0xffffff;

			this.defaultNode = this.parseData(null, this.nodes[0]);
			this.defaultNode.sort();

			this.show();

			//$('body').append('<pre>' + this.print(this.rootNode) + '</pre>');
		};

		TreeMapVisualisation.prototype.show = function(filter, x, y, dx, dy) {
			this.rootNode = this.defaultNode;

			if(filter) {
				this.rootNode = this.find(filter) || this.rootNode;
			}

			this.rootNode.frame.x = x || 0;
			this.rootNode.frame.y = y || 0;
			this.rootNode.frame.dx = dx || this.width;
			this.rootNode.frame.dy = dy || this.height;

			this.scale([this.rootNode], this.rootNode.frame.dx * this.rootNode.frame.dy / this.rootNode.getWeight());
			this.squarify(this.rootNode);

			this.draw(this.rootNode);
		};

		TreeMapVisualisation.prototype.find = function(filter) {
			var remaining = [];
			remaining.push(this.rootNode);

			while(remaining.length > 0) {
				var n = remaining.shift();
				if(n.data == filter.data) {
					return n;
				}

				if(n.isNode()) {
					remaining = remaining.concat(n.children);
				}
			}

			return null;
		};

		TreeMapVisualisation.prototype.deleteScene = function() {
			var myList = this.fluidDiagrams.nodeSceneGraph.getDescendants();
            for (var desc in myList) {
                var tmp = myList[desc];
                this.fluidDiagrams.nodeSceneGraph.remove(tmp);
            }

            for (var desc in this.fluidDiagrams.geometryObjects) {
                var tmp = this.fluidDiagrams.geometryObjects[desc];
                this.fluidDiagrams.geometryObjects[desc].remove(tmp);
            }
		};

		TreeMapVisualisation.prototype.draw = function(node) {
			var remaining = [];
			remaining.push(node);

			while(remaining.length > 0) {
				var n = remaining.shift();
				if(n.isNode()) {
					remaining = remaining.concat(n.children);
				}
				else {
					this.drawRect(n, n.parent.color);
				}
			}
		};

		TreeMapVisualisation.prototype.drawRect = function(node, color) {
			var rectShape = new THREE.Shape();
			rectShape.moveTo(node.frame.x, node.frame.y);
			rectShape.lineTo(node.frame.x + node.frame.dx, node.frame.y);
			rectShape.lineTo(node.frame.x + node.frame.dx, node.frame.y + node.frame.dy);
			rectShape.lineTo(node.frame.x, node.frame.y + node.frame.dy);
			rectShape.lineTo(node.frame.x, node.frame.y);

			var rectGeom = new THREE.ShapeGeometry(rectShape);
			var rectMesh = new THREE.Mesh(rectGeom, new THREE.MeshBasicMaterial({ 
				color: color
			}));

			rectMesh.source = node;
			this.fluidDiagrams.addSceneObject(rectMesh);

			edges = new THREE.EdgesHelper(rectMesh, 0x000000);

			edges.source = node;
			this.fluidDiagrams.addSceneObject(edges);
		};

		TreeMapVisualisation.prototype.findNode = function(x, y) {
			var remaining = [];
			remaining.push(this.rootNode);

			while(remaining.length > 0) {
				var n = remaining.shift();

				if(n.isNode()) {
					remaining = remaining.concat(n.children);
				}
				else {
					if(n.isLeaf() && n.frame.x < x && n.frame.x + n.frame.dx > x && n.frame.y < y && n.frame.y + n.frame.dy > y) {
						return n;
					}
				}
			}

			return null;
		};

		TreeMapVisualisation.prototype.squarify = function(node) {
			var children = node.children;
			if (children && children.length) {
				var rect = {x: node.frame.x, y: node.frame.y, dx: node.frame.dx, dy: node.frame.dy},
					row = [],
					remaining = children.slice(), // copy-on-write
					child,
					best = Infinity, // the best row score so far
					score, // the current row score
					u = Math.min(rect.dx, rect.dy), // initial orientation
					n;
			
				this.scale(remaining, rect.dx * rect.dy / node.getWeight());
				row.area = 0;
				
				while ((n = remaining.length) > 0) {
					row.push(child = remaining[n - 1]);
					row.area += child.area;
					if ((score = this.worst(row, u)) <= best) { // continue with this orientation
						remaining.pop();
						best = score;
					}
					else { // abort, and try a different orientation
						row.area -= row.pop().area;
						this.position(row, u, rect, false);
						u = Math.min(rect.dx, rect.dy);
						row.length = row.area = 0;
						best = Infinity;
					}
				}

				if (row.length) {
					this.position(row, u, rect, true);
					row.length = row.area = 0;
				}
				
				children.forEach(this.squarify.bind(this));
			}
		};

		TreeMapVisualisation.prototype.scale = function(children, k) {
			var i = -1,
				n = children.length,
				child,
				area;
			
			while (++i < n) {
				area = (child = children[i]).getWeight() * (k < 0 ? 0 : k);
				child.area = isNaN(area) || area <= 0 ? 0 : area;
			}
		};

		TreeMapVisualisation.prototype.worst = function(row, u) {
			var s = row.area,
				r,
				rmax = 0,
				rmin = Infinity,
				i = -1,
				n = row.length,
				ratio = 0.5 * (1 + Math.sqrt(5));
			
			while (++i < n) {
				if (!(r = row[i].area)) continue;
				if (r < rmin) rmin = r;
				if (r > rmax) rmax = r;
			}
			
			s *= s;
			u *= u;
			
			return s ? Math.max((u * rmax * ratio) / s, s / (u * rmin * ratio)) : Infinity;
		};

		TreeMapVisualisation.prototype.position = function(row, u, rect, flush) {
			var i = -1,
				n = row.length,
				x = rect.x,
				y = rect.y,
				v = u ? Math.round(row.area / u) : 0,
				o;
			
			if (u == rect.dx) { // horizontal subdivision
				if (flush || v > rect.dy) v = rect.dy; // over+underflow
				
				while (++i < n) {
					o = row[i].frame;
					o.x = x;
					o.y = y;
					o.dy = v;
					x += o.dx = Math.min(rect.x + rect.dx - x, v ? Math.round(row[i].area / v) : 0);
				}
				
				o.z = true;
				o.dx += rect.x + rect.dx - x; // rounding error
				rect.y += v;
				rect.dy -= v;
			}
			else { // vertical subdivision
				if (flush || v > rect.dx) v = rect.dx; // over+underflow
				
				while (++i < n) {
					o = row[i].frame;
					o.x = x;
					o.y = y;
					o.dx = v;
					y += o.dy = Math.min(rect.y + rect.dy - y, v ? Math.round(row[i].area / v) : 0);
				}
				
				o.z = false;
				o.dy += rect.y + rect.dy - y; // rounding error
				rect.x += v;
				rect.dx -= v;
			}
		};

		TreeMapVisualisation.prototype.parseData = function(root, node) {
			var n;

			if(!node.children || node.children.length === 0) {
				n = new FD.TreeMapLeaf(node.name.trim(), node.size);
			}
			else {
				n = new FD.TreeMapNode(node.name.trim());

				for(var child in node.children) {
					this.parseData(n, node.children[child]);
				}
			}

			if(root === null)
				return n;
			else
				return root.addNode(n);
		};

		TreeMapVisualisation.prototype.print = function(node, indent) {
			indent = indent || 1;
			var output = '';

			var i = Array(indent).join(0).split('').reduce(function(p) { return p + '  '; }, '');
			output += i + node.data + '[' + node.getWeight() + ']' + ' >> ' + node.frame.toString() + '\n';

			for(var child in node.children) {
				output += this.print(node.children[child], indent+1);
			}

			return output;
		};

		return TreeMapVisualisation;
	})(FD.FDVisualisation);
	FD.TreeMapVisualisation = TreeMapVisualisation;
})(FD || (FD = {}));