(function() {

    //  touch.js 
    // 
    //  Sample file using tractor action, haptic vibration, and color change to demonstrate two spheres 
    //  That can give a sense of touch to the holders.  
    //  Create two standard spheres, make them grabbable, and attach this entity script.  Grab them and touch them together.
    //  
    //  Created by Philip Rosedale on March 18, 2017
    //  Copyright 2017 High Fidelity, Inc.
    //  
    //  Distributed under the Apache License, Version 2.0.
    //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
    //

    var TIMESCALE = 0.03;
    var ACTION_TTL = 10;
    var _this;

    var RIGHT_HAND = 1;
    var LEFT_HAND = 0;
    var HAPTIC_PULSE_FIRST_STRENGTH = 0.5;
    var HAPTIC_PULSE_MIN_STRENGTH = 0.20;
    var HAPTIC_PULSE_MAX_STRENGTH = 0.5;
    var HAPTIC_PULSE_FIRST_DURATION = 1.0;
    var HAPTIC_PULSE_DURATION = 16.0;
    var HAPTIC_PULSE_DISTANCE = 0.0;
    var MAX_PENETRATION = 0.02;
    var HAPTIC_MIN_VELOCITY = 0.002;
    var HAPTIC_MAX_VELOCITY = 0.5;
    var PENETRATION_PULLBACK_FACTOR = 0.65;
    var FRAME_TIME = 0.016;

    var isColliding = false;

    var hand = LEFT_HAND;
    var lastHapticPulseLocation = { x:0, y:0, z:0 };


    var GRAY = { red: 128, green: 128, blue: 128 };
    var RED = { red: 255, green: 0, blue: 0 };

    var targetColor = { x: GRAY.red, y: GRAY.green, z: GRAY.blue };

    var lastPosition = { x: 0, y: 0, z: 0 };
    var velocity = { x: 0, y: 0, z: 0 };

    var lastOtherPosition = { x: 0, y: 0, z: 0 };
    var otherVelocity = { x: 0, y: 0, z: 0 };


    function TouchExample() {
        _this = this;
    }

    function updateTractorAction(timescale) {
        var targetProps = Entities.getEntityProperties(_this.entityID);
        //
        //  Look for nearby entities to touch 
        //
        var copyProps = Entities.getEntityProperties(_this.copy);
        var nearbyEntities = Entities.findEntities(copyProps.position, copyProps.dimensions.x * 2); 
        var wasColliding = isColliding; 
        var targetAdjust = { x: 0, y: 0, z: 0 };

        isColliding = false;
        for (var i = 0; i < nearbyEntities.length; i++) {
            if (_this.copy != nearbyEntities[i] && _this.entityID != nearbyEntities[i]) {
                var otherProps = Entities.getEntityProperties(nearbyEntities[i]);
                var penetration = Vec3.distance(copyProps.position, otherProps.position) - (copyProps.dimensions.x / 2 + otherProps.dimensions.x / 2);
                if (otherProps.type === 'Sphere' && penetration < 0 && penetration > -copyProps.dimensions.x * 3) {
                    isColliding = true;
                    targetAdjust = Vec3.sum(targetAdjust, Vec3.multiply(Vec3.normalize(Vec3.subtract(targetProps.position, otherProps.position)), -penetration * PENETRATION_PULLBACK_FACTOR));
                    if (!wasColliding && false) {
                        targetColor = { x: RED.red, y: RED.green, z: RED.blue };
                    } else {
                        targetColor = { x: 200 + Math.min(-penetration / MAX_PENETRATION, 1.0) * 55, y: GRAY.green, z: GRAY.blue };
                    }
                    if (Vec3.distance(targetProps.position, lastHapticPulseLocation) > HAPTIC_PULSE_DISTANCE || !wasColliding) {
                        if (!wasColliding) {
                            velocity = { x: 0, y: 0, z: 0};
                            otherVelocity = { x: 0, y: 0, z: 0 };
                            Controller.triggerHapticPulse(HAPTIC_PULSE_FIRST_STRENGTH, HAPTIC_PULSE_FIRST_DURATION, hand);
                        } else {
                            velocity = Vec3.distance(targetProps.position, lastPosition) / FRAME_TIME;   
                            otherVelocity = Vec3.distance(otherProps.position, lastOtherPosition) / FRAME_TIME; 
                            var velocityStrength = Math.min(velocity + otherVelocity / HAPTIC_MAX_VELOCITY, 1.0);
                            var strength = HAPTIC_PULSE_MIN_STRENGTH + Math.min(-penetration / MAX_PENETRATION, 1.0) * (HAPTIC_PULSE_MAX_STRENGTH - HAPTIC_PULSE_MIN_STRENGTH);
                            Controller.triggerHapticPulse(velocityStrength * strength, HAPTIC_PULSE_DURATION, hand);
                        }
                        lastPosition = targetProps.position;
                        lastOtherPosition = otherProps.position;
                        lastHapticPulseLocation = targetProps.position;
                    }
                }     
            }
        }
        if ((wasColliding != isColliding) && !isColliding) {
            targetColor = { x: GRAY.red, y: GRAY.green, z: GRAY.blue };
        }
        //  Interpolate color toward target color 
        var currentColor = { x: copyProps.color.red,  y: copyProps.color.green,  z: copyProps.color.blue };
        var newColor = Vec3.sum(currentColor, Vec3.multiply(Vec3.subtract(targetColor, currentColor), 0.1));
        Entities.editEntity(_this.copy, { color: { red: newColor.x, green: newColor.y, blue: newColor.z } });

        var props = {
            targetPosition: Vec3.sum(targetProps.position, targetAdjust),
            targetRotation: targetProps.rotation,
            linearTimeScale: timescale,
            angularTimeScale: timescale,
            ttl: ACTION_TTL
        };
        var success = Entities.updateAction(_this.copy, _this.actionID, props);
    }

    function createTractorAction(timescale) {

        var targetProps = Entities.getEntityProperties(_this.entityID);
        var props = {
            targetPosition: targetProps.position,
            targetRotation: targetProps.rotation,
            linearTimeScale: timescale,
            angularTimeScale: timescale,
            ttl: ACTION_TTL
        };
        _this.actionID = Entities.addAction("tractor", _this.copy, props);
        return;
    }

    function createCopy() {
        var originalProps = Entities.getEntityProperties(_this.entityID);
        var props = {
            type: originalProps.type,
            modelURL: originalProps.modelURL,
            dimensions: originalProps.dimensions,
            color: GRAY,
            dynamic: true,
            damping: 0.0,
            angularDamping: 0.0,
            collidesWith: 'static',
            rotation: originalProps.rotation,
            position: originalProps.position,
            shapeType: originalProps.shapeType,
            visible: true,
            userData:JSON.stringify({
                grabbableKey:{
                    grabbable:false
                }
            })
        };
        _this.copy = Entities.addEntity(props);
    }

    function deleteCopy() {
        print("Delete copy");
        Entities.deleteEntity(_this.copy);
    }

    function makeOriginalInvisible() {
        Entities.editEntity(_this.entityID, {
            visible: false,
            collisionless: true
        });
    }

    function makeOriginalVisible() {
        Entities.editEntity(_this.entityID, {
            visible: true,
            collisionless: false
        });
    }

    function deleteTractorAction() {
        Entities.deleteAction(_this.copy, _this.actionID);
    }

    function setHand(position) {
        if (Vec3.distance(MyAvatar.getLeftPalmPosition(), position) < Vec3.distance(MyAvatar.getRightPalmPosition(), position)) {
            hand = LEFT_HAND;
        } else {
            hand = RIGHT_HAND;
        }
    }

    TouchExample.prototype = {
        preload: function(entityID) {
            _this.entityID = entityID;
        },
        startNearGrab: function(entityID, data) {
            createCopy();
            createTractorAction(TIMESCALE);
            makeOriginalInvisible();
            setHand(Entities.getEntityProperties(_this.entityID).position);
        },
        continueNearGrab: function() {
            updateTractorAction(TIMESCALE);
        },
        releaseGrab: function() {
            deleteTractorAction();
            deleteCopy();
            makeOriginalVisible();
        }
    };

    return new TouchExample();
});