System.register(["./defaults.js", "./utils.js", "./collections.js"], function (_export, _context) {
  "use strict";

  var defaultExample, deepCopy, DefaultMap;
  return {
    setters: [function (_defaultsJs) {
      defaultExample = _defaultsJs.defaultExample;
    }, function (_utilsJs) {
      deepCopy = _utilsJs.deepCopy;
    }, function (_collectionsJs) {
      DefaultMap = _collectionsJs.DefaultMap;
    }],
    execute: function () {
      const __SystemJSRewritingHack = {};
      _recorder_._src_babylonian_programming_editor_utils_tracker_js = _recorder_._src_babylonian_programming_editor_utils_tracker_js || {};
      Object.defineProperty(_recorder_._src_babylonian_programming_editor_utils_tracker_js, "defaultExample", {
        get() {
          return defaultExample;
        },

        set(thisIsVererySecretVariableName) {
          defaultExample = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_babylonian_programming_editor_utils_tracker_js, "deepCopy", {
        get() {
          return deepCopy;
        },

        set(thisIsVererySecretVariableName) {
          deepCopy = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_babylonian_programming_editor_utils_tracker_js, "DefaultMap", {
        get() {
          return DefaultMap;
        },

        set(thisIsVererySecretVariableName) {
          DefaultMap = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      class Tracker {

        constructor() {
          this.ids = new DefaultMap( // Map(id, Map(exampleId, Map(runId, {before, after: {type, value, name}]})) 
          DefaultMap.builder(DefaultMap.builder(Object)));
          this.idIterationParents = new DefaultMap(0); // Map(id, iterationId); 
          this.iterations = new DefaultMap( // Map(id, Map(exampleId, iterationCounter))
          DefaultMap.builder(0));
          this.errors = new Map(); // Map(exampleId, errorMsg);
          this.executedBlocks = new Set(); // Set(id)
          // this.exampleId = defaultExample().id;
          this.exampleIds = new Set();
          this.exampleIds.add(this.exampleId);
          this.timer = new Timer();
          this._identities = new Map(); // Map(identity, symbol)
          this._symbolProvider = new IdentitySymbolProvider();
        }

        reset() {
          this.ids.clear();
          this.idIterationParents.clear();
          this.iterations.clear();
          this.errors.clear();
          this.executedBlocks.clear();
          this.exampleId = defaultExample().id;
          this.exampleIds.clear();
          this.exampleIds.add(this.exampleId);
          this._identities.clear();
          this._symbolProvider.reset();
        }

        id(id, exampleId, iterationParentId, runId, value, name, keyword = "after") {
          const originalValue = value;
          let prototype;

          // console.log('TRACKER ID ' + id 
          //             + " exampleId: " + exampleId 
          //             +  " iterationParentId: " + iterationParentId
          //             +  " runId: " + runId
          //             + " Value: " + value
          //             + " keyword:" + keyword)
          // #TODO #ContinueHere for implementing aysnc Babylonian Programming....
          // next steps is signalling that this item has changed and might be in need for updating...
          if (!["before", "after"].includes(keyword)) {
            return value;
          }
          this.idIterationParents.set(id, iterationParentId);

          // Check and assign object identity
          if (value instanceof Object) {
            if (this._identities.has(value)) {
              value.__tracker_identity = this._identities.get(value);
            } else {
              value.__tracker_identity = this._symbolProvider.next();
              this._identities.set(value, value.__tracker_identity);
            }
          }

          // Check and store object type
          let type = typeof value;
          if (value && value.constructor && value.constructor.name) {
            type = value.constructor.name;
          }

          if (value && value.constructor && value.constructor.prototype) {
            prototype = value.constructor.prototype;
          }

          // Copy the value
          if (value instanceof CanvasRenderingContext2D) {
            value = value.getImageData(0, 0, value.canvas.width, value.canvas.height);
          } else if (value instanceof Object) {
            value = deepCopy(value);
          } else {
            // don't copy values since they are unmutable
          }

          if (value instanceof Object) {
            value.__tracker_time = Date.now();
          }

          this.ids.get(id).get(exampleId).get(runId)[keyword] = {
            type: type,
            value: value,
            prototype: prototype,
            name: name
          };

          return originalValue;
        }

        block(id) {
          this.executedBlocks.add(id);
        }

        iteration(id) {
          const iterationMap = this.iterations.get(id);
          const iterationCount = iterationMap.get(this.exampleId);
          iterationMap.set(this.exampleId, iterationCount + 1);
          return iterationCount;
        }

        error(errorMsg) {
          this.errors.set(this.exampleId, errorMsg);
        }

        get exampleId() {
          return Zone.current.babylonianExampleId;
        }

        set exampleId(id) {
          // throw new Error("exampleId should not be set")
        }

        example(exampleId) {
          // this.exampleId = exampleId; // for async examples this is not enough... Zone.current.babylonianExampleId
          this.exampleIds.add(this.exampleId);
        }
      }

      _export("default", Tracker);

      Object.defineProperty(_recorder_._src_babylonian_programming_editor_utils_tracker_js, "Tracker", {
        get() {
          return Tracker;
        },

        set(thisIsVererySecretVariableName) {
          _export("default", Tracker = thisIsVererySecretVariableName);

          return true;
        },

        enumerable: true,
        configurable: true
      });
      class IdentitySymbolProvider {
        constructor() {
          this._identitySymbols = ['🐶', '🐺', '🐱', '🐭', '🐹', '🐰', '🐸', '🐯', '🐨', '🐻', '🐷', '🐽', '🐮', '🐗', '🐵', '🐒', '🐴', '🐑', '🐘', '🐼', '🐧', '🐦', '🐤', '🐥', '🐣', '🐔', '🐍', '🐢', '🐛', '🐝', '🐜', '🐞', '🐌', '🐙', '🐚', '🐠', '🐟', '🐳', '🐋', '🐄', '🐏', '🐀', '🐃', '🐅', '🐇', '🐉', '🐎', '🐐', '🐓', '🐕', '🐖', '🐁', '🐂', '🐲', '🐡', '🐊'];
          this._index = 0;
        }

        next() {
          return this._identitySymbols[this._index++ % this._identitySymbols.length];
        }

        reset() {
          this._index = 0;
        }
      }

      _export("IdentitySymbolProvider", IdentitySymbolProvider);

      Object.defineProperty(_recorder_._src_babylonian_programming_editor_utils_tracker_js, "IdentitySymbolProvider", {
        get() {
          return IdentitySymbolProvider;
        },

        set(thisIsVererySecretVariableName) {
          _export("IdentitySymbolProvider", IdentitySymbolProvider = thisIsVererySecretVariableName);

          return true;
        },

        enumerable: true,
        configurable: true
      });
      class Timer {

        static get MaxRuntime() {
          return 2000;
        }

        constructor() {
          this._maxRuntime = Timer.MaxRuntime;
          this._startTime = null;
        }

        start() {
          this._startTime = +new Date();
        }

        reset() {
          this._startTime = null;
        }

        check() {
          if (this._startTime === null) {
            return;
          }

          const time = Date.now();
          if (time - this._startTime > this._maxRuntime) {
            throw new Error("Timeout reached. Maybe there is an inifinite loop?");
          }
        }
      }

      _export("Timer", Timer);

      Object.defineProperty(_recorder_._src_babylonian_programming_editor_utils_tracker_js, "Timer", {
        get() {
          return Timer;
        },

        set(thisIsVererySecretVariableName) {
          _export("Timer", Timer = thisIsVererySecretVariableName);

          return true;
        },

        enumerable: true,
        configurable: true
      });
    }
  };
});