System.register(['../utils/event-target.js', 'src/client/preferences.js'], function (_export, _context) {
  "use strict";

  var EventTarget, Preferences;
  return {
    setters: [function (_utilsEventTargetJs) {
      EventTarget = _utilsEventTargetJs.default;
    }, function (_srcClientPreferencesJs) {
      Preferences = _srcClientPreferencesJs.default;
    }],
    execute: function () {
      const __SystemJSRewritingHack = {};
      _recorder_._src_client_reactive_active_expression_ae_registry_js = _recorder_._src_client_reactive_active_expression_ae_registry_js || {};
      Object.defineProperty(_recorder_._src_client_reactive_active_expression_ae_registry_js, 'normalizeLocation', {
        get() {
          return normalizeLocation;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_client_reactive_active_expression_ae_registry_js, 'EventTarget', {
        get() {
          return EventTarget;
        },

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

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_client_reactive_active_expression_ae_registry_js, 'Preferences', {
        get() {
          return Preferences;
        },

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

        enumerable: true,
        configurable: true
      });


      function normalizeLocation(url) {
        if (!url || !url.indexOf) return url;
        return url.substring(url.indexOf("/src/"));
      }

      class LocationCache {

        constructor() {
          this.unknownLocations = { counter: 0, aes: [] };
          this.files = new Map();
        }

        // adds an AE into the cache and returns an unique identifier for this AE.
        add(ae) {
          if (ae.meta().has('location')) {
            let location = ae.meta().get('location');
            let file = normalizeLocation(location.file);
            let locationArray = this._getColumn(file, location.start.line, location.start.column);
            locationArray.aes.push(ae);
            return file + '@' + location.start.line + ':' + location.start.column + "#" + ++locationArray.counter;
          } else {
            this.unknownLocations.aes.push(ae);
            return 'unknown_location#' + ++this.unknownLocations.counter;
          }
        }

        remove(ae) {
          if (ae.meta().has('location')) {
            let location = ae.meta().get('location');
            const aesInLocation = this.getAEsInLocation(location);
            aesInLocation.splice(aesInLocation.indexOf(ae));
          } else {
            this.unknownLocations.aes.splice(this.unknownLocations.aes.indexOf(ae));
          }
        }

        clear() {
          this.files.clear();
          this.unknownLocations = { counter: 0, aes: [] };
        }

        getLoggingMode(file, line = undefined) {
          if (line) {
            if (this._getLine(file, line).loggingMode !== LoggingModes.DEFAULT) {
              return this._getLine(file, line).loggingMode;
            }
          }
          return this._getFile(file).loggingMode;
        }

        setLoggingModeIfDefault(file) {
          const fileObj = this._getFile(file);
          if (fileObj.loggingMode === LoggingModes.DEFAULT) {
            fileObj.loggingMode = LoggingModes.ALL;
          }
        }

        setLoggingMode(mode, file, line = undefined) {
          if (line) {
            this._getLine(file, line).loggingMode = mode;
          } else {
            this._getFile(file).loggingMode = mode;
          }
        }

        getAEsInLocation(location) {
          let file = normalizeLocation(location.file);
          return this.getAEsInColumn(file, location.start.line, location.start.column);
        }

        getAEsInFile(file) {
          return this.extractAEsRecursive(this._getFile(file));
        }

        getAEsInLine(file, line) {
          return this.extractAEsRecursive(this._getLine(file, line));
        }

        getAEsInColumn(file, line, column) {
          return this.extractAEsRecursive(this._getColumn(file, line, column));
        }

        _getFile(file) {
          let fileID = normalizeLocation(file);
          return this.files.getOrCreate(fileID, () => {
            return { loggingMode: LoggingModes.DEFAULT, lines: new Map() };
          });
        }

        _getLine(file, line) {
          return this._getFile(file).lines.getOrCreate(line, () => {
            return { loggingMode: LoggingModes.DEFAULT, columns: new Map() };
          });
        }

        _getColumn(file, line, column) {
          return this._getLine(file, line).columns.getOrCreate(column, () => {
            return { counter: 0, aes: [] };
          });
        }

        extractAEsRecursive(map) {
          if (!map) return [];
          if (map.lines) return this.extractAEsRecursive(map.lines);
          if (map.columns) return this.extractAEsRecursive(map.columns);
          if (map instanceof Map) {
            return [...map.values()].flatMap(v => this.extractAEsRecursive(v));
          } else {
            return map.aes;
          }
        }
      }

      Object.defineProperty(_recorder_._src_client_reactive_active_expression_ae_registry_js, 'LocationCache', {
        get() {
          return LocationCache;
        },

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

        enumerable: true,
        configurable: true
      });
      const LoggingModes = {
        ALL: "ALL",
        SMART: "SMART",
        NONE: "NONE",
        DEFAULT: "DEFAULT"
        /*MD ## Registry of Active Expressions MD*/
      };
      _export('LoggingModes', LoggingModes);

      Object.defineProperty(_recorder_._src_client_reactive_active_expression_ae_registry_js, 'LoggingModes', {
        get() {
          return LoggingModes;
        },

        set(thisIsVererySecretVariableName) {
          _export('LoggingModes', LoggingModes = thisIsVererySecretVariableName);

          return true;
        },

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

        constructor(oldRegistry) {
          if (oldRegistry) {
            this.aexprs = oldRegistry.aexprs;
            this.aesPerLocation = oldRegistry.aesPerLocation;
            this.eventTarget = oldRegistry.eventTarget;
            this.callbackStack = oldRegistry.callbackStack;
            this.evaluationStack = oldRegistry.evaluationStack;
          } else {
            this.aexprs = new Set();
            this.aesPerLocation = new LocationCache();
            this.eventTarget = new EventTarget();
            this.callbackStack = [];
            this.evaluationStack = [];
          }
        }

        get loggingMode() {
          return Preferences.get("EnableAEDebugging") ? Preferences.get("SmartAELogging") ? LoggingModes.SMART : LoggingModes.ALL : LoggingModes.NONE;
        }

        /**
         * Handling membership
         */
        addAExpr(aexpr) {
          this.aexprs.add(aexpr);
          this.buildIdFor(aexpr);
          this.eventTarget.dispatchEvent('add', aexpr);
        }

        removeAExpr(aexpr) {
          const deleted = this.aexprs.delete(aexpr);
          this.aesPerLocation.remove(aexpr);
          if (deleted) {
            this.eventTarget.dispatchEvent('remove', aexpr);
          }
        }

        updateAExpr(aexpr) {
          this.eventTarget.dispatchEvent('update', aexpr);
        }

        on(type, callback) {
          return this.eventTarget.addEventListener(type, callback);
        }

        off(type, callback) {
          return this.eventTarget.removeEventListener(type, callback);
        }

        addToCallbackStack(ae, callback) {
          this.callbackStack.push({ ae, callback });
        }

        popCallbackStack() {
          this.callbackStack.pop();
        }

        addToEvaluationStack(ae) {
          this.evaluationStack.push(ae);
        }

        popEvaluationStack() {
          this.evaluationStack.pop();
        }

        evaluationStack() {
          return this.evaluationStack;
        }

        buildIdFor(ae) {
          let locationId = this.aesPerLocation.add(ae);
          ae.meta({ id: locationId });
        }

        /**
         * For Development purpose if the registry gets into inconsistent state
         */
        purge() {
          for (let each of this.aexprs) {
            each._isDisposed = true;
          }
          this.eventTarget.callbacks.clear();
          this.aexprs.clear();
          this.aesPerLocation.clear();
        }

        /**
         * Access
         */
        allAsArray() {
          return Array.from(this.aexprs);
        }

        getLocationCache() {
          return this.aesPerLocation;
        }

        addEventListener(reference, callback) {
          if (!this.listeners) this.listeners = [];
          this.listeners.push({ reference, callback });
        }

        removeEventListener(reference) {
          if (!this.listeners) return;
          this.listeners = this.listeners.filter(listener => listener.reference !== reference);
        }

        eventListeners() {
          if (!this.listeners) return [];
          return this.listeners;
        }

        fileSaved(url) {
          if (this.loggingMode === LoggingModes.SMART) {
            this.aesPerLocation.setLoggingModeIfDefault(url);
          }
        }

        toggleLoggingLocation(url, line = undefined) {
          this.setLoggingLocation(!this.shouldLog(url, line), url, line);
        }

        setLoggingLocation(enable, url, line = undefined) {
          this.aesPerLocation.setLoggingMode(enable ? LoggingModes.ALL : LoggingModes.NONE, url, line);
        }

        shouldLog(url, line = undefined) {
          let mode = this.aesPerLocation.getLoggingMode(url, line);
          mode = mode === LoggingModes.DEFAULT ? this.loggingMode : mode;
          switch (mode) {
            case LoggingModes.ALL:
              return true;
            case LoggingModes.NONE:
              return false;
            case LoggingModes.SMART:
              return this.isInteresting(url);
            default:
              return false;
          }
        }

        isInteresting(url) {
          //is currently open
          url = normalizeLocation(url);
          const openURLs = new Set(Array.from(document.body.querySelectorAll('lively-container')).map(c => normalizeLocation(c.getAttribute('src'))));
          return openURLs.has(url);
        }
      }
      Object.defineProperty(_recorder_._src_client_reactive_active_expression_ae_registry_js, 'AExprRegistryClass', {
        get() {
          return AExprRegistryClass;
        },

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

        enumerable: true,
        configurable: true
      });
      self.__aeRegistry__ = new AExprRegistryClass(self.__aeRegistry__);
      const AExprRegistry = self.__aeRegistry__;

      _export('AExprRegistry', AExprRegistry);

      Object.defineProperty(_recorder_._src_client_reactive_active_expression_ae_registry_js, 'AExprRegistry', {
        get() {
          return AExprRegistry;
        },

        set(thisIsVererySecretVariableName) {
          _export('AExprRegistry', AExprRegistry = thisIsVererySecretVariableName);

          return true;
        },

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