System.register(['src/client/bound-eval.js', 'utils', 'src/client/vivide/utils.js', 'src/client/vivide/vividescriptstep.js', 'src/client/vivide/vivideobject.js', 'src/client/reactive/utils/annotations.js'], function (_export, _context) {
  "use strict";

  var boundEval, uuid, stepFolder, ScriptStep, VivideObject, Annotations;
  return {
    setters: [function (_srcClientBoundEvalJs) {
      boundEval = _srcClientBoundEvalJs.default;
    }, function (_utils) {
      uuid = _utils.uuid;
    }, function (_srcClientVivideUtilsJs) {
      stepFolder = _srcClientVivideUtilsJs.stepFolder;
    }, function (_srcClientVivideVividescriptstepJs) {
      ScriptStep = _srcClientVivideVividescriptstepJs.default;
    }, function (_srcClientVivideVivideobjectJs) {
      VivideObject = _srcClientVivideVivideobjectJs.default;
    }, function (_srcClientReactiveUtilsAnnotationsJs) {
      Annotations = _srcClientReactiveUtilsAnnotationsJs.default;
    }],
    execute: function () {
      const __SystemJSRewritingHack = {};
      _recorder_._src_client_vivide_vividescript_js = _recorder_._src_client_vivide_vividescript_js || {};
      Object.defineProperty(_recorder_._src_client_vivide_vividescript_js, 'boundEval', {
        get() {
          return boundEval;
        },

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

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

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

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

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

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

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

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

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

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

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

        enumerable: true,
        configurable: true
      });
      class Script {
        constructor(view) {
          this.setView(view);
        }

        setView(view) {
          this._view = view;
        }

        /**
         * Access
         */
        setInitialStep(step) {
          return this.initialStep = step;
        }
        getInitialStep() {
          return this.initialStep;
        }

        getLastStep() {
          const firstStep = this.getInitialStep();
          return firstStep && firstStep.getLastStep();
        }

        getLoopStartStep() {
          const lastStep = this.getLastStep();
          return lastStep && lastStep.getNextExecutionStep();
        }

        numberOfSteps() {
          let length = 0;
          this.getInitialStep().iterateLinear(() => length++);
          return length;
        }

        getPrevStep(step) {
          return this.getInitialStep().find(s => s.getNextExecutionStep() === step);
        }

        async forEachStepAsync(cb) {
          await this.getInitialStep().iterateLinearAsync(cb);
        }

        stepsAsArray() {
          const arr = [];
          this.getInitialStep().iterateLinear(step => arr.push(step));

          return arr;
        }

        /**
         * Modify
         */
        async insertStepAfter(stepType, prevStep) {
          const newStep = await ScriptStep.newFromTemplate(stepType);
          newStep.setScript(this);

          prevStep.insertAfter(newStep);

          return newStep;
        }

        removeStep(stepToBeRemoved) {
          if (stepToBeRemoved === this.getLoopStartStep()) {
            this.removeLoop();
          }

          if (stepToBeRemoved === this.getInitialStep()) {
            // First script was removed
            this.setInitialStep(stepToBeRemoved.getNextExecutionStep());
          }

          stepToBeRemoved.remove();
        }

        removeLoop() {
          const lastStep = this.getLastStep();
          lastStep.removeLoopTargetStep();
        }

        setLoopStart(step) {
          const lastStep = step.getLastStep();
          lastStep.setLoopTargetStep(step);
        }

        /**
         * Handling script execution
         */
        gotUpdated() {
          lively.notify('VivideScript::gotIpdated');
          this._view.scriptGotUpdated();
        }

        /**
         * Script execution
         */
        async getViewConfig() {
          const viewConfig = new Annotations();
          const steps = this.stepsAsArray();

          for (let step of steps) {
            // #TODO: just say 'await step.getConfig()' (make it lazy and memoize later)
            const [fn, config] = await step.getExecutable();
            viewConfig.add(config);
          }

          return viewConfig;
        }

        /**
         * Serialization
         */
        static async createDefaultScript(view) {
          const transform = await ScriptStep.newFromTemplate('transform');
          const extract = await ScriptStep.newFromTemplate('extract');
          const descent = await ScriptStep.newFromTemplate('descent');

          transform.insertAfter(extract);
          extract.insertAfter(descent);

          const script = new Script(view);
          script.setInitialStep(transform);
          transform.setScript(script);
          extract.setScript(script);
          descent.setScript(script);

          return script;
        }

        toJSO() {
          const jsonContainer = {};

          this.stepsAsArray().forEach(step => {
            jsonContainer[step.id] = step.toJSO();
          });

          return jsonContainer;
        }

        toJSON() {
          return JSON.stringify(this.toJSO());
        }

        static async fromJSON(json, view) {
          // this is deserialization of a script
          let jsonScripts = JSON.parse(json);
          let scripts = {};

          var firstScriptStep;
          var lastScriptStep;

          const script = new Script(view);
          for (let scriptId in jsonScripts) {
            let step = new ScriptStep(jsonScripts[scriptId].source, jsonScripts[scriptId].type, scriptId);
            step.setScript(script);
            scripts[scriptId] = step;
            if (!firstScriptStep) firstScriptStep = step;
            if (lastScriptStep) {
              lastScriptStep.insertAfter(step);
            }
            lastScriptStep = step;
          }
          for (let scriptId in jsonScripts) {
            let step = scripts[scriptId];
            let stepJSON = jsonScripts[scriptId];
            var loopId = stepJSON["loop-target-step-id"];
            if (loopId) {
              var otherStep = scripts[loopId];
              if (otherStep) {
                step.setLoopTargetStep(otherStep);
              } else {
                debugger;
                lively.warn("Could not restore Vivide Step Loop");
              }
            }
          }

          script.setInitialStep(firstScriptStep);
          return script;
        }
      }

      _export('default', Script);

      Object.defineProperty(_recorder_._src_client_vivide_vividescript_js, 'Script', {
        get() {
          return Script;
        },

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

          return true;
        },

        enumerable: true,
        configurable: true
      });
      // #UPDATE_INSTANCES
      // #TODO: idea: using a list of all object, we can make them become anew
      // go through all object reachable from window
      document.querySelectorAll("vivide-view").forEach(vv => {
        const script = vv.myCurrentScript;
        if (script) {
          script.migrateTo(Script);
        }
      });
    }
  };
});