
/*
* DisplayObject3D ----------------------------------------------
*/
var DisplayObject3D = function(){
	return this;
};

DisplayObject3D.prototype._x = 0;
DisplayObject3D.prototype._y = 0;

//Create 3d Points
DisplayObject3D.prototype.make3DPoint = function(x,y,z) {
	var point = {};
	point.x = x;
	point.y = y;
	point.z = z;
	return point;
};

//Create 2d Points
DisplayObject3D.prototype.make2DPoint = function(x,y, depth, scaleFactor){
	var point = {};
	point.x = x;
	point.y = y;
	point.depth = depth;
	point.scaleFactor = scaleFactor;
	return point;
};

DisplayObject3D.prototype.container = undefined;
DisplayObject3D.prototype.pointsArray = [];

DisplayObject3D.prototype.init = function (container){
	
	this.container = jQuery(container);
	this.containerId = this.container.attr("id");
	
	//if there isn't a ul than it creates a list of +'s
	if (jQuery(container+":has(ul)").length === 0){
		for (i=0; i < this.pointsArray.length; i++){
			this.container.append('<b id="item'+i+'">'+i+'</b>');
		}
	}
};	

/*
* DisplayObject3D End ----------------------------------------------
*/


/*
* Camera3D ----------------------------------------------
*/
var Camera3D = function (){};

Camera3D.prototype.x = 0;
Camera3D.prototype.y = 0;
Camera3D.prototype.z = 500;
Camera3D.prototype.focalLength = 1000;

Camera3D.prototype.scaleRatio = function(item){
	return this.focalLength/(this.focalLength + item.z - this.z);
};

Camera3D.prototype.init = function (x,y,z,focalLength){
	this.x = x;
	this.y = y;
	this.z = z;
	this.focalLength = focalLength;
};


/*
* Camera3D End ----------------------------------------------
*/


/*
* Object3D ----------------------------------------------
*/
var Object3D = function (container){
	this.container = jQuery(container);
};

Object3D.prototype.objects = [];

Object3D.prototype.addChild = function (object3D){
		
	this.objects.push(object3D);
	
	object3D.init(this.container);
	
	return object3D;
};

/*
* Object3D End ----------------------------------------------
*/


/*
* Scene3D ----------------------------------------------
*/

var Scene3D = function (){};

Scene3D.prototype.sceneItems = [];
Scene3D.prototype.addToScene = function (object){
	this.sceneItems.push(object);
};

Scene3D.prototype.Transform3DPointsTo2DPoints = function(points, axisRotations,camera){
	var TransformedPointsArray = [];
	var sx = Math.sin(axisRotations.x);
	var cx = Math.cos(axisRotations.x);
	var sy = Math.sin(axisRotations.y);
	var cy = Math.cos(axisRotations.y);
	var sz = Math.sin(axisRotations.z);
	var cz = Math.cos(axisRotations.z);
	var x,y,z, xy,xz, yx,yz, zx,zy, scaleFactor;

	var i = points.length;
	
	while (i--){
		x = points[i].x;
		y = points[i].y;
		z = points[i].z;

		// rotation around x
		xy = cx*y - sx*z;
		xz = sx*y + cx*z;
		// rotation around y
		yz = cy*xz - sy*x;
		yx = sy*xz + cy*x;
		// rotation around z
		zx = cz*yx - sz*xy;
		zy = sz*yx + cz*xy;
		
		scaleFactor = camera.focalLength/(camera.focalLength + yz);
		x = zx*scaleFactor;
		y = zy*scaleFactor;
		z = yz;
		
		var displayObject = new DisplayObject3D();
		TransformedPointsArray[i] = displayObject.make2DPoint(x, y, -z, scaleFactor);
	}
	
	return TransformedPointsArray;
};

Scene3D.prototype.renderCamera = function (camera, radius, ratio){
	
	for(var i = 0 ; i< this.sceneItems.length; i++){

		var obj = this.sceneItems[i].objects[0];
	
		var screenPoints = this.Transform3DPointsTo2DPoints(obj.pointsArray, axisRotation, camera);
		
		var hasList = (document.getElementById(obj.containerId).getElementsByTagName("ul").length > 0);
		
		for (k=0; k < obj.pointsArray.length; k++){
			var currItem = null;
			
			if (hasList){
				currItem = document.getElementById(obj.containerId).getElementsByTagName("ul")[0].getElementsByTagName("li")[k];
			}else{
				
				currItem = document.getElementById(obj.containerId).getElementsByTagName("*")[k];
			}
			
			if(currItem){
				currItem._x = screenPoints[k].x;
				currItem._y = screenPoints[k].y;
				currItem.scale = screenPoints[k].scaleFactor;
				
				currItem.style.position = "absolute";
				currItem.style.fontSize = 100*currItem.scale+'%';
				
				//currItem.style.left = currItem._x+'px';
				//currItem.style.top = currItem._y+'px';
				
				var halfWidth = (currItem.offsetWidth / 2);
				currItem.style.left = currItem._x*ratio - (halfWidth + (halfWidth*((currItem._x*ratio)/(radius*ratio))))+'px';

				
				var halfHeight = (currItem.offsetHeight / 2);
				currItem.style.top = currItem._y - (halfHeight + (halfHeight*(screenPoints[k].y/radius)))+'px';
				
				
				
				jQuery(currItem).css({opacity:(currItem.scale-.5)});

			}

			
		}

	}
};

/*
* Scene3D End ----------------------------------------------
*/


//Center for rotation
var axisRotation = new DisplayObject3D().make3DPoint(0,0,0);
var Sphere = function (radius, numOfItems){
	
	var rings = Math.ceil(Math.sqrt(numOfItems))+1;
	if(rings%2==0) rings = rings + 1;
	
	var maxSlots = Math.pow(Math.floor(rings/2), 3)+2;
	var overflow = numOfItems - maxSlots;

	for (var j = 0 ; j < rings; j++){
		var ringSize = Math.floor(rings/2) - Math.abs(0-Math.floor(rings/2)+j);
		if(ringSize==0) {
			var itemsThisRing = 1;
			var ringPct = 0;
		} else {
			var itemsThisRing = ringSize * Math.floor(rings/2);
			var ringPct = itemsThisRing / (maxSlots-2);
		}

		itemsThisRing = itemsThisRing + Math.round(overflow * ringPct);


		for (var i = 0; i < itemsThisRing; i++)	{
			var angle = i * Math.PI * 2 / itemsThisRing;
			var angleB = j * Math.PI  / (rings-1);
			
			var x =  Math.sin(angle) * Math.sin(angleB)*radius;
			var y =  Math.cos(angle) * Math.sin(angleB)*radius;
			var z =  Math.cos(angleB)* radius;
								
			this.pointsArray.push(this.make3DPoint(x,y,z));
		}
	
	};
};

Sphere.prototype = new DisplayObject3D();

// JavaScript Document
		jQuery(document).ready(function() {
			var camera = new Camera3D();
			var tags = document.getElementById("tags");
			var radius = tags.offsetHeight / 2;
			var ratio = tags.offsetWidth / tags.offsetHeight;

			tags.style.top = radius + "px";
			tags.style.left = radius*ratio + "px";
			
			camera.init(radius*ratio,radius,radius,radius*3);
			
			var container = jQuery("#tags")
			
			var item = new Object3D(container);


		
			item.addChild(new Sphere(radius,document.getElementById("tags").getElementsByTagName("li").length));
			
			var scene = new Scene3D();
			scene.addToScene(item);
			
			var maxSpeed=150;
			
			var mouseX = maxSpeed;
			var mouseY = maxSpeed;
			
			var offsetX = container.offset().left;
			var offsetY = container.offset().top-jQuery(window).scrollTop();
			var speed = 6000;
			
			var deadzoneX=(radius*ratio)/3;
			var deadzoneY=radius/3;
			
			jQuery(document).mousemove(function(e){
																		 
				var offsetY = container.offset().top-jQuery(window).scrollTop();
				
				mouseX = e.clientX - offsetX;
				mouseY = e.clientY - offsetY;
				
				
				if(mouseX < (maxSpeed*-1)) mouseX = (maxSpeed*-1);
				if(mouseY < (maxSpeed*-1)) mouseY = (maxSpeed*-1);

				if(mouseX > maxSpeed) mouseX = maxSpeed;
				if(mouseY > maxSpeed) mouseY = maxSpeed;

				if(Math.abs(mouseX) < deadzoneX) mouseX = mouseX / (deadzoneX-mouseX+1);
				if(Math.abs(mouseY) < deadzoneY) mouseY = mouseY / (deadzoneY-mouseY+1);
				
			});
			

			var animateIt = function(){

				if (mouseX != undefined){
					axisRotation.y -= (mouseX) / speed
				}
				if (mouseY != undefined){
					axisRotation.x += mouseY / speed;
				}

				scene.renderCamera(camera, radius, ratio);

			};
			
			setInterval(animateIt, 20);
			
			});
