/**
 * @author qiao / https://github.com/qiao
 * @author mrdoob / http://mrdoob.com
 * @author alteredq / http://alteredqualia.com/
 * @author WestLangley / http://github.com/WestLangley
 * @modified wolfwind
 */

THREE.OrbitControls = function ( object, domElement ) {

    this.object = object;
    this.domElement = ( domElement !== undefined ) ? domElement : document;

    // API

    this.enabled = true;

    this.center = new THREE.Vector3();

    this.userZoom = true;
    this.userZoomSpeed = 0.5;

    this.userRotate = true;
    this.userRotateSpeed = 1.0;

    this.userPan = true;
    this.userPanSpeed = 1.5;

    this.autoRotate = false;
    this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60

    this.minPolarAngle = 0; // radians
    this.maxPolarAngle = Math.PI/2; // radians

    this.minDistance = 0;
    this.maxDistance = Infinity;

    // 65 /*A*/, 83 /*S*/, 68 /*D*/
    this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40, ROTATE: 65, ZOOM: 83, PAN: 68 };

    this.viewChanged = true;

    // internals

    var scope = this;

    var EPS = 0.000001;
    var PIXELS_PER_ROUND = 1800;

    var rotateStart = new THREE.Vector2();
    var rotateEnd = new THREE.Vector2();
    var rotateDelta = new THREE.Vector2();

    var zoomStart = new THREE.Vector2();
    var zoomEnd = new THREE.Vector2();
    var zoomDelta = new THREE.Vector2();

    var panStart = new THREE.Vector2();
    var panEnd = new THREE.Vector2();
    var panDelta = new THREE.Vector2();

    var phiDelta = 0;
    var thetaDelta = 0;
    var scale = 1;

    var lastPosition = new THREE.Vector3();

    var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
    var state = STATE.NONE;

    // events

    var changeEvent = { type: 'change' };
    var startEvent = { type: 'start'};
    var endEvent = { type: 'end'};

    this.reset = function (){
        rotateStart = new THREE.Vector2();
        rotateEnd = new THREE.Vector2();
        rotateDelta = new THREE.Vector2();

        zoomStart = new THREE.Vector2();
        zoomEnd = new THREE.Vector2();
        zoomDelta = new THREE.Vector2();

        panStart = new THREE.Vector2();
        panEnd = new THREE.Vector2();
        panDelta = new THREE.Vector2();

        phiDelta = 0;
        thetaDelta = 0;
        scale = 1;

        lastPosition = new THREE.Vector3();
        state = STATE.NONE;

        this.center = new THREE.Vector3();

    }

    this.rotateLeft = function ( angle ) {

        if ( angle === undefined ) {

            angle = getAutoRotationAngle();

        }

        thetaDelta -= angle;

    };

    this.rotateRight = function ( angle ) {

        if ( angle === undefined ) {

            angle = getAutoRotationAngle();

        }

        thetaDelta += angle;

    };

    this.rotateUp = function ( angle ) {

        if ( angle === undefined ) {

            angle = getAutoRotationAngle();

        }

        phiDelta -= angle;

    };

    this.rotateDown = function ( angle ) {

        if ( angle === undefined ) {

            angle = getAutoRotationAngle();

        }

        phiDelta += angle;

    };

    this.zoomIn = function ( zoomScale ) {

        if ( zoomScale === undefined ) {

            zoomScale = getZoomScale();

        }

        scale /= zoomScale;

    };

    this.zoomOut = function ( zoomScale ) {

        if ( zoomScale === undefined ) {

            zoomScale = getZoomScale();

        }

        scale *= zoomScale;

    };

    this.pan = function ( distance ) {

        distance.transformDirection( this.object.matrix );
        distance.multiplyScalar( scope.userPanSpeed );

        this.object.position.add( distance );
        this.center.add( distance );

    };

    this.update = function () {

        var position = this.object.position;
        var offset = position.clone().sub( this.center );

        // angle from z-axis around y-axis

        var theta = Math.atan2( offset.x, offset.z );

        // angle from y-axis

        var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );

        if ( this.autoRotate ) {

            this.rotateLeft( getAutoRotationAngle() );

        }

        theta += thetaDelta;
        phi += phiDelta;

        // restrict phi to be between desired limits
        phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );

        // restrict phi to be betwee EPS and PI-EPS
        phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );

        var radius = offset.length() * scale;

        // restrict radius to be between desired limits
        radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );

        offset.x = radius * Math.sin( phi ) * Math.sin( theta );
        offset.y = radius * Math.cos( phi );
        offset.z = radius * Math.sin( phi ) * Math.cos( theta );

        position.copy( this.center ).add( offset );

        this.object.lookAt( this.center );

        thetaDelta = 0;
        phiDelta = 0;
        scale = 1;

        if ( lastPosition.distanceTo( this.object.position ) > 0 ) {

            this.dispatchEvent( changeEvent );

            lastPosition.copy( this.object.position );
            this.viewChanged = true;

        }

    };


    function getAutoRotationAngle() {

        return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;

    }

    function getZoomScale() {

        return Math.pow( 0.95, scope.userZoomSpeed );

    }

    function onMouseDown( event ) {

        if ( scope.enabled === false ) return;
        if ( scope.userRotate === false ) return;

        event.preventDefault();

        if ( state === STATE.NONE )
        {
            if ( event.button === 2 )
                state = STATE.PAN;
            if ( event.button === 1 )
                state = STATE.ZOOM;
            if ( event.button === 0 )
                state = STATE.ROTATE;
        }


        if ( state === STATE.ROTATE ) {

            //state = STATE.ROTATE;

            rotateStart.set( event.clientX, event.clientY );

        } else if ( state === STATE.ZOOM ) {

            //state = STATE.ZOOM;

            zoomStart.set( event.clientX, event.clientY );

        } else if ( state === STATE.PAN ) {

            //state = STATE.PAN;
            panStart.set(event.clientX, event.clientY);

        }

        document.addEventListener( 'mousemove', onMouseMove, false );
        document.addEventListener( 'mouseup', onMouseUp, false );

    }

    function onMouseMove( event ) {

        if ( scope.enabled === false ) return;

        event.preventDefault();



        if ( state === STATE.ROTATE ) {

            rotateEnd.set( event.clientX, event.clientY );
            rotateDelta.subVectors( rotateEnd, rotateStart );

            scope.rotateLeft( 2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed );
            scope.rotateUp( 2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed );

            rotateStart.copy( rotateEnd );

        } else if ( state === STATE.ZOOM ) {

            zoomEnd.set( event.clientX, event.clientY );
            zoomDelta.subVectors( zoomEnd, zoomStart );

            if ( zoomDelta.y > 0 ) {

                scope.zoomIn();

            } else {

                scope.zoomOut();

            }

            zoomStart.copy( zoomEnd );

        } else if ( state === STATE.PAN ) {

            panEnd.set(event.clientX, event.clientY);
            panDelta.subVectors(panEnd, panStart);
            scope.pan( new THREE.Vector3( - panDelta.x, panDelta.y , 0 ) );
            panStart.copy(panEnd);

        }

    }

    function onMouseUp( event ) {

        if ( scope.enabled === false ) return;
        if ( scope.userRotate === false ) return;

        document.removeEventListener( 'mousemove', onMouseMove, false );
        document.removeEventListener( 'mouseup', onMouseUp, false );

        state = STATE.NONE;

    }

    function onMouseWheel( event ) {

        if ( scope.enabled === false ) return;
        if ( scope.userZoom === false ) return;

        var delta = 0;

        if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9

            delta = event.wheelDelta;

        } else if ( event.detail ) { // Firefox

            delta = - event.detail;

        }

        if ( delta > 0 ) {

            scope.zoomOut();

        } else {

            scope.zoomIn();

        }

    }

    function onKeyDown( event ) {

        if ( scope.enabled === false ) return;
        if ( scope.userPan === false ) return;

        switch ( event.keyCode ) {

            /*case scope.keys.UP:
             scope.pan( new THREE.Vector3( 0, 1, 0 ) );
             break;
             case scope.keys.BOTTOM:
             scope.pan( new THREE.Vector3( 0, - 1, 0 ) );
             break;
             case scope.keys.LEFT:
             scope.pan( new THREE.Vector3( - 1, 0, 0 ) );
             break;
             case scope.keys.RIGHT:
             scope.pan( new THREE.Vector3( 1, 0, 0 ) );
             break;
             */
            case scope.keys.ROTATE:
                state = STATE.ROTATE;
                break;
            case scope.keys.ZOOM:
                state = STATE.ZOOM;
                break;
            case scope.keys.PAN:
                state = STATE.PAN;
                break;

        }

    }

    function onKeyUp( event ) {

        switch ( event.keyCode ) {

            case scope.keys.ROTATE:
            case scope.keys.ZOOM:
            case scope.keys.PAN:
                state = STATE.NONE;
                break;
        }

    }

    function touchstart( event ) {

        if ( scope.enabled === false ) return;

        switch ( event.touches.length ) {

            case 2:

                    state = STATE.TOUCH_ROTATE;
                    rotateStart.copy(event.touches[ 0 ].clientX, event.touches[ 0 ].clientY );
                    rotateEnd.copy(rotateStart);

                break;
            case 1:
                state = STATE.TOUCH_ZOOM_PAN;
//                var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
//                var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
//                _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );

//                var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
//                var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
                panStart.set( event.touches[ 0 ].clientX, event.touches[ 0 ].clientY );
                //panEnd.copy( panStart );
                break;

            default:
                state = STATE.NONE;

        }
        document.addEventListener( 'touchend', touchend, false );
        document.addEventListener( 'touchmove', touchmove, false );
        scope.dispatchEvent( startEvent );


    }

    function touchmove( event ) {

        if ( scope.enabled === false ) return;

        event.preventDefault();
        event.stopPropagation();

        switch ( event.touches.length ) {

            case 2:

                    rotateEnd.copy(event.touches[ 0 ].clientX, event.touches[ 0 ].clientY );

                break;
            case 1:
//                var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
//                var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
//                _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
//
//                var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
//                var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
                panEnd.set( event.touches[ 0 ].clientX, event.touches[ 0 ].clientY );
                panDelta.subVectors(panEnd, panStart);
                scope.pan(new THREE.Vector3( - panDelta.x, panDelta.y , 0 ));
                panStart.copy(panEnd);
                break;

            default:
                state = STATE.NONE;

        }

    }

    function touchend( event ) {

        if ( scope.enabled === false ) return;

//        switch ( event.touches.length ) {
//
//            case 2:
//                if(scope.is3d) {
//                    rotateEnd.copy(event.touches[ 0 ].clientX, event.touches[ 0 ].clientY );
//                    rotateStart.copy(rotateEnd);
//                }
//                break;
//            case 1:
////                _touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
////
////                var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
////                var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
////                panEnd.copy( event.touches[ 0 ].clientX, event.touches[ 0 ].clientY );
////                panStart.copy( panEnd );
//                break;
//
//        }
        document.removeEventListener('touchend', touchend, false);
        document.removeEventListener('touchmove', touchmove, false);

        state = STATE.NONE;
        scope.dispatchEvent( endEvent );

    }

    this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
    this.domElement.addEventListener( 'mousedown', onMouseDown, false );
    this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
    this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
    this.domElement.addEventListener( 'touchstart', touchstart, false );

    window.addEventListener( 'keydown', onKeyDown, false );
    window.addEventListener( 'keyup', onKeyUp, false );

};

THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );