text: import { assertionsEnabled, isBlank } from 'angular2/src/facade/lang';
  import { BaseException } from 'angular2/src/facade/exceptions';
  import { ListWrapper } from 'angular2/src/facade/collection';
  import { AbstractChangeDetector } from './abstract_change_detector';
  import { ChangeDetectionUtil } from './change_detection_util';
  import { RecordType } from './proto_record';
  import { CodegenNameUtil, sanitizeName } from './codegen_name_util';
  import { CodegenLogicUtil } from './codegen_logic_util';
  import { codify } from './codegen_facade';
  import { ChangeDetectorState } from './constants';
  import { createPropertyRecords, createEventRecords } from './proto_change_detector';
  /**
   * The code generator takes a list of proto records and creates a function/class
   * that "emulates" what the developer would write by hand to implement the same
   * kind of behaviour.
   *
   * This code should be kept in sync with the Dart transformer's
   * `angular2.transform.template_compiler.change_detector_codegen` library. If you make updates
   * here, please make equivalent changes there.
  */
  const IS_CHANGED_LOCAL = "isChanged";
  const CHANGES_LOCAL = "changes";
  export class ChangeDetectorJITGenerator {
      constructor(definition, changeDetectionUtilVarName, abstractChangeDetectorVarName, changeDetectorStateVarName) {
          this.changeDetectionUtilVarName = changeDetectionUtilVarName;
          this.abstractChangeDetectorVarName = abstractChangeDetectorVarName;
          this.changeDetectorStateVarName = changeDetectorStateVarName;
          var propertyBindingRecords = createPropertyRecords(definition);
          var eventBindingRecords = createEventRecords(definition);
          var propertyBindingTargets = definition.bindingRecords.map(b => b.target);
          this.id = definition.id;
          this.changeDetectionStrategy = definition.strategy;
          this.genConfig = definition.genConfig;
          this.records = propertyBindingRecords;
          this.propertyBindingTargets = propertyBindingTargets;
          this.eventBindings = eventBindingRecords;
          this.directiveRecords = definition.directiveRecords;
          this._names = new CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords, this.changeDetectionUtilVarName);
          this._logic =
              new CodegenLogicUtil(this._names, this.changeDetectionUtilVarName, this.changeDetectorStateVarName, this.changeDetectionStrategy);
          this.typeName = sanitizeName(`ChangeDetector_${this.id}`);
      }
      generate() {
          var factorySource = `
        ${this.generateSource()}
        return function(dispatcher) {
          return new ${this.typeName}(dispatcher);
        }
      `;
          return new Function(this.abstractChangeDetectorVarName, this.changeDetectionUtilVarName, this.changeDetectorStateVarName, factorySource)(AbstractChangeDetector, ChangeDetectionUtil, ChangeDetectorState);
      }
      generateSource() {
          return `
        var ${this.typeName} = function ${this.typeName}(dispatcher) {
          ${this.abstractChangeDetectorVarName}.call(
              this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length},
              ${this.typeName}.gen_propertyBindingTargets, ${this.typeName}.gen_directiveIndices,
              ${codify(this.changeDetectionStrategy)});
          this.dehydrateDirectives(false);
        }
  
        ${this.typeName}.prototype = Object.create(${this.abstractChangeDetectorVarName}.prototype);
  
        ${this.typeName}.prototype.detectChangesInRecordsInternal = function(throwOnChange) {
          ${this._names.genInitLocals()}
          var ${IS_CHANGED_LOCAL} = false;
          var ${CHANGES_LOCAL} = null;
  
          ${this._genAllRecords(this.records)}
        }
  
        ${this._maybeGenHandleEventInternal()}
  
        ${this._maybeGenAfterContentLifecycleCallbacks()}
  
        ${this._maybeGenAfterViewLifecycleCallbacks()}
  
        ${this._maybeGenHydrateDirectives()}
  
        ${this._maybeGenDehydrateDirectives()}
  
        ${this._genPropertyBindingTargets()}
  
        ${this._genDirectiveIndices()}
      `;
      }
      /** @internal */
      _genPropertyBindingTargets() {
          var targets = this._logic.genPropertyBindingTargets(this.propertyBindingTargets, this.genConfig.genDebugInfo);
          return `${this.typeName}.gen_propertyBindingTargets = ${targets};`;
      }
      /** @internal */
      _genDirectiveIndices() {
          var indices = this._logic.genDirectiveIndices(this.directiveRecords);
          return `${this.typeName}.gen_directiveIndices = ${indices};`;
      }
      /** @internal */
      _maybeGenHandleEventInternal() {
          if (this.eventBindings.length > 0) {
              var handlers = this.eventBindings.map(eb => this._genEventBinding(eb)).join("\n");
              return `
          ${this.typeName}.prototype.handleEventInternal = function(eventName, elIndex, locals) {
            var ${this._names.getPreventDefaultAccesor()} = false;
            ${this._names.genInitEventLocals()}
            ${handlers}
            return ${this._names.getPreventDefaultAccesor()};
          }
        `;
          }
          else {
              return '';
          }
      }
      /** @internal */
      _genEventBinding(eb) {
          let codes = [];
          this._endOfBlockIdxs = [];
          ListWrapper.forEachWithIndex(eb.records, (r, i) => {
              let code;
              if (r.isConditionalSkipRecord()) {
                  code = this._genConditionalSkip(r, this._names.getEventLocalName(eb, i));
              }
              else if (r.isUnconditionalSkipRecord()) {
                  code = this._genUnconditionalSkip(r);
              }
              else {
                  code = this._genEventBindingEval(eb, r);
              }
              code += this._genEndOfSkipBlock(i);
              codes.push(code);
          });
          return `
      if (eventName === "${eb.eventName}" && elIndex === ${eb.elIndex}) {
        ${codes.join("\n")}
      }`;
      }
      /** @internal */
      _genEventBindingEval(eb, r) {
          if (r.lastInBinding) {
              var evalRecord = this._logic.genEventBindingEvalValue(eb, r);
              var markPath = this._genMarkPathToRootAsCheckOnce(r);
              var prevDefault = this._genUpdatePreventDefault(eb, r);
              return `${evalRecord}\n${markPath}\n${prevDefault}`;
          }
          else {
              return this._logic.genEventBindingEvalValue(eb, r);
          }
      }
      /** @internal */
      _genMarkPathToRootAsCheckOnce(r) {
          var br = r.bindingRecord;
          if (br.isDefaultChangeDetection()) {
              return "";
          }
          else {
              return `${this._names.getDetectorName(br.directiveRecord.directiveIndex)}.markPathToRootAsCheckOnce();`;
          }
      }
      /** @internal */
      _genUpdatePreventDefault(eb, r) {
          var local = this._names.getEventLocalName(eb, r.selfIndex);
          return `if (${local} === false) { ${this._names.getPreventDefaultAccesor()} = true};`;
      }
      /** @internal */
      _maybeGenDehydrateDirectives() {
          var destroyPipesCode = this._names.genPipeOnDestroy();
          if (destroyPipesCode) {
              destroyPipesCode = `if (destroyPipes) { ${destroyPipesCode} }`;
          }
          var dehydrateFieldsCode = this._names.genDehydrateFields();
          if (!destroyPipesCode && !dehydrateFieldsCode)
              return '';
          return `${this.typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
          ${destroyPipesCode}
          ${dehydrateFieldsCode}
      }`;
      }
      /** @internal */
      _maybeGenHydrateDirectives() {
          var hydrateDirectivesCode = this._logic.genHydrateDirectives(this.directiveRecords);
          var hydrateDetectorsCode = this._logic.genHydrateDetectors(this.directiveRecords);
          if (!hydrateDirectivesCode && !hydrateDetectorsCode)
              return '';
          return `${this.typeName}.prototype.hydrateDirectives = function(directives) {
        ${hydrateDirectivesCode}
        ${hydrateDetectorsCode}
      }`;
      }
      /** @internal */
      _maybeGenAfterContentLifecycleCallbacks() {
          var notifications = this._logic.genContentLifecycleCallbacks(this.directiveRecords);
          if (notifications.length > 0) {
              var directiveNotifications = notifications.join("\n");
              return `
          ${this.typeName}.prototype.afterContentLifecycleCallbacksInternal = function() {
            ${directiveNotifications}
          }
        `;
          }
          else {
              return '';
          }
      }
      /** @internal */
      _maybeGenAfterViewLifecycleCallbacks() {
          var notifications = this._logic.genViewLifecycleCallbacks(this.directiveRecords);
          if (notifications.length > 0) {
              var directiveNotifications = notifications.join("\n");
              return `
          ${this.typeName}.prototype.afterViewLifecycleCallbacksInternal = function() {
            ${directiveNotifications}
          }
        `;
          }
          else {
              return '';
          }
      }
      /** @internal */
      _genAllRecords(rs) {
          var codes = [];
          this._endOfBlockIdxs = [];
          for (let i = 0; i < rs.length; i++) {
              let code;
              let r = rs[i];
              if (r.isLifeCycleRecord()) {
                  code = this._genDirectiveLifecycle(r);
              }
              else if (r.isPipeRecord()) {
                  code = this._genPipeCheck(r);
              }
              else if (r.isConditionalSkipRecord()) {
                  code = this._genConditionalSkip(r, this._names.getLocalName(r.contextIndex));
              }
              else if (r.isUnconditionalSkipRecord()) {
                  code = this._genUnconditionalSkip(r);
              }
              else {
                  code = this._genReferenceCheck(r);
              }
              code = `
          ${this._maybeFirstInBinding(r)}
          ${code}
          ${this._maybeGenLastInDirective(r)}
          ${this._genEndOfSkipBlock(i)}
        `;
              codes.push(code);
          }
          return codes.join("\n");
      }
      /** @internal */
      _genConditionalSkip(r, condition) {
          let maybeNegate = r.mode === RecordType.SkipRecordsIf ? '!' : '';
          this._endOfBlockIdxs.push(r.fixedArgs[0] - 1);
          return `if (${maybeNegate}${condition}) {`;
      }
      /** @internal */
      _genUnconditionalSkip(r) {
          this._endOfBlockIdxs.pop();
          this._endOfBlockIdxs.push(r.fixedArgs[0] - 1);
          return `} else {`;
      }
      /** @internal */
      _genEndOfSkipBlock(protoIndex) {
          if (!ListWrapper.isEmpty(this._endOfBlockIdxs)) {
              let endOfBlock = ListWrapper.last(this._endOfBlockIdxs);
              if (protoIndex === endOfBlock) {
                  this._endOfBlockIdxs.pop();
                  return '}';
              }
          }
          return '';
      }
      /** @internal */
      _genDirectiveLifecycle(r) {
          if (r.name === "DoCheck") {
              return this._genOnCheck(r);
          }
          else if (r.name === "OnInit") {
              return this._genOnInit(r);
          }
          else if (r.name === "OnChanges") {
              return this._genOnChange(r);
          }
          else {
              throw new BaseException(`Unknown lifecycle event '${r.name}'`);
          }
      }
      /** @internal */
      _genPipeCheck(r) {
          var context = this._names.getLocalName(r.contextIndex);
          var argString = r.args.map((arg) => this._names.getLocalName(arg)).join(", ");
          var oldValue = this._names.getFieldName(r.selfIndex);
          var newValue = this._names.getLocalName(r.selfIndex);
          var pipe = this._names.getPipeName(r.selfIndex);
          var pipeName = r.name;
          var init = `
        if (${pipe} === ${this.changeDetectionUtilVarName}.uninitialized) {
          ${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeName}');
        }
      `;
          var read = `${newValue} = ${pipe}.pipe.transform(${context}, [${argString}]);`;
          var contexOrArgCheck = r.args.map((a) => this._names.getChangeName(a));
          contexOrArgCheck.push(this._names.getChangeName(r.contextIndex));
          var condition = `!${pipe}.pure || (${contexOrArgCheck.join(" || ")})`;
          var check = `
        if (${this.changeDetectionUtilVarName}.looseNotIdentical(${oldValue}, ${newValue})) {
          ${newValue} = ${this.changeDetectionUtilVarName}.unwrapValue(${newValue})
          ${this._genChangeMarker(r)}
          ${this._genUpdateDirectiveOrElement(r)}
          ${this._genAddToChanges(r)}
          ${oldValue} = ${newValue};
        }
      `;
          var genCode = r.shouldBeChecked() ? `${read}${check}` : read;
          if (r.isUsedByOtherRecord()) {
              return `${init} if (${condition}) { ${genCode} } else { ${newValue} = ${oldValue}; }`;
          }
          else {
              return `${init} if (${condition}) { ${genCode} }`;
          }
      }
      /** @internal */
      _genReferenceCheck(r) {
          var oldValue = this._names.getFieldName(r.selfIndex);
          var newValue = this._names.getLocalName(r.selfIndex);
          var read = `
        ${this._logic.genPropertyBindingEvalValue(r)}
      `;
          var check = `
        if (${this.changeDetectionUtilVarName}.looseNotIdentical(${oldValue}, ${newValue})) {
          ${this._genChangeMarker(r)}
          ${this._genUpdateDirectiveOrElement(r)}
          ${this._genAddToChanges(r)}
          ${oldValue} = ${newValue};
        }
      `;
          var genCode = r.shouldBeChecked() ? `${read}${check}` : read;
          if (r.isPureFunction()) {
              var condition = r.args.map((a) => this._names.getChangeName(a)).join(" || ");
              if (r.isUsedByOtherRecord()) {
                  return `if (${condition}) { ${genCode} } else { ${newValue} = ${oldValue}; }`;
              }
              else {
                  return `if (${condition}) { ${genCode} }`;
              }
          }
          else {
              return genCode;
          }
      }
      /** @internal */
      _genChangeMarker(r) {
          return r.argumentToPureFunction ? `${this._names.getChangeName(r.selfIndex)} = true` : ``;
      }
      /** @internal */
      _genUpdateDirectiveOrElement(r) {
          if (!r.lastInBinding)
              return "";
          var newValue = this._names.getLocalName(r.selfIndex);
          var oldValue = this._names.getFieldName(r.selfIndex);
          var notifyDebug = this.genConfig.logBindingUpdate ? `this.logBindingUpdate(${newValue});` : "";
          var br = r.bindingRecord;
          if (br.target.isDirective()) {
              var directiveProperty = `${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.target.name}`;
              return `
          ${this._genThrowOnChangeCheck(oldValue, newValue)}
          ${directiveProperty} = ${newValue};
          ${notifyDebug}
          ${IS_CHANGED_LOCAL} = true;
        `;
          }
          else {
              return `
          ${this._genThrowOnChangeCheck(oldValue, newValue)}
          this.notifyDispatcher(${newValue});
          ${notifyDebug}
        `;
          }
      }
      /** @internal */
      _genThrowOnChangeCheck(oldValue, newValue) {
          if (assertionsEnabled()) {
              return `
          if(throwOnChange) {
            this.throwOnChangeError(${oldValue}, ${newValue});
          }
          `;
          }
          else {
              return '';
          }
      }
      /** @internal */
      _genAddToChanges(r) {
          var newValue = this._names.getLocalName(r.selfIndex);
          var oldValue = this._names.getFieldName(r.selfIndex);
          if (!r.bindingRecord.callOnChanges())
              return "";
          return `${CHANGES_LOCAL} = this.addChange(${CHANGES_LOCAL}, ${oldValue}, ${newValue});`;
      }
      /** @internal */
      _maybeFirstInBinding(r) {
          var prev = ChangeDetectionUtil.protoByIndex(this.records, r.selfIndex - 1);
          var firstInBinding = isBlank(prev) || prev.bindingRecord !== r.bindingRecord;
          return firstInBinding && !r.bindingRecord.isDirectiveLifecycle() ?
              `${this._names.getPropertyBindingIndex()} = ${r.propertyBindingIndex};` :
              '';
      }
      /** @internal */
      _maybeGenLastInDirective(r) {
          if (!r.lastInDirective)
              return "";
          return `
        ${CHANGES_LOCAL} = null;
        ${this._genNotifyOnPushDetectors(r)}
        ${IS_CHANGED_LOCAL} = false;
      `;
      }
      /** @internal */
      _genOnCheck(r) {
          var br = r.bindingRecord;
          return `if (!throwOnChange) ${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.doCheck();`;
      }
      /** @internal */
      _genOnInit(r) {
          var br = r.bindingRecord;
          return `if (!throwOnChange && ${this._names.getStateName()} === ${this.changeDetectorStateVarName}.NeverChecked) ${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.onInit();`;
      }
      /** @internal */
      _genOnChange(r) {
          var br = r.bindingRecord;
          return `if (!throwOnChange && ${CHANGES_LOCAL}) ${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.onChanges(${CHANGES_LOCAL});`;
      }
      /** @internal */
      _genNotifyOnPushDetectors(r) {
          var br = r.bindingRecord;
          if (!r.lastInDirective || br.isDefaultChangeDetection())
              return "";
          var retVal = `
        if(${IS_CHANGED_LOCAL}) {
          ${this._names.getDetectorName(br.directiveRecord.directiveIndex)}.markAsCheckOnce();
        }
      `;
          return retVal;
      }
  }
  //# sourceMappingURL=change_detection_jit_generator.js.map