//
//  Pointer.js
//  CoverPage
//
//  Created by Richard Anaya on 1/9/2013.
//  Copyright (c) 2014 Evernote. All rights reserved.
//

var Pointer = function() {
    this.heads = {
        blue: THREE.ImageUtils.loadTexture( "images/Pointer/pointerHeadBlue.png" ),
        green: THREE.ImageUtils.loadTexture( "images/Pointer/pointerHeadGreen.png" ),
        pink: THREE.ImageUtils.loadTexture( "images/Pointer/pointerHeadPink.png" )
    }
    this.enabled = true;
    this.fps = 30;
    this.spline = new CRSpline();
    this.simulated = false;

    this.FULL_SPEED = 1;
    this.HALF_SPEED = 2;

    this.showStats = false;

    this.visible = true;
    var _this = this;

    this.windowHalfX = window.innerWidth / 2;
    this.windowHalfY = window.innerHeight / 2;

    this.container = document.createElement( 'div' );
    $encpJquery(this.container).addClass('pointer-container');

    $encpJquery(this.container).css('position','fixed');
    $encpJquery(this.container).css('left','0px');
    $encpJquery(this.container).css('top','0px');
    $encpJquery(this.container).css('margin','0px');
    $encpJquery(this.container).css('padding','0px');
    $encpJquery(this.container).css('z-index','9999999999');

    $encpJquery(window).on('mouseleave.presentationmode',function(){
        if(_this.simulated){ return;}
        _this.hide();})
    $encpJquery(window).on('mouseenter.presentationmode',function(){
        if(_this.simulated){ return;}
        _this.show();})

    document.body.appendChild( this.container );

    var width = window.innerWidth;
    var height = window.innerHeight-1;

    this.camera = new THREE.OrthographicCamera( 0, width, 0, height, 1, 1000 );
    this.camera.position.z = 1000;

    this.scene = new THREE.Scene();
    this.scene.fog = new THREE.FogExp2( 0x000000, 0.0007 );



    var triangles = 200*2;

    var geometry = new THREE.BufferGeometry();
    geometry.dynamic = true;
    geometry.attributes = {
        index: {
            itemSize: 1,
            array: new Uint16Array( triangles * 3 )
        },
        position: {
            itemSize: 3,
            array: new Float32Array( triangles * 3 * 3 )
        },
        alpha: {
            itemSize: 1,
            array: new Float32Array( triangles * 3 * 1 )
        },
        normal: {
            itemSize: 3,
            array: new Float32Array( triangles * 3 * 3 )
        },
        color: {
            itemSize: 3,
            array: new Float32Array( triangles * 3 * 3 )
        }
    }

    // break geometry into
    // chunks of 21,845 triangles (3 unique vertices per triangle)
    // for indices to fit into 16 bit integer number
    // floor(2^16 / 3) = 21845

    var chunkSize = 21845;

    var indices = geometry.attributes.index.array;

    for ( var i = 0; i < indices.length; i ++ ) {

        indices[ i ] = i % ( 3 * chunkSize );

    }

    var positions = geometry.attributes.position.array;
    var alphas = geometry.attributes.alpha.array;
    var normals = geometry.attributes.normal.array;
    var colors = geometry.attributes.color.array;

    var color = new THREE.Color();

    var n = 800, n2 = n/2;	// triangles spread in the cube
    var d = 12, d2 = d/2;	// individual triangle size

    var pA = new THREE.Vector3();
    var pB = new THREE.Vector3();
    var pC = new THREE.Vector3();

    var cb = new THREE.Vector3();
    var ab = new THREE.Vector3();

    for ( var i = 0; i < positions.length; i += 18 ) {

        // positions

        var x = Math.random() * n - n2;
        var y = Math.random() * n - n2;
        var z = Math.random() * n - n2;

        var ax = 0;
        var ay = 0;
        var az = 0;

        var bx = 100;
        var by = 0;
        var bz = 0;

        var cx = 0;
        var cy = 100;
        var cz = 0;

        positions[ i ]     = ax;
        positions[ i + 1 ] = ay;
        positions[ i + 2 ] = 0;

        positions[ i + 3 ] = bx;
        positions[ i + 4 ] = by;
        positions[ i + 5 ] = 0;

        positions[ i + 6 ] = cx;
        positions[ i + 7 ] = cy;
        positions[ i + 8 ] = 0;

        positions[ i + 9]     = 100;
        positions[ i + 10 ] = 100;
        positions[ i + 11 ] = 0;

        positions[ i + 12 ] = bx;
        positions[ i + 13 ] = by;
        positions[ i + 14 ] = 0;

        positions[ i + 15 ] = cx;
        positions[ i + 16 ] = cy;
        positions[ i + 17 ] = 0;

        // flat face normals

        pA.set( ax, ay, az );
        pB.set( bx, by, bz );
        pC.set( cx, cy, cz );

        cb.subVectors( pC, pB );
        ab.subVectors( pA, pB );
        cb.cross( ab );

        cb.normalize();

        var nx = cb.x;
        var ny = cb.y;
        var nz = cb.z;

        alphas[ i/3 + 0 ] = 1;
        alphas[ i/3 + 1 ] = 1;
        alphas[ i/3 + 2 ] = 1;
        alphas[ i/3 + 3 ] = 1;
        alphas[ i/3 + 4 ] = 1;
        alphas[ i/3 + 5 ] = 1;

        normals[ i ]     = nx;
        normals[ i + 1 ] = ny;
        normals[ i + 2 ] = nz;

        normals[ i + 3 ] = nx;
        normals[ i + 4 ] = ny;
        normals[ i + 5 ] = nz;

        normals[ i + 6 ] = nx;
        normals[ i + 7 ] = ny;
        normals[ i + 8 ] = nz;

        normals[ i + 9]     = nx;
        normals[ i + 10 ] = ny;
        normals[ i + 11 ] = nz;

        normals[ i + 12 ] = nx;
        normals[ i + 13 ] = ny;
        normals[ i + 14 ] = nz;

        normals[ i + 15 ] = nx;
        normals[ i + 16 ] = ny;
        normals[ i + 17 ] = nz;

        // colors

        colors[ i ]     = 0;
        colors[ i + 1 ] = 0;
        colors[ i + 2 ] = 0;

        colors[ i + 3 ] = 0;
        colors[ i + 4 ] = 0;
        colors[ i + 5 ] = 0;

        colors[ i + 6 ] = 0;
        colors[ i + 7 ] = 0;
        colors[ i + 8 ] = 0;

        colors[ i + 9]   = 0;
        colors[ i + 10 ] = 0;
        colors[ i + 11 ] = 0;

        colors[ i + 12 ] = 0;
        colors[ i + 13 ] = 0;
        colors[ i + 14 ] = 0;

        colors[ i + 15 ] = 0;
        colors[ i + 16 ] = 0;
        colors[ i + 17 ] = 0;
    }

    geometry.offsets = [];
    geometry.dynamic

    var offsets = triangles / chunkSize;

    for ( var i = 0; i < offsets; i ++ ) {

        var offset = {
            start: i * chunkSize * 3,
            index: i * chunkSize * 3,
            count: Math.min( triangles - ( i * chunkSize ), chunkSize ) * 3
        };

        geometry.offsets.push( offset );

    }

    geometry.computeBoundingSphere();




    // attributes
    var materialattributes = {

        alpha: { type: 'f', value: [] }

    };

    // uniforms
    var materialuniforms = {

    };

    // particle system material
    var material = new THREE.ShaderMaterial( {

        uniforms:       materialuniforms,
        attributes:     materialattributes,
        vertexShader:   document.getElementById( 'vertexshader' ).textContent,
        fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
        transparent:    true,
        side: THREE.DoubleSide
    });

    /*material = new THREE.MeshBasicMaterial( {
     //color: 0xaaaaaa, ambient: 0xaaaaaa, specular: 0xffffff, shininess: 25
     side: THREE.DoubleSide, vertexColors: THREE.VertexColors
     } );*/

    mesh = new THREE.Mesh( geometry, material );
    this.scene.add( mesh );
    this.mesh = mesh;

    var headgeometry = new THREE.Geometry();

    var headsprite = THREE.ImageUtils.loadTexture( "images/Pointer/pointerHeadBlue.png" );

    var vertex = new THREE.Vector3();
    vertex.x = 0;
    vertex.y = 0;
    vertex.z = 0;

    headgeometry.vertices.push( vertex );

    var headmaterial = new THREE.ParticleBasicMaterial( { size: 20, sizeAttenuation: false, map: headsprite, transparent: true } );
    headmaterial.color.setHSL( 1,1,1 );

    var headparticles = new THREE.ParticleSystem( headgeometry, headmaterial );
    this.scene.add( headparticles );

    this.headparticles = headparticles;
    this.headparticles.position.x = -5000;
    this.headparticles.position.y = -5000;

    this.renderer = new THREE.WebGLRenderer({antialiasing: true, alpha: true });
    this.renderer.setClearColor( 0, 0);
    this.renderer.setSize( window.innerWidth, window.innerHeight-1 );
    this.container.appendChild( this.renderer.domElement );


    if(this.showStats){
        this.stats = new Stats();
        this.stats.domElement.style.position = 'fixed';
        this.stats.domElement.style.top = '0px';
        this.container.appendChild( this.stats.domElement );
    }

    $encpJquery(document).on('mousemove.presentationmode', _.bind(this.onDocumentMouseMove,this) );

    $encpJquery(document).on('mousedown.presentationmode', _.bind(this.onDocumentMouseDown,this) );
    this.clicked = new Signal();

    $encpJquery(window).on('resize.presentationmode',_.bind(this.onWindowResize,this));
    this.FPS = 60;
    this.animateInterval = window.setInterval(_.bind(this.animate,this),1000/this.fps);


    //prevent i-beam cursor
    $encpJquery('canvas',this.container).get(0).onselectstart = function () { return false; };
    $encpJquery('canvas',this.container).css("pointer-events","none");
    $encpJquery(this.container).css("pointer-events","none");


    this.cursorColors = {
        "blue": "#0090F4",
        "green": "#C0E23C",
        "pink": "#F7478E",
        "none": null
    };
    this.currentColor = "green";

    /*$encpJquery(document).on('keydown.presentationmode',function(e){
        if(e.keyCode == 16){
            _this.spline.animationDuration = _this.HALF_SPEED;
        }
        if(e.keyCode == 20){
            if(_this.spline.animationDuration == _this.FULL_SPEED){
                _this.spline.animationDuration = _this.HALF_SPEED;
            }
            if(_this.spline.animationDuration == _this.HALF_SPEED){
                _this.spline.animationDuration = _this.FULL_SPEED;
            }
        }
    }).keyup(function(e){
            if(e.keyCode == 16){
                _this.spline.animationDuration = _this.FULL_SPEED;
            }
        });*/

    this.listenToMouse = false;
    window.setTimeout(function(){
        _this.listenToMouse = true;
    },1000);

};

Pointer.prototype.onWindowResize = function(){
    this.camera.left = 0;
    this.camera.right = window.innerWidth;
    this.camera.top = 0;
    this.camera.bottom = window.innerHeight;

    this.camera.updateProjectionMatrix();

    this.renderer.setSize( window.innerWidth, window.innerHeight );
};

Pointer.prototype.onDocumentMouseDown = function( event ) {
    this.clicked.dispatch(event);
};

Pointer.prototype.setZIndex = function( zindex ) {
    $encpJquery(this.container).css('z-index',zindex);
};

Pointer.prototype.onDocumentMouseMove = function( event ) {
    if(this.simulated){ return;}
    if(!this.listenToMouse) { return; }
    this.updateMousePosition(event.clientX,event.clientY);
};

Pointer.prototype.updateMousePosition = function(x,y) {
    this.mouseX = x;
    this.mouseY = y;
    this.spline.addControlPoint({x:this.mouseX,y:this.mouseY});
    this.headparticles.position.x = x;
    this.headparticles.position.y = y;
    this.headparticles.position.z = 0;
    //this.spline.curves[this.spline.curves.length-1].controlPointEnd.x = x;
    //this.spline.curves[this.spline.curves.length-1].controlPointEnd.y = y;
};

Pointer.prototype.clear = function(){
    this.spline.curves = [];
    if(this.renderer) {
        this.renderer.clear();
    }
};

Pointer.prototype.animate = function(){
    if(!this.animateCallback){
        this.animateCallback = _.bind(this.animate,this);
    }

    this.render();
    
    this.renderer.context.drawingBufferHeight = this.renderer.context.canvas.height;
    this.renderer.context.drawingBufferWidth = this.renderer.context.canvas.width;
    if(this.showStats){
        this.stats.update();
    }
};

Pointer.prototype.render = function() {
    if(!this.enabled) return;
    //Before the curve is being drawn a curve geometry update is expected to be called
    this.spline.update(this.fps/1000);
    var geometry = this.mesh.geometry;
    var positions = geometry.attributes.position.array;
    var color = geometry.attributes.color.array;
    var alphas = geometry.attributes.alpha.array;
    var i = 0;
    var _this = this;
    var s = 20;//p.size/2;
    var hs = s/2;
    var first = true;
    this.spline.foreachPointReverse(function(p,lastP){
        if(lastP == null){
            positions[ i ]     = 0;
            positions[ i + 1 ] = 0;
            //positions[ i + 2 ] = 0;

            positions[ i + 3 ] = 0;
            positions[ i + 4 ] = 0;
            //positions[ i + 5 ] = 0;

            positions[ i + 6 ] = 0;
            positions[ i + 7 ] = 0;
            //positions[ i + 8 ] = 0;

            positions[ i + 9]   = 0;
            positions[ i + 10 ] = 0;
            //positions[ i + 11 ] = 0;

            positions[ i + 12 ] = 0;
            positions[ i + 13 ] = 0;
            //positions[ i + 14 ] = 0;

            positions[ i + 15 ] = 0;
            positions[ i + 16 ] = 0;
        }
        else {
            var x = p.x;
            var y = p.y;
            if(first){
                x = _this.mouseX;
                y = _this.mouseY;
                first = false;
            }
            var hs = p.size/2;
            var lhs = lastP.size/2;

            var topLeftX = x+ p.normalTop.x*hs;
            var topLeftY = y+ p.normalTop.y*hs;

            var topRightX = x+ p.normalBottom.x*hs;
            var topRightY = y+ p.normalBottom.y*hs;
            var botLeftX = lastP.x + lastP.normalTop.x*lhs;
            var botLeftY = lastP.y + lastP.normalTop.y*lhs;

            var botRightX = lastP.x + lastP.normalBottom.x*lhs;
            var botRightY = lastP.y + lastP.normalBottom.y*lhs;

            positions[ i ]     = topLeftX;
            positions[ i + 1 ] = topLeftY;
            //positions[ i + 2 ] = 0;

            positions[ i + 3 ] = topRightX;
            positions[ i + 4 ] = topRightY;
            //positions[ i + 5 ] = 0;

            positions[ i + 6 ] = botLeftX;
            positions[ i + 7 ] = botLeftY;
            //positions[ i + 8 ] = 0;

            positions[ i + 9]   = topRightX;
            positions[ i + 10 ] = topRightY;
            //positions[ i + 11 ] = 0;

            positions[ i + 12 ] = botRightX;
            positions[ i + 13 ] = botRightY;
            //positions[ i + 14 ] = 0;

            positions[ i + 15 ] = botLeftX;
            positions[ i + 16 ] = botLeftY;
            //positions[ i + 17 ] = 0;

            color[ i ]     = p.color.r/255;
            color[ i + 1 ] = p.color.g/255;
            color[ i + 2 ] = p.color.b/255;

            color[ i + 3 ] = p.color.r/255;
            color[ i + 4 ] = p.color.g/255;
            color[ i + 5 ] = p.color.b/255;

            color[ i + 6 ] = lastP.color.r/255;
            color[ i + 7 ] = lastP.color.g/255;
            color[ i + 8 ] = lastP.color.b/255;

            color[ i + 9]   = p.color.r/255;
            color[ i + 10 ] = p.color.g/255;
            color[ i + 11 ] = p.color.b/255;

            color[ i + 12 ] = lastP.color.r/255;
            color[ i + 13 ] = lastP.color.g/255;
            color[ i + 14 ] = lastP.color.b/255;

            color[ i + 15 ] = lastP.color.r/255;
            color[ i + 16 ] = lastP.color.g/255;
            color[ i + 17 ] = lastP.color.b/255;

            alphas[ i/3 +0 ] = p.alpha;
            alphas[ i/3 +1 ] = p.alpha;
            alphas[ i/3 +2 ] = lastP.alpha;
            alphas[ i/3 +3 ] = p.alpha;
            alphas[ i/3 +4 ] = lastP.alpha;
            alphas[ i/3 +5 ] = lastP.alpha;
        }


        i += 18;
    });
    for(i=i;i<positions.length;i++){
        positions[i] = 0;
    }

    geometry.attributes.position.needsUpdate = true;
    geometry.attributes.color.needsUpdate = true;
    geometry.attributes.alpha.needsUpdate = true;

    this.renderer.render( this.scene, this.camera );
};

Pointer.prototype.show = function(preventClear) {
    if(!this.enabled) return;
    if(!preventClear){
            this.spline.clear();
    }
    $encpJquery(this.container).show();
    if(this.currentCursor == "default"){
        $encpJquery('html').css('cursor','default');
        $encpJquery('body').css('cursor','default');
        $encpJquery(this.container).css('cursor','default');
        $encpJquery(this.container).css('opacity', 0);
    }
    else {
        $encpJquery('html').css('cursor','none');
        $encpJquery('body').css('cursor','none');
        $encpJquery(this.container).css('cursor','none');
        if(this.fading){
            this.fading.stop();
        }
        var _this = this;
        if(!this.fadingIn){
            this.fadingIn = true;
            $encpJquery(this.renderer.context.canvas).show();
            this.fading = $encpJquery(this.renderer.context.canvas).tween({
                 alpha:{
                     start: 0,
                     stop: 100,
                     time: 0,
                     duration:.75,
                     units: 'px',
                     effect: 'easeOut',
                     onStop: function(){
                         _this.fading = null;
                         this.fadingIn = false;
                     }
                 }
            }).play();
        }
        this.isVisible = true;
    }
};

Pointer.prototype.hide = function(overrideSystemCursor, hideImmediately) {
    if(!this.enabled) return;
    this.autoHidden = false;
    this.fadingIn = false;
    var sysCursor = "default";
    if(overrideSystemCursor){
        sysCursor = overrideSystemCursor;
    }

    if(this.currentCursor == "default"){
        $encpJquery('html').css('cursor',sysCursor);
        $encpJquery('body').css('cursor',sysCursor);
        $encpJquery(this.container).css('cursor',sysCursor);
        $encpJquery(this.container).css('opacity', 0);
        this.hiding = false;
    }
    else {
        $encpJquery('html').css('cursor',sysCursor);
        $encpJquery('body').css('cursor',sysCursor);
        $encpJquery(this.container).css('cursor',sysCursor);
        if(this.fading){
            this.fading.stop();
        }
        var _this = this;
        if(hideImmediately){
            $encpJquery(this.renderer.context.canvas).hide();
            this.fading = null;
        }
        else {
            this.fading = $encpJquery(this.renderer.context.canvas).tween({
                alpha:{
                     start: 100,
                     stop: 0,
                     time: 0,
                     duration:.75,
                     units: 'px',
                     effect: 'easeOut',
                     onStop: function(){
                         _this.fading = null;
                     }
                 }
             }).play();
        }
        this.isVisible = false;
    }
};

Pointer.prototype.removeUI = function() {
    $encpJquery(this.container).remove();
}

Pointer.prototype.changeCursor = function(type) {
    localStorage.setItem("cursorType",type);
    this.currentCursor = type;
    if(type == "default"){
        if(!this.isVisible){
            this.hide();
        }
        $encpJquery(this.container).css('cursor','default');
    }
    else {
        var spriteFile = "";

        if(type == "blue"){
            this.spline.startColor = { r: 51, g: 164, b: 255 };
            this.spline.midColor = { r: 153, g: 236, b: 255 };
            this.spline.endColor = { r: 179, g: 241, b: 255 };
            spriteFile = this.heads.blue;
        }
        else if(type == "red"){
            this.spline.startColor = { r: 255, g: 102, b: 127 };
            this.spline.midColor = { r: 255, g: 166, b: 233 };
            this.spline.endColor = { r: 255, g: 204, b: 235 };
            spriteFile = this.heads.pink;
        }
        else if(type == "yellow"){
            this.spline.startColor = { r: 182, g: 242, b: 121 };
            this.spline.midColor = { r: 208, g: 255, b: 77 };
            this.spline.endColor = { r: 242, g: 255, b: 217 };
            spriteFile = this.heads.green;
        }

        this.scene.remove( this.headparticles );

        var headgeometry = new THREE.Geometry();

        var headsprite = spriteFile;

        var vertex = new THREE.Vector3();
        vertex.x = 0;
        vertex.y = 0;
        vertex.z = 0;

        headgeometry.vertices.push( vertex );

        var headmaterial = new THREE.ParticleBasicMaterial( { size: 20, sizeAttenuation: false, map: headsprite, transparent: true } );
        headmaterial.color.setHSL( 1,1,1 );

        var headparticles = new THREE.ParticleSystem( headgeometry, headmaterial );
        this.scene.add( headparticles );

        this.headparticles = headparticles;

        $encpJquery(this.container).css('opacity', 100);
        if(this.isVisible){
            this.show();
        }
        $encpJquery(this.container).css('cursor','none');
        this.updateMousePosition(this.mouseX,this.mouseY);
    }
}

Pointer.prototype.destroy = function(){
    window.clearInterval(this.animateInterval);
    $encpJquery(document).off('mousemove.presentationmode');
    $encpJquery(document).off('mousedown.presentationmode');
    $encpJquery(document).off('keydown.presentationmode');
    $encpJquery(window).off('resize.presentationmode');
    $encpJquery(window).off('mouseleave.presentationmode');
    $encpJquery(window).off('mouseenter.presentationmode');
    $encpJquery(this.container).remove();
    this.enabled = false;
};