(function () {
    // See tests/performance/tribbles.js
    Script.include("../../libraries/virtualBaton.js");
    var dimensions, oldColor, entityID,
        editRate = 60,
        moveRate = 1,
        scale = 2,
        accumulated = 0,
        increment = {red: 1, green: 1, blue: 1},
        hasUpdate = false,
        shutdown = false,
        baton;
    function nextWavelength(color) {
        var old = oldColor[color];
        if (old === 255) {
            increment[color] = -1;
        } else if (old === 0) {
            increment[color] = 1;
        }
        var next = (old + increment[color]) % 256;
        return next;
    }
    function update(delta) {  // High frequency stuff is done in update in case we fall behind.
        accumulated += delta;
        if (accumulated > (1 / editRate)) {
            var newColor = {red: nextWavelength('red'), green: nextWavelength('green'), blue: nextWavelength('blue')};
            oldColor = newColor;
            Entities.editEntity(entityID, {color: newColor});
            accumulated = 0;
        }
    }
    function randomCentered() {
        return Math.random() - 0.5;
    }
    function randomVector() {
        return {x: randomCentered() * dimensions.x, y: randomCentered() * dimensions.y, z: randomCentered() * dimensions.z};
    }
    function move() {
        var newData = {velocity: Vec3.sum({x: 0, y: 1, z: 0}, randomVector()), angularVelocity: Vec3.multiply(Math.PI, randomVector())};
        var nextChange = Math.ceil(Math.random() * 2000 / moveRate);
        Entities.editEntity(entityID, newData);
        if (!shutdown) {
            Script.setTimeout(move, nextChange);
        }
    }
    function startUpdate() {
        print('startUpdate', entityID);
        hasUpdate = true;
        Script.update.connect(update);
    }
    function stopUpdate() {
        print('stopUpdate', entityID, hasUpdate);
        if (!hasUpdate) {
            return;
        }
        hasUpdate = false;
        Script.update.disconnect(update);
    }
    function stopUpdateAndReclaim() {
        print('stopUpdateAndReclaim', entityID);
        stopUpdate();
        baton.claim(startUpdate, stopUpdateAndReclaim);
    }
    this.preload = function (givenEntityID) {
        entityID = givenEntityID;
        var properties = Entities.getEntityProperties(entityID);
        var userData = properties.userData && JSON.parse(properties.userData);
        var moveTimeout = userData ? userData.moveTimeout : 0;
        var editTimeout = userData ? userData.editTimeout : 0;
        var debug = (userData && userData.debug) || {};
        editRate = (userData && userData.editRate) || editRate;
        moveRate = (moveRate && userData.moveRate) || moveRate;
        oldColor = properties.color;
        dimensions = Vec3.multiply(scale, properties.dimensions);
        baton = virtualBaton({
            batonName: 'io.highfidelity.tribble:' + entityID, // One winner for each entity
            debugFlow: debug.flow,
            debugSend: debug.send,
            debugReceive: debug.receive
        });
        if (editTimeout) {
            baton.claim(startUpdate, stopUpdateAndReclaim);
            if (editTimeout > 0) {
                Script.setTimeout(stopUpdate, editTimeout * 1000);
            }
        }
        if (moveTimeout) {
            Script.setTimeout(move, 1000);
            if (moveTimeout > 0) {
                Script.setTimeout(function () {
                    shutdown = true;
                }, moveTimeout * 1000);
            }
        }
    };
    this.unload = function () {
        baton.unload();
        shutdown = true;
        stopUpdate();
    };
})