System.register(['src/client/reactive/active-expression/ae-registry.js', 'src/external/diff-match-patch.js', 'src/client/reactive/active-expression-rewriting/active-expression-rewriting.js'], function (_export, _context) {
  "use strict";

  var AExprRegistry, diff, getAEDataForFile, getHooksInFile;
  return {
    setters: [function (_srcClientReactiveActiveExpressionAeRegistryJs) {
      AExprRegistry = _srcClientReactiveActiveExpressionAeRegistryJs.AExprRegistry;
    }, function (_srcExternalDiffMatchPatchJs) {
      diff = _srcExternalDiffMatchPatchJs.default;
    }, function (_srcClientReactiveActiveExpressionRewritingActiveExpressionRewritingJs) {
      getAEDataForFile = _srcClientReactiveActiveExpressionRewritingActiveExpressionRewritingJs.getAEDataForFile;
      getHooksInFile = _srcClientReactiveActiveExpressionRewritingActiveExpressionRewritingJs.getHooksInFile;
    }],
    execute: function () {
      const __SystemJSRewritingHack = {};
      _recorder_._src_client_reactive_active_expression_ae_debugging_cache_js = _recorder_._src_client_reactive_active_expression_ae_debugging_cache_js || {};
      Object.defineProperty(_recorder_._src_client_reactive_active_expression_ae_debugging_cache_js, 'AExprRegistry', {
        get() {
          return AExprRegistry;
        },

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

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

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

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

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

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

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

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

        constructor() {
          this.registeredDebuggingViews = [];
          this.debouncedUpdateDebuggingViews = _.debounce(this.updateDebggingViews, 100);
          this.changedFiles = new Set();
        }
        /*MD ## Registration MD*/
        async registerFileForAEDebugging(url, context, aeDataCallback) {
          const callback = async () => {
            if (context && (!context.valid || context.valid())) {
              var data = await this.getAEDataForFile(url);
              aeDataCallback(data);
              return true;
            }
            return false;
          };
          this.registeredDebuggingViews.push({ callback, url });

          aeDataCallback((await this.getAEDataForFile(url)));
        }

        getAEDataForFile(url) {
          //Currently only supports RewritingAEs
          return getAEDataForFile(url);
        }

        /*MD ## Caching MD*/

        /*MD ## Code Change API MD*/
        async updateFile(url, oldCode, newCode) {
          AExprRegistry.fileSaved(url);
          try {
            const lineMapping = this.calculateMapping(oldCode, newCode);
            for (const ae of AExprRegistry.getLocationCache().getAEsInFile(url)) {
              const location = ae.meta().get("location");
              this.remapLocation(lineMapping, location);
            }
            for (const hook of await getHooksInFile(url)) {
              hook.getLocations().then(locations => {
                for (const location of locations) {
                  this.remapLocation(lineMapping, location);
                }
              });
            }
          } catch (e) {
            console.error(e);
          }
        }

        remapLocation(lineMapping, location) {
          if (!location) return;
          if (lineMapping.length - 1 < location.start.line) return;
          let [diffType, lineChanged] = lineMapping[location.start.line];

          if (diffType === -1) {
            location.start.line = 0;
          } else {
            location.start.line = diffType;
          }

          let [diffType2, lineChanged2] = lineMapping[location.end.line];

          if (diffType2 === -1) {
            location.end.line = 0;
          } else {
            location.end.line = diffType2;
          }
          if (lineChanged || lineChanged2) {
            lively.notify("Changed AE code for existing AE. There are outdated expressions in the system");
            //ToDo: Check if the content is similar enough, mark AEs as outdated
          }
        }

        calculateMapping(oldCode, newCode) {
          const mapping = []; //For each line of oldCode: new Line in newCode if existing, changedContent if the line no longer exists but can be mapped to a newCode line
          var dmp = new diff.diff_match_patch();
          var a = dmp.diff_linesToChars_(oldCode, newCode);
          var lineText1 = a.chars1;
          var lineText2 = a.chars2;
          var lineArray = a.lineArray;
          var diffs = dmp.diff_main(lineText1, lineText2, false);

          let originalLine = 0;
          let newLine = 0;
          let recentDeletions = 0;
          let recentAdditions = 0;
          for (let [type, data] of diffs) {
            if (type === 0) {
              recentDeletions = 0;
              recentAdditions = 0;
              for (let i = 0; i < data.length; i++) {
                mapping[originalLine] = [newLine, false];
                originalLine++;
                newLine++;
              }
            } else if (type === 1) {
              if (recentDeletions > 0) {
                const matchingLines = Math.max(recentDeletions, data.length);
                for (let i = 0; i < matchingLines; i++) {
                  mapping[originalLine - matchingLines + i] = [newLine, true];
                  newLine++;
                }
                data = data.substring(matchingLines);
              }
              if (data.length > 0) {
                newLine += data.length;
                recentAdditions += data.length;
              }
            } else {
              if (recentAdditions > 0) {
                const matchingLines = Math.max(recentAdditions, data.length);
                for (let i = 0; i < matchingLines; i++) {
                  mapping[originalLine] = [newLine - matchingLines + i, true];
                  originalLine++;
                }
                data = data.substring(matchingLines);
              }
              recentDeletions += data.length;
              for (let i = 0; i < data.length; i++) {
                mapping[originalLine] = [-1, false];
                originalLine++;
              }
            }
          }

          dmp.diff_charsToLines_(diffs, lineArray);

          return mapping;
        }

        updateFiles(files) {
          if (!files) return;
          files.forEach(file => {
            if (file) {
              this.changedFiles.add(file);
            }
          });
          this.debouncedUpdateDebuggingViews();
        }

        async updateDebggingViews() {
          for (let i = 0; i < this.registeredDebuggingViews.length; i++) {
            if (![...this.changedFiles].some(file => file.includes(this.registeredDebuggingViews[i].url))) continue;
            if (!(await this.registeredDebuggingViews[i].callback())) {
              this.registeredDebuggingViews.splice(i, 1);
              i--;
            }
          }
          this.changedFiles = new Set();
        }

      }

      _export('default', AEDebuggingCache);

      Object.defineProperty(_recorder_._src_client_reactive_active_expression_ae_debugging_cache_js, 'AEDebuggingCache', {
        get() {
          return AEDebuggingCache;
        },

        set(thisIsVererySecretVariableName) {
          _export('default', AEDebuggingCache = thisIsVererySecretVariableName);

          return true;
        },

        enumerable: true,
        configurable: true
      });
      const DebuggingCache = new AEDebuggingCache();

      _export('DebuggingCache', DebuggingCache);

      Object.defineProperty(_recorder_._src_client_reactive_active_expression_ae_debugging_cache_js, 'DebuggingCache', {
        get() {
          return DebuggingCache;
        },

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

          return true;
        },

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