var TagCloud = new Class({
	"initialize": function(dl) {
		this.loadData(dl);
		this.createCloud();
		this.rotationInterval = 50;
		this.rotationAngle = 2 * Math.PI / 360 / 2;
		this.scale = 1;
	},
	
	"revalidate": function() {
		if (!this.shown) {
			this.shown = true;
			// TODO
			var interval = 50;
			var i = 0;
			var appearanceInterval = setInterval(function() {
				var node = this.nodeOrder[i];
				node.tween("opacity", 1);
				if (++i >= this.nodeOrder.length) {
					$clear(appearanceInterval);
				}
			}.bind(this), interval);
		}
		var coord = this.element.getCoordinates();
		this.width = coord.width;
		this.height = coord.height;
		// TODO: configurable percentages
		var startOffset = 1 / 5;
		var endOffset = 1 / 5;
		var fontScale = 1 / 100;
		this.element.setStyle("font-size",
			Math.round(
				Math.sqrt(this.width * this.width + this.height * this.height)
					* fontScale
			)
		);
		var xStartRadius = this.scale * this.width / 2 * startOffset,
			yStartRadius = this.scale * this.height / 2 * startOffset;
		var xEndRadius = this.scale * this.width / 2 * (1 - endOffset),
			yEndRadius = this.scale * this.height / 2 * (1 - endOffset);
		var xRadiusDelta = (xEndRadius - xStartRadius) / (this.tagCount - 1),
			yRadiusDelta = (yEndRadius - yStartRadius) / (this.tagCount - 1);
		var xRadius = xStartRadius, yRadius = yStartRadius;
		this.counts.each(function(count) {
			var tags = this.data[count];
			var angle, angleDelta;
			if (!this.running) {
				angle = $random(0, 360) / 360 * 2 * Math.PI;
				angleDelta = 2 * Math.PI / tags.length;
			}
			tags.each(function(tag) {
				var coord = tag.element.getCoordinates();
				tag.width = coord.width;
				tag.height = coord.height;
				if (!this.running) {
					tag.angle = angle;
					angle += angleDelta;
				}
				tag.xRadius = xRadius;
				tag.yRadius = yRadius;
				tag.revalidate();
			}.bind(this));
			xRadius += xRadiusDelta;
			yRadius += yRadiusDelta;
		}.bind(this));
		this.running = true;
	},

	"rotate": function() {
		this.rotationEnabled = true;
		(function() {
			if (this.rotationEnabled) {
				var coord = this.element.getCoordinates();
				var width = coord.width, height = coord.height;
				this.eachTag(function(tag) {
					var factor = tag.level + 1;
					var angle = this.rotationAngle / factor / factor;
					tag.rotate(angle);
				}.bind(this));
			}
		}).bind(this).periodical(this.rotationInterval);
	},

	"scaleTo": function(factor) {
		this.scale = factor;
		this.revalidate();
	},

	"eachTag": function(func) {
		for (count in this.data) {
			if (this.data[count]) {
				this.data[count].each(func);
			}
		}
	},

	"createCloud": function() {
		this.element = new Element("div");
		this.nodeOrder = [];
		this.eachTag(function(obj) {
			var node = new Element("a", {
				"href": obj.url,
				"title": obj.name + ": " + obj.count,
				"events": {
					"mouseover": function() {
						this.rotationEnabled = false;
					}.bind(this),
					"mouseout": function() {
						this.rotationEnabled = true;
					}.bind(this)
				},
				"class": "level-" + obj.level,
				"styles": {
					"opacity": 0,
					"z-index": this.counts.length - obj.level
				}
			});
			node.set("text", obj.name);
			// TODO
			var maxScale = 3;
			var perc = 100 + Math.round(
				(maxScale - 1) * 100
					* (obj.count - this.min) / (this.max - this.min));
			node.setStyle("font-size", perc + "%");
			this.element.appendChild(node);
			obj.element = node;
			this.nodeOrder.push(node);
		}.bind(this));
		this.nodeOrder.shuffle();
	},

	"loadData": function(dl) {
		this.data = {};
		this.min = 0;
		this.max = 0;
		this.tagCount = 0;
		var name, url;
		var i = 0;
		dl.getChildren().each(function(child) {
			switch (child.get("tag")) {
				case "dt":
					var a = child.getFirst();
					name = a.get("text");
					url = a.getAttribute("href");
					break;
				case "dd":
					var count = parseInt(child.get("text"));
					if (this.min == 0 || count < this.min) {
						this.min = count;
					}
					if (count > this.max) {
						this.max = count;
					}
					if (!this.data[count]) {
						this.data[count] = [];
						this.tagCount++;
					}
					this.data[count].push(
						new Tag(i++, name, count, url, this));
					break;
			}
		}.bind(this));
		var counts = [];
		for (count in this.data) {
			counts.push(count);
		}
		this.counts = counts.sort(function (a, b) { return b - a; });
		var countIndex = {};
		for (var i = 0; i < this.counts.length; i++) {
			countIndex[this.counts[i]] = i;
		}
		this.eachTag(function(tag) {
			tag.level = countIndex[tag.count];
		});
	}
});

var Tag = new Class({
	"initialize": function(id, name, count, url, cloud) {
		this.id = id;
		this.name = name;
		this.count = count;
		this.url = url;
		this.cloud = cloud;
	},
	
	"rotate": function(delta) {
		this.angle += delta;
		this.revalidate();
	},

	"revalidate": function() {
		var x = Math.round(
			this.cloud.width / 2
			+ this.xRadius * Math.cos(this.angle)
			- this.width / 2
		);
		var y = Math.round(
			this.cloud.height / 2
			+ this.yRadius * Math.sin(this.angle)
			- this.height / 2
		);
		this.element.setStyles({
			"left": x,
			"top": y
		});
	}
});

var Scaler = new Class({
	"initialize": function(cloud, maxScale, onScale) {
		this.cloud = cloud;
		this.element = new Element("div");
		this.knob = new Element("div");
		this.element.appendChild(this.knob);
		this.maxScale = maxScale;
		this.onScale = onScale;
	},

	"install": function() {
		var steps = (this.maxScale - 1) * 100;
		new Slider(this.element, this.knob, {
			"onChange": function(step) {
				var s = this.maxScale - step / 100;
				this.cloud.scaleTo(s);
				if (this.onScale) this.onScale(s);
			}.bind(this),
			"steps": steps,
			"mode": "vertical"
		}).set(steps);
	}
});

(function() {
	var cloud, scaler;

	window.addEvent("load", function() {
		var dl = $("tags");
		cloud = new TagCloud(dl);
		cloud.element.setAttribute("id", "tag-cloud");
		dl.dispose();
		document.body.appendChild(cloud.element);
		cloud.revalidate();
		cloud.rotate();
		scaler = new Scaler(cloud, 2);
		scaler.element.setAttribute("id", "tag-cloud-scaler");
		var scalerContainer = new Element("div",
			{ "id": "tag-cloud-scaler-container" });
		scalerContainer.appendChild(scaler.element);
		scalerContainer.setStyle("opacity", 0);
		document.body.appendChild(scalerContainer);
		scalerContainer.tween("opacity", 1);
		scaler.install();
	});

	window.addEvent("resize", function() {
		cloud.revalidate();
	});
})();