/* Copyright © 2015-2016 David Valdman */
define(function(require, exports, module){
var OptionsManager = require('../core/OptionsManager');
var Stream = require('../streams/Stream');
var preTickQueue = require('../core/queues/preTickQueue');
var dirtyQueue = require('../core/queues/dirtyQueue');
/**
* Accumulator is a Stream that accumulates a value given by a
* number or array of numbers.
*
* It emits `start`, `update` and `end` events.
*
* @example
*
* var accumulator = new Accumulator(0);
*
* // this gives the total displacement of mouse input
* accumulator.subscribe(mouseInput.pluck('delta'));
*
*
* @class Accumulator
* @extends Streams.Stream
* @namespace Streams
* @constructor
* @param [sum] {Number|Array} Initial value
* @param [options] {Object} Options
* @param [options.min] {Number} Set a minimum value
* @param [options.max] {Number} Set a maximum value
*/
function Accumulator(sum, options){
this.options = OptionsManager.setOptions(this, options);
// TODO: is this state necessary?
this.sum = undefined;
if (sum !== undefined) this.set(sum);
Stream.call(this, {
start : function(){ return this.sum || 0; }.bind(this),
update : function(){ return this.sum; }.bind(this),
end : function(){ return this.sum || 0; }.bind(this)
});
// TODO: is `start` event necessary?
this._eventInput.on('start', function(value){
if (this.sum !== undefined) return;
if (value instanceof Array) {
this.sum = [];
for (var i = 0; i < value.length; i++)
this.sum[i] = clamp(value[i], this.options.min, this.options.max);
}
else this.sum = clamp(value, this.options.min, this.options.max);
}.bind(this));
this._eventInput.on('update', function(delta){
if (delta instanceof Array){
for (var i = 0; i < delta.length; i++){
this.sum[i] += delta[i];
this.sum[i] = clamp(this.sum[i], this.options.min, this.options.max);
}
}
else {
this.sum += delta;
this.sum = clamp(this.sum, this.options.min, this.options.max);
}
}.bind(this));
}
Accumulator.prototype = Object.create(Stream.prototype);
Accumulator.prototype.constructor = Accumulator;
Accumulator.DEFAULT_OPTIONS = {
min : -Infinity,
max : Infinity
};
function clamp(value, min, max){
return Math.min(Math.max(value, min), max);
}
/**
* Set accumulated value.
*
* @method set
* @param sum {Number} Current value
* @param [silent=false] {Boolean} Flag to suppress events
*/
Accumulator.prototype.set = function(sum, silent){
this.sum = sum;
if (silent === true) return;
var self = this;
preTickQueue.push(function(){
self.trigger('start', sum);
dirtyQueue.push(function(){
self.trigger('end', sum);
});
})
};
/**
* Returns current accumulated value.
*
* @method get
* @return {Number}
*/
Accumulator.prototype.get = function(){
return this.sum;
};
module.exports = Accumulator;
});