text: /**
   * @license
   * Copyright Google Inc. All Rights Reserved.
   *
   * Use of this source code is governed by an MIT-style license that can be
   * found in the LICENSE file at https://angular.io/license
   */
  import { StringMapWrapper } from '../../facade/collection';
  import { isJsObject, looseIdentical, stringify } from '../../facade/lang';
  export var DefaultKeyValueDifferFactory = (function () {
      function DefaultKeyValueDifferFactory() {
      }
      DefaultKeyValueDifferFactory.prototype.supports = function (obj) { return obj instanceof Map || isJsObject(obj); };
      DefaultKeyValueDifferFactory.prototype.create = function (cdRef) { return new DefaultKeyValueDiffer(); };
      return DefaultKeyValueDifferFactory;
  }());
  export var DefaultKeyValueDiffer = (function () {
      function DefaultKeyValueDiffer() {
          this._records = new Map();
          this._mapHead = null;
          this._previousMapHead = null;
          this._changesHead = null;
          this._changesTail = null;
          this._additionsHead = null;
          this._additionsTail = null;
          this._removalsHead = null;
          this._removalsTail = null;
      }
      Object.defineProperty(DefaultKeyValueDiffer.prototype, "isDirty", {
          get: function () {
              return this._additionsHead !== null || this._changesHead !== null ||
                  this._removalsHead !== null;
          },
          enumerable: true,
          configurable: true
      });
      DefaultKeyValueDiffer.prototype.forEachItem = function (fn) {
          var record;
          for (record = this._mapHead; record !== null; record = record._next) {
              fn(record);
          }
      };
      DefaultKeyValueDiffer.prototype.forEachPreviousItem = function (fn) {
          var record;
          for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
              fn(record);
          }
      };
      DefaultKeyValueDiffer.prototype.forEachChangedItem = function (fn) {
          var record;
          for (record = this._changesHead; record !== null; record = record._nextChanged) {
              fn(record);
          }
      };
      DefaultKeyValueDiffer.prototype.forEachAddedItem = function (fn) {
          var record;
          for (record = this._additionsHead; record !== null; record = record._nextAdded) {
              fn(record);
          }
      };
      DefaultKeyValueDiffer.prototype.forEachRemovedItem = function (fn) {
          var record;
          for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
              fn(record);
          }
      };
      DefaultKeyValueDiffer.prototype.diff = function (map) {
          if (!map) {
              map = new Map();
          }
          else if (!(map instanceof Map || isJsObject(map))) {
              throw new Error("Error trying to diff '" + map + "'");
          }
          return this.check(map) ? this : null;
      };
      DefaultKeyValueDiffer.prototype.onDestroy = function () { };
      DefaultKeyValueDiffer.prototype.check = function (map) {
          var _this = this;
          this._reset();
          var records = this._records;
          var oldSeqRecord = this._mapHead;
          var lastOldSeqRecord = null;
          var lastNewSeqRecord = null;
          var seqChanged = false;
          this._forEach(map, function (value, key) {
              var newSeqRecord;
              if (oldSeqRecord && key === oldSeqRecord.key) {
                  newSeqRecord = oldSeqRecord;
                  _this._maybeAddToChanges(newSeqRecord, value);
              }
              else {
                  seqChanged = true;
                  if (oldSeqRecord !== null) {
                      _this._removeFromSeq(lastOldSeqRecord, oldSeqRecord);
                      _this._addToRemovals(oldSeqRecord);
                  }
                  if (records.has(key)) {
                      newSeqRecord = records.get(key);
                      _this._maybeAddToChanges(newSeqRecord, value);
                  }
                  else {
                      newSeqRecord = new KeyValueChangeRecord(key);
                      records.set(key, newSeqRecord);
                      newSeqRecord.currentValue = value;
                      _this._addToAdditions(newSeqRecord);
                  }
              }
              if (seqChanged) {
                  if (_this._isInRemovals(newSeqRecord)) {
                      _this._removeFromRemovals(newSeqRecord);
                  }
                  if (lastNewSeqRecord == null) {
                      _this._mapHead = newSeqRecord;
                  }
                  else {
                      lastNewSeqRecord._next = newSeqRecord;
                  }
              }
              lastOldSeqRecord = oldSeqRecord;
              lastNewSeqRecord = newSeqRecord;
              oldSeqRecord = oldSeqRecord && oldSeqRecord._next;
          });
          this._truncate(lastOldSeqRecord, oldSeqRecord);
          return this.isDirty;
      };
      /** @internal */
      DefaultKeyValueDiffer.prototype._reset = function () {
          if (this.isDirty) {
              var record = void 0;
              // Record the state of the mapping
              for (record = this._previousMapHead = this._mapHead; record !== null; record = record._next) {
                  record._nextPrevious = record._next;
              }
              for (record = this._changesHead; record !== null; record = record._nextChanged) {
                  record.previousValue = record.currentValue;
              }
              for (record = this._additionsHead; record != null; record = record._nextAdded) {
                  record.previousValue = record.currentValue;
              }
              this._changesHead = this._changesTail = null;
              this._additionsHead = this._additionsTail = null;
              this._removalsHead = this._removalsTail = null;
          }
      };
      /** @internal */
      DefaultKeyValueDiffer.prototype._truncate = function (lastRecord, record) {
          while (record !== null) {
              if (lastRecord === null) {
                  this._mapHead = null;
              }
              else {
                  lastRecord._next = null;
              }
              var nextRecord = record._next;
              this._addToRemovals(record);
              lastRecord = record;
              record = nextRecord;
          }
          for (var rec = this._removalsHead; rec !== null; rec = rec._nextRemoved) {
              rec.previousValue = rec.currentValue;
              rec.currentValue = null;
              this._records.delete(rec.key);
          }
      };
      DefaultKeyValueDiffer.prototype._maybeAddToChanges = function (record, newValue) {
          if (!looseIdentical(newValue, record.currentValue)) {
              record.previousValue = record.currentValue;
              record.currentValue = newValue;
              this._addToChanges(record);
          }
      };
      /** @internal */
      DefaultKeyValueDiffer.prototype._isInRemovals = function (record) {
          return record === this._removalsHead || record._nextRemoved !== null ||
              record._prevRemoved !== null;
      };
      /** @internal */
      DefaultKeyValueDiffer.prototype._addToRemovals = function (record) {
          if (this._removalsHead === null) {
              this._removalsHead = this._removalsTail = record;
          }
          else {
              this._removalsTail._nextRemoved = record;
              record._prevRemoved = this._removalsTail;
              this._removalsTail = record;
          }
      };
      /** @internal */
      DefaultKeyValueDiffer.prototype._removeFromSeq = function (prev, record) {
          var next = record._next;
          if (prev === null) {
              this._mapHead = next;
          }
          else {
              prev._next = next;
          }
          record._next = null;
      };
      /** @internal */
      DefaultKeyValueDiffer.prototype._removeFromRemovals = function (record) {
          var prev = record._prevRemoved;
          var next = record._nextRemoved;
          if (prev === null) {
              this._removalsHead = next;
          }
          else {
              prev._nextRemoved = next;
          }
          if (next === null) {
              this._removalsTail = prev;
          }
          else {
              next._prevRemoved = prev;
          }
          record._prevRemoved = record._nextRemoved = null;
      };
      /** @internal */
      DefaultKeyValueDiffer.prototype._addToAdditions = function (record) {
          if (this._additionsHead === null) {
              this._additionsHead = this._additionsTail = record;
          }
          else {
              this._additionsTail._nextAdded = record;
              this._additionsTail = record;
          }
      };
      /** @internal */
      DefaultKeyValueDiffer.prototype._addToChanges = function (record) {
          if (this._changesHead === null) {
              this._changesHead = this._changesTail = record;
          }
          else {
              this._changesTail._nextChanged = record;
              this._changesTail = record;
          }
      };
      DefaultKeyValueDiffer.prototype.toString = function () {
          var items = [];
          var previous = [];
          var changes = [];
          var additions = [];
          var removals = [];
          var record;
          for (record = this._mapHead; record !== null; record = record._next) {
              items.push(stringify(record));
          }
          for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
              previous.push(stringify(record));
          }
          for (record = this._changesHead; record !== null; record = record._nextChanged) {
              changes.push(stringify(record));
          }
          for (record = this._additionsHead; record !== null; record = record._nextAdded) {
              additions.push(stringify(record));
          }
          for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
              removals.push(stringify(record));
          }
          return 'map: ' + items.join(', ') + '\n' +
              'previous: ' + previous.join(', ') + '\n' +
              'additions: ' + additions.join(', ') + '\n' +
              'changes: ' + changes.join(', ') + '\n' +
              'removals: ' + removals.join(', ') + '\n';
      };
      /** @internal */
      DefaultKeyValueDiffer.prototype._forEach = function (obj, fn) {
          if (obj instanceof Map) {
              obj.forEach(fn);
          }
          else {
              StringMapWrapper.forEach(obj, fn);
          }
      };
      return DefaultKeyValueDiffer;
  }());
  /**
   * @stable
   */
  export var KeyValueChangeRecord = (function () {
      function KeyValueChangeRecord(key) {
          this.key = key;
          this.previousValue = null;
          this.currentValue = null;
          /** @internal */
          this._nextPrevious = null;
          /** @internal */
          this._next = null;
          /** @internal */
          this._nextAdded = null;
          /** @internal */
          this._nextRemoved = null;
          /** @internal */
          this._prevRemoved = null;
          /** @internal */
          this._nextChanged = null;
      }
      KeyValueChangeRecord.prototype.toString = function () {
          return looseIdentical(this.previousValue, this.currentValue) ?
              stringify(this.key) :
              (stringify(this.key) + '[' + stringify(this.previousValue) + '->' +
                  stringify(this.currentValue) + ']');
      };
      return KeyValueChangeRecord;
  }());
  //# sourceMappingURL=default_keyvalue_differ.js.map