import Pulse from './Pulse';
import {error, inherits, isArray} from 'vega-util';

/**
 * Represents a set of multiple pulses. Used as input for operators
 * that accept multiple pulses at a time. Contained pulses are
 * accessible via the public "pulses" array property. This pulse doe
 * not carry added, removed or modified tuples directly. However,
 * the visit method can be used to traverse all such tuples contained
 * in sub-pulses with a timestamp matching this parent multi-pulse.
 * @constructor
 * @param {Dataflow} dataflow - The backing dataflow instance.
 * @param {number} stamp - The timestamp.
 * @param {Array<Pulse>} pulses - The sub-pulses for this multi-pulse.
 */
export default function MultiPulse(dataflow, stamp, pulses, encode) {
  var p = this,
      c = 0,
      pulse, hash, i, n, f;

  this.dataflow = dataflow;
  this.stamp = stamp;
  this.fields = null;
  this.encode = encode || null;
  this.pulses = pulses;

  for (i=0, n=pulses.length; i<n; ++i) {
    pulse = pulses[i];
    if (pulse.stamp !== stamp) continue;

    if (pulse.fields) {
      hash = p.fields || (p.fields = {});
      for (f in pulse.fields) { hash[f] = 1; }
    }

    if (pulse.changed(p.ADD)) c |= p.ADD;
    if (pulse.changed(p.REM)) c |= p.REM;
    if (pulse.changed(p.MOD)) c |= p.MOD;
  }

  this.changes = c;
}

var prototype = inherits(MultiPulse, Pulse);

/**
 * Creates a new pulse based on the values of this pulse.
 * The dataflow, time stamp and field modification values are copied over.
 * @return {Pulse}
 */
prototype.fork = function() {
  if (arguments.length && (arguments[0] & Pulse.prototype.ALL)) {
    error('MultiPulse fork does not support tuple change sets.');
  }
  return new Pulse(this.dataflow).init(this, 0);
};

prototype.changed = function(flags) {
  return this.changes & flags;
};

prototype.modified = function(_) {
  var p = this, fields = p.fields;
  return !(fields && (p.changes & p.MOD)) ? 0
    : isArray(_) ? _.some(function(f) { return fields[f]; })
    : fields[_];
};

prototype.filter = function() {
  error('MultiPulse does not support filtering.');
};

prototype.materialize = function() {
  error('MultiPulse does not support materialization.');
};

prototype.visit = function(flags, visitor) {
  var pulses = this.pulses, i, n;

  for (i=0, n=pulses.length; i<n; ++i) {
    if (pulses[i].stamp === this.stamp) {
      pulses[i].visit(flags, visitor);
    }
  }

  return this;
};

@@ -2248,16 +2248,32 @@
 %7B%0A  var 
+p = this,%0A      
 pulses =
@@ -2277,20 +2277,17 @@
 s = 
-this
+p
 .pulses,
  i, 
@@ -2286,43 +2286,162 @@
 ses,
- i, n;%0A%0A  for (i=0, n=pulses.length
+%0A      n = pulses.length,%0A      i = 0;%0A%0A  if (flags & p.SOURCE) %7B%0A    for (; i%3Cn; ++i) %7B%0A      pulses%5Bi%5D.visit(flags, visitor);%0A    %7D%0A  %7D else %7B%0A    for (
 ; i%3C
@@ -2450,24 +2450,26 @@
  ++i) %7B%0A    
+  
 if (pulses%5Bi
@@ -2480,20 +2480,17 @@
 amp === 
-this
+p
 .stamp) 
@@ -2487,24 +2487,26 @@
  p.stamp) %7B%0A
+  
       pulses
@@ -2532,22 +2532,30 @@
 sitor);%0A
+  
     %7D%0A
+    %7D%0A
   %7D%0A%0A  r
@@ -2560,17 +2560,14 @@
  return 
-this
+p
 ;%0A%7D;%0A

Fix MultiPulse visit bug.