var mouseX = 0, mouseY = 0,
windowHalfX = window.innerWidth / 2, windowHalfY = window.innerHeight / 2,
camera, scene, renderer, pointLight, pointLight2, pointLight3, exSphere, intSphere, stats,
material, deformationMultiplier = 60 +Math.random()*100, softSelectionDist = 50, degree = 0, radius = 100, relatedVec, morphTarget = new Array(), morphStep = 0.07, morphDone = false,
sphereMaterial, sphereMaterialWire, extMaterialWire,
gui;


init();


function init() {   
	
	//create GUI
	//var gui = new DAT.GUI();
    //gui.add(this, 'morphStep',0.01,0.5,0.01);
	
    var container, 
        canvasHeight = $(window).height() -3,
        canvasWidth = $(window).width(),
        particles = new THREE.Geometry();

    container = document.getElementById("cover_01");
    
    camera = new THREE.Camera( 75, canvasWidth/canvasHeight, 1, 10000 );
    camera.position.x = 40;
    camera.position.y = 200;
    camera.position.z = 400;
    
    scene = new THREE.Scene();

    renderer = new THREE.WebGLRenderer();
    renderer.setSize( canvasWidth ,canvasHeight);
    container.appendChild( renderer.domElement );
    
    // set up the sphere vars
    var exRadius = radius, exSegments = 20, exRings = 20,
        intRadius = radius, intSegments = 60, intRings = 60;
    
    sphereMaterial = new THREE.MeshPhongMaterial( 
    { 
        ambient: 0x084351, 
        //color: 0xA1FF00,
        color: 0x0B586B,
        specular: 0xffffff, 
        shininess: 15, 
        shading: THREE.FlatShading,
        wireframe:false,
    }),
    extMaterialWire   = new THREE.MeshLambertMaterial({color: 0xFFFFFF, wireframe:true, opacity:false}),
    sphereMaterialWire   = new THREE.MeshLambertMaterial({color: 0xFFFFFF, wireframe:true, opacity:0.3});

    //gui.add(extMaterialWire,'opacity').name("Show Structure");
	//gui.add(sphereMaterial, 'wireframe').name("Remove Shading");
    
    // create external lowpoly sphere
    exSphere = new THREE.Mesh(
    new THREE.SphereGeometry(exRadius,exSegments,exRings),[extMaterialWire]);
    scene.addChild(exSphere);
    exSphere.geometry.dynamic = true;

    //init morphtarget array
    var target, morphVertex, sphereVertex, num = exSphere.geometry.vertices.length;
    for(var i=0; i< num; i++)
    {
        sphereVertex = exSphere.geometry.vertices[i].position;
        morphVertex = new THREE.Vector3(sphereVertex.x,sphereVertex.y,sphereVertex.z);
        target = {position:morphVertex, distance: 0};    
        morphTarget.push(target);
    }
    
    // create internal highPoly sphere
    intSphere = new THREE.Mesh(
    new THREE.SphereGeometry(intRadius,intSegments,intRings),[sphereMaterial,extMaterialWire]);
    scene.addChild(intSphere);
    intSphere.geometry.dynamic = true;
	//exSphere.rotation.x = 45;

    evInfluenceVec();   
    updateExGeometry(1);

    // create lights
    pointLight = new THREE.PointLight( 0xFFFFFF );
    pointLight.position.x = -560;
    pointLight.position.y = 380;
    pointLight.position.z = 800;
	scene.addLight(pointLight);
	
	
	// gui.add(pointLight.position,"x",-1000,1000);
	// gui.add(pointLight.position,"y",-1000,1000);
	// gui.add(pointLight.position,"z",-1000,1000);
	// gui.add(pointLight,"intensity",0,1);
	
	
	pointLight2 = new THREE.PointLight( 0xFFFFFF );
    pointLight2.position.x = 940;
    pointLight2.position.y = 800;
    pointLight2.position.z = 0;
    pointLight2.intensity = 1;
	scene.addLight(pointLight2);
	
	// gui.add(pointLight2.position,"x",-1000,1000);
	// gui.add(pointLight2.position,"y",-1000,1000);
	// gui.add(pointLight2.position,"z",-1000,1000);
	// gui.add(pointLight2,"intensity",0,1);

    
    //add event listeners
    document.addEventListener( 'mousemove', onDocumentMouseMove, false );
    document.addEventListener( 'touchstart', onDocumentTouchStart, false );
    document.addEventListener( 'touchmove', onDocumentTouchMove, false );
    window.addEventListener('resize', resizeRender, false);
    
    draw();

	//gui.close();
	//gui.open();
}

function onDocumentMouseMove(event) 
{

    mouseX = event.clientX - windowHalfX;
    mouseY = event.clientY - windowHalfY;
}

function onDocumentTouchStart( event ) 
{

    if ( event.touches.length > 1 ) {

        event.preventDefault();

        mouseX = event.touches[ 0 ].pageX - windowHalfX;
        mouseY = event.touches[ 0 ].pageY - windowHalfY;
    }
}

function onDocumentTouchMove( event ) 
{

    if ( event.touches.length == 1 ) {

        event.preventDefault();

        mouseX = event.touches[ 0 ].pageX - windowHalfX;
        mouseY = event.touches[ 0 ].pageY - windowHalfY;
    }
}

function evInfluenceVec()
{
    var exVec, intVec, dist, relV, veight,
    numEx = exSphere.geometry.vertices.length, 
    numInt = intSphere.geometry.vertices.length;
    
    relatedVec = new Array();
    
    for(var i=0; i< numEx; i++)
    {
        relV = new Array(); 
        v1 = exSphere.geometry.vertices[i].position;
        exVec = new  THREE.Vector3(v1.x,v1.y,v1.z);
        
        for(var id=0; id< numInt; id++)
        {
            v2 = intSphere.geometry.vertices[id].position;
            intVec = new  THREE.Vector3(v2.x,v2.y,v2.z);
            dist = intVec.distanceTo(exVec);
            if(dist <= softSelectionDist)
            {
                veight = 1+(dist/softSelectionDist)*-1;
                relV.push({id:id,dist:dist,veight:veight});
            }
        }
        relatedVec.push(relV);
    }
    

}

//this ealuate a new deformation for the xternal sphere
function updateExGeometry(multiplier)
{
    //console.log("--> updating geometry");
    var targetVertex, sphereVertex, num = morphTarget.length;
    
    for(var i=0; i< num; i++)
    {
        targetVertex = morphTarget[i];
        sphereVertex = exSphere.geometry.vertices[i].position;
        
        //manipulating sphere radius
        var targetSphere = new toxi.Vec3D(targetVertex.position.x, targetVertex.position.y, targetVertex.position.z);
        var sphereSphere = new toxi.Vec3D(sphereVertex.x, sphereVertex.y, sphereVertex.z);

        //convert to spherical. Now "x" is the radius 
        targetSphere.toSpherical();
        sphereSphere.toSpherical();
        targetSphere.x = radius + (Math.random(targetSphere.x)*deformationMultiplier)*multiplier;
        targetVertex.distance = targetSphere.x - sphereSphere.x;
        
        //convert back to cartesian and paste values
        targetSphere.toCartesian();
        morphTarget[i].position.x = targetSphere.x;
        morphTarget[i].position.y = targetSphere.y;
        morphTarget[i].position.z = targetSphere.z;
    
    }
    // changes to the vertices
    //intSphere.geometry.__dirtyVertices = true;

    morphDone = false;
}

//Evaluate internal soft geometry 
//TODO use shaders for the deformation
function updateIntGeometry()
{
    var v1, v2, num = exSphere.geometry.vertices.length, rVec, rVecNum, veight, sv1, sv2, radInc;
    
    for(var i=0; i< num; i++)
    {
        rVec = relatedVec[i];
        rVecNum = rVec.length;
        v1 = exSphere.geometry.vertices[i].position;
        sv1 = new toxi.Vec3D(v1.x, v1.y, v1.z);
        sv1.toSpherical();
        
        for(var id=0; id< rVecNum; id++)
        {
            //evaluating internal vertex radius
            veight = rVec[id].veight;
            v2 = intSphere.geometry.vertices[rVec[id].id].position;
            sv2 = new toxi.Vec3D(v2.x, v2.y, v2.z);
            sv2.toSpherical();      
            radInc = (sv1.x-sv2.x)*veight;      
            sv2.x += radInc;
            sv2.toCartesian();
            
            //update internal sphere radius
            intSphere.geometry.vertices[rVec[id].id].position.x = sv2.x;
            intSphere.geometry.vertices[rVec[id].id].position.y = sv2.y;
            intSphere.geometry.vertices[rVec[id].id].position.z = sv2.z;
        }   
    
    }
    // changes to the vertices
    exSphere.geometry.__dirtyVertices = true;
}

//This is used to dinamicaly upate sphere points to their target position
function morphSpheres() {
    var targetSpherical, sphereSpherical, targetVertex, sphereVertex, num = morphTarget.length, currentDist, dist;
   
    for(var i=0; i< num; i++)
    {
        targetVertex = morphTarget[i].position;
        initDist = morphTarget[i].distance;
        sphereVertex = exSphere.geometry.vertices[i].position;

        //manipulating sphere radius
        targetSpherical = new toxi.Vec3D(targetVertex.x, targetVertex.y, targetVertex.z);
        sphereSpherical = new toxi.Vec3D(sphereVertex.x, sphereVertex.y, sphereVertex.z);
        
        
        //convert to spherical. Now "x" is the radius 
        sphereSpherical.toSpherical();
        targetSpherical.toSpherical();
        currentDist = targetSpherical.x - sphereSpherical.x;
        //dist = 1 * (-Math.pow(2, -10 * currentDist )) + initDist ;
        dist = currentDist;
        sphereSpherical.x += dist*morphStep;

        if(i == num - 1 && dist < 1.5){
            morphDone = true;
            deformationMultiplier = Math.random()*200;
        }
        
        //convert back to cartesian and paste values
        sphereSpherical.toCartesian();
        exSphere.geometry.vertices[i].position.x = sphereSpherical.x;
        exSphere.geometry.vertices[i].position.y = sphereSpherical.y;
        exSphere.geometry.vertices[i].position.z = sphereSpherical.z;
    
    }
    // changes to the vertices
    intSphere.geometry.__dirtyVertices = true;

    updateIntGeometry();
}

function draw() {
    //call itself for recursive rendering
    requestAnimationFrame(draw);

    //evaluate spheres
    if(!morphDone) {
        morphSpheres();
    } else {
        updateExGeometry(1);
    }

    //update sphere rotation according to the mouse position
    var xInc = mouseX *.00005;
    var yInc = mouseY *.00005;
    exSphere.rotation.y  += xInc;
    intSphere.rotation.y  += xInc;
    exSphere.rotation.x  += yInc;
    intSphere.rotation.x  += yInc;
	
    renderer.render(scene, camera);
    

    // stats.update();
}

function resizeRender() {
    canvasHeight = $(window).height() -3,
    canvasWidth = $(window).width(),
    // notify the renderer of the size change
    renderer.setSize(canvasWidth, canvasHeight );
    // update the camera
    camera.aspect   = canvasWidth / canvasHeight;
    camera.updateProjectionMatrix();
}
