//coordinates of the acceleration vector
var ax = 0, ay = 0;

//various parameters for the rotation
var x,y,z,ux,ux2,uy,uy2,uz,uz2,uxy,uyz,uxz,newX,newY,newZ,id,angle,cosAngle,sinAngle,d,elem, elemStyle, realXRatio, realYratio, fontRatio;

//interface pointer for each rotation
var interval;

var isDecelerating = false;

//onload
$(document).ready(function(){
    initCloudArray();

    //bind the mouse movement to redefine the acceleration vector and start the rotation
	$('#tag_cloud').bind('mousemove', function(e){
        if(interval == null)
            interval = setInterval('rotateCloud()', 40); //each 40ms -> 25 FPS

        isDecelerating = false;
        ax = (e.pageX - $('#tag_cloud').offset().left) -  (containerWidth / 2);
		ay = (e.pageY - $('#tag_cloud').offset().top) -  (containerHeight / 2);

        computeAxis();
	});

    //bind the mouse out event to stop the rotation movement
    $('#tag_cloud').bind('mouseleave', function (e){
        isDecelerating = true;
    });
});

//initialize the cloud
function initCloudArray()
{
    computeAxis();

    //centering each tag based on their width and height
    $('#tag_cloud .point').each(function(){
        id = this.id;
        $('#'+id).css('marginLeft','-'+($('#'+id).width()/2)+'px');
        $('#'+id).css('marginTop','-'+($('#'+id).height()/2)+'px');
    });

    //compute ratios for optimization purpose
    realXRatio = Math.round((containerWidth-(2*marge))/2);
    realYRatio = Math.round((containerHeight-(2*marge))/2);
    colorRRatio = Math.round((maximumRed-minimumRed)/2);
    colorGRatio = Math.round((maximumGreen-minimumGreen)/2);
    colorBRatio = Math.round((maximumBlue-minimumBlue)/2);
    fontRatio = (maximumFont-minimumFont)/2;
}

//compute the new axis and angle based on the mouse's position
function computeAxis()
{
    d = Math.sqrt(ax * ax + ay * ay);

    angle = (Math.PI / 5000.0 * d); //with a speed factor

    c = Math.cos(angle);
    s = Math.sin(angle);

    ux = (ay / d);
    uy = (-ax / d);

    ux2 = ux * ux;
    uy2 = uy * uy;
    uxy = ux * uy;
}

//rotate the cloud
function rotateCloud()
{
    //stopping the rotation...
    if(isDecelerating)
    {
        //diminishing the acceleration vector
        ax = (ax * 0.8).toFixed(3);
        ay = (ay * 0.8).toFixed(3);

        computeAxis();

        //... until there is no more acceleration
        if(ax == 0 && ay == 0)
        {
            clearInterval(interval);
            return;
        }
    }

    $('#tag_cloud .point').each(function(){
        id = this.id;
        x = cloudArray[id]['x'];
        y = cloudArray[id]['y'];
        z = cloudArray[id]['z'];

        //new coordinates of the point after rotating
        newX = (x*(ux2+(1-ux2)*c) + y*(uxy*(1-c)) + z*(uy*s));
        newY = (x*(uxy*(1-c)) + y*(uy2+(1-uy2)*c) + z*(-ux*s));
        newZ = (x*(-uy*s) + y*(ux*s) + z*c);

        //new style based on the new coordinates
        elemStyle = document.getElementById(id).style;
        elemStyle.left = getLeft(newX)+'px';
        elemStyle.top = getTop(newY)+'px';
        elemStyle.zIndex = getZIndex(newZ);
        elemStyle.fontSize = getFontSize(newZ)+'px';
        elemStyle.color = getRgbColor(newZ);

        //updating the coordinates in the cloud array
        cloudArray[id]['x'] = newX;
        cloudArray[id]['y'] = newY;
        cloudArray[id]['z'] = newZ;
    });
}

function getLeft(xVal)
{
    return Math.round((xVal + 1) * realXRatio + marge);
}

function getTop(yVal)
{
    return Math.round((yVal + 1) * realYRatio + marge);
}

function getRgbColor(zVal)
{
    var color = 'rgb(';
    color += Math.round((zVal + 1) * colorRRatio + minimumRed)+',';
    color += Math.round((zVal + 1) * colorGRatio + minimumGreen)+',';
    color += Math.round((zVal + 1) * colorBRatio + minimumBlue)+')';

    return color;
}

function getFontSize(zVal)
{
    return ((zVal + 1) * fontRatio + minimumFont).toFixed(2);
}

function getZIndex(zVal)
{
    return Math.round((zVal + 1) * 100);
}
