'use strict';var lang_1 = require('angular2/src/core/facade/lang');
var exceptions_1 = require('angular2/src/core/facade/exceptions');
var abstract_change_detector_1 = require('./abstract_change_detector');
var change_detection_util_1 = require('./change_detection_util');
var codegen_name_util_1 = require('./codegen_name_util');
var codegen_logic_util_1 = require('./codegen_logic_util');
var codegen_facade_1 = require('./codegen_facade');
var proto_change_detector_1 = require('./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.
*/
var IS_CHANGED_LOCAL = "isChanged";
var CHANGES_LOCAL = "changes";
var ChangeDetectorJITGenerator = (function () {
    function ChangeDetectorJITGenerator(definition, changeDetectionUtilVarName, abstractChangeDetectorVarName) {
        this.changeDetectionUtilVarName = changeDetectionUtilVarName;
        this.abstractChangeDetectorVarName = abstractChangeDetectorVarName;
        var propertyBindingRecords = proto_change_detector_1.createPropertyRecords(definition);
        var eventBindingRecords = proto_change_detector_1.createEventRecords(definition);
        var propertyBindingTargets = definition.bindingRecords.map(function (b) { return 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 codegen_name_util_1.CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords, this.changeDetectionUtilVarName);
        this._logic = new codegen_logic_util_1.CodegenLogicUtil(this._names, this.changeDetectionUtilVarName, this.changeDetectionStrategy);
        this.typeName = codegen_name_util_1.sanitizeName("ChangeDetector_" + this.id);
    }
    ChangeDetectorJITGenerator.prototype.generate = function () {
        var factorySource = "\n      " + this.generateSource() + "\n      return function(dispatcher) {\n        return new " + this.typeName + "(dispatcher);\n      }\n    ";
        return new Function(this.abstractChangeDetectorVarName, this.changeDetectionUtilVarName, factorySource)(abstract_change_detector_1.AbstractChangeDetector, change_detection_util_1.ChangeDetectionUtil);
    };
    ChangeDetectorJITGenerator.prototype.generateSource = function () {
        var _this = this;
        return "\n      var " + this.typeName + " = function " + this.typeName + "(dispatcher) {\n        " + this.abstractChangeDetectorVarName + ".call(\n            this, " + JSON.stringify(this.id) + ", dispatcher, " + this.records.length + ",\n            " + this.typeName + ".gen_propertyBindingTargets, " + this.typeName + ".gen_directiveIndices,\n            " + codegen_facade_1.codify(this.changeDetectionStrategy) + ");\n        this.dehydrateDirectives(false);\n      }\n\n      " + this.typeName + ".prototype = Object.create(" + this.abstractChangeDetectorVarName + ".prototype);\n\n      " + this.typeName + ".prototype.detectChangesInRecordsInternal = function(throwOnChange) {\n        " + this._names.genInitLocals() + "\n        var " + IS_CHANGED_LOCAL + " = false;\n        var " + CHANGES_LOCAL + " = null;\n\n        " + this.records.map(function (r) { return _this._genRecord(r); }).join("\n") + "\n      }\n\n      " + this._maybeGenHandleEventInternal() + "\n\n      " + this._genCheckNoChanges() + "\n\n      " + this._maybeGenAfterContentLifecycleCallbacks() + "\n\n      " + this._maybeGenAfterViewLifecycleCallbacks() + "\n\n      " + this._maybeGenHydrateDirectives() + "\n\n      " + this._maybeGenDehydrateDirectives() + "\n\n      " + this._genPropertyBindingTargets() + "\n\n      " + this._genDirectiveIndices() + "\n    ";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genPropertyBindingTargets = function () {
        var targets = this._logic.genPropertyBindingTargets(this.propertyBindingTargets, this.genConfig.genDebugInfo);
        return this.typeName + ".gen_propertyBindingTargets = " + targets + ";";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genDirectiveIndices = function () {
        var indices = this._logic.genDirectiveIndices(this.directiveRecords);
        return this.typeName + ".gen_directiveIndices = " + indices + ";";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._maybeGenHandleEventInternal = function () {
        var _this = this;
        if (this.eventBindings.length > 0) {
            var handlers = this.eventBindings.map(function (eb) { return _this._genEventBinding(eb); }).join("\n");
            return "\n        " + this.typeName + ".prototype.handleEventInternal = function(eventName, elIndex, locals) {\n          var " + this._names.getPreventDefaultAccesor() + " = false;\n          " + this._names.genInitEventLocals() + "\n          " + handlers + "\n          return " + this._names.getPreventDefaultAccesor() + ";\n        }\n      ";
        }
        else {
            return '';
        }
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genEventBinding = function (eb) {
        var _this = this;
        var recs = eb.records.map(function (r) { return _this._genEventBindingEval(eb, r); }).join("\n");
        return "\n    if (eventName === \"" + eb.eventName + "\" && elIndex === " + eb.elIndex + ") {\n      " + recs + "\n    }";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genEventBindingEval = function (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 */
    ChangeDetectorJITGenerator.prototype._genMarkPathToRootAsCheckOnce = function (r) {
        var br = r.bindingRecord;
        if (br.isDefaultChangeDetection()) {
            return "";
        }
        else {
            return this._names.getDetectorName(br.directiveRecord.directiveIndex) + ".markPathToRootAsCheckOnce();";
        }
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genUpdatePreventDefault = function (eb, r) {
        var local = this._names.getEventLocalName(eb, r.selfIndex);
        return "if (" + local + " === false) { " + this._names.getPreventDefaultAccesor() + " = true};";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._maybeGenDehydrateDirectives = function () {
        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) {\n        " + destroyPipesCode + "\n        " + dehydrateFieldsCode + "\n    }";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._maybeGenHydrateDirectives = function () {
        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) {\n      " + hydrateDirectivesCode + "\n      " + hydrateDetectorsCode + "\n    }";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._maybeGenAfterContentLifecycleCallbacks = function () {
        var notifications = this._logic.genContentLifecycleCallbacks(this.directiveRecords);
        if (notifications.length > 0) {
            var directiveNotifications = notifications.join("\n");
            return "\n        " + this.typeName + ".prototype.afterContentLifecycleCallbacksInternal = function() {\n          " + directiveNotifications + "\n        }\n      ";
        }
        else {
            return '';
        }
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._maybeGenAfterViewLifecycleCallbacks = function () {
        var notifications = this._logic.genViewLifecycleCallbacks(this.directiveRecords);
        if (notifications.length > 0) {
            var directiveNotifications = notifications.join("\n");
            return "\n        " + this.typeName + ".prototype.afterViewLifecycleCallbacksInternal = function() {\n          " + directiveNotifications + "\n        }\n      ";
        }
        else {
            return '';
        }
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genRecord = function (r) {
        var rec;
        if (r.isLifeCycleRecord()) {
            rec = this._genDirectiveLifecycle(r);
        }
        else if (r.isPipeRecord()) {
            rec = this._genPipeCheck(r);
        }
        else {
            rec = this._genReferenceCheck(r);
        }
        return "\n      " + this._maybeFirstInBinding(r) + "\n      " + rec + "\n      " + this._maybeGenLastInDirective(r) + "\n    ";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genDirectiveLifecycle = function (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 exceptions_1.BaseException("Unknown lifecycle event '" + r.name + "'");
        }
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genPipeCheck = function (r) {
        var _this = this;
        var context = this._names.getLocalName(r.contextIndex);
        var argString = r.args.map(function (arg) { return _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 = "\n      if (" + pipe + " === " + this.changeDetectionUtilVarName + ".uninitialized) {\n        " + pipe + " = " + this._names.getPipesAccessorName() + ".get('" + pipeName + "');\n      }\n    ";
        var read = newValue + " = " + pipe + ".pipe.transform(" + context + ", [" + argString + "]);";
        var contexOrArgCheck = r.args.map(function (a) { return _this._names.getChangeName(a); });
        contexOrArgCheck.push(this._names.getChangeName(r.contextIndex));
        var condition = "!" + pipe + ".pure || (" + contexOrArgCheck.join(" || ") + ")";
        var check = "\n      if (" + this.changeDetectionUtilVarName + ".looseNotIdentical(" + oldValue + ", " + newValue + ")) {\n        " + newValue + " = " + this.changeDetectionUtilVarName + ".unwrapValue(" + newValue + ")\n        " + this._genChangeMarker(r) + "\n        " + this._genUpdateDirectiveOrElement(r) + "\n        " + this._genAddToChanges(r) + "\n        " + oldValue + " = " + newValue + ";\n      }\n    ";
        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 */
    ChangeDetectorJITGenerator.prototype._genReferenceCheck = function (r) {
        var _this = this;
        var oldValue = this._names.getFieldName(r.selfIndex);
        var newValue = this._names.getLocalName(r.selfIndex);
        var read = "\n      " + this._logic.genPropertyBindingEvalValue(r) + "\n    ";
        var check = "\n      if (" + this.changeDetectionUtilVarName + ".looseNotIdentical(" + oldValue + ", " + newValue + ")) {\n        " + this._genChangeMarker(r) + "\n        " + this._genUpdateDirectiveOrElement(r) + "\n        " + this._genAddToChanges(r) + "\n        " + oldValue + " = " + newValue + ";\n      }\n    ";
        var genCode = r.shouldBeChecked() ? "" + read + check : read;
        if (r.isPureFunction()) {
            var condition = r.args.map(function (a) { return _this._names.getChangeName(a); }).join(" || ");
            if (r.isUsedByOtherRecord()) {
                return "if (" + condition + ") { " + genCode + " } else { " + newValue + " = " + oldValue + "; }";
            }
            else {
                return "if (" + condition + ") { " + genCode + " }";
            }
        }
        else {
            return genCode;
        }
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genChangeMarker = function (r) {
        return r.argumentToPureFunction ? this._names.getChangeName(r.selfIndex) + " = true" : "";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genUpdateDirectiveOrElement = function (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 "\n        " + this._genThrowOnChangeCheck(oldValue, newValue) + "\n        " + directiveProperty + " = " + newValue + ";\n        " + notifyDebug + "\n        " + IS_CHANGED_LOCAL + " = true;\n      ";
        }
        else {
            return "\n        " + this._genThrowOnChangeCheck(oldValue, newValue) + "\n        this.notifyDispatcher(" + newValue + ");\n        " + notifyDebug + "\n      ";
        }
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genThrowOnChangeCheck = function (oldValue, newValue) {
        if (this.genConfig.genCheckNoChanges) {
            return "\n        if(throwOnChange) {\n          this.throwOnChangeError(" + oldValue + ", " + newValue + ");\n        }\n        ";
        }
        else {
            return '';
        }
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genCheckNoChanges = function () {
        if (this.genConfig.genCheckNoChanges) {
            return this.typeName + ".prototype.checkNoChanges = function() { this.runDetectChanges(true); }";
        }
        else {
            return '';
        }
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genAddToChanges = function (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 */
    ChangeDetectorJITGenerator.prototype._maybeFirstInBinding = function (r) {
        var prev = change_detection_util_1.ChangeDetectionUtil.protoByIndex(this.records, r.selfIndex - 1);
        var firstInBindng = lang_1.isBlank(prev) || prev.bindingRecord !== r.bindingRecord;
        return firstInBindng && !r.bindingRecord.isDirectiveLifecycle() ?
            this._names.getPropertyBindingIndex() + " = " + r.propertyBindingIndex + ";" :
            '';
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._maybeGenLastInDirective = function (r) {
        if (!r.lastInDirective)
            return "";
        return "\n      " + CHANGES_LOCAL + " = null;\n      " + this._genNotifyOnPushDetectors(r) + "\n      " + IS_CHANGED_LOCAL + " = false;\n    ";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genOnCheck = function (r) {
        var br = r.bindingRecord;
        return "if (!throwOnChange) " + this._names.getDirectiveName(br.directiveRecord.directiveIndex) + ".doCheck();";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genOnInit = function (r) {
        var br = r.bindingRecord;
        return "if (!throwOnChange && !" + this._names.getAlreadyCheckedName() + ") " + this._names.getDirectiveName(br.directiveRecord.directiveIndex) + ".onInit();";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genOnChange = function (r) {
        var br = r.bindingRecord;
        return "if (!throwOnChange && " + CHANGES_LOCAL + ") " + this._names.getDirectiveName(br.directiveRecord.directiveIndex) + ".onChanges(" + CHANGES_LOCAL + ");";
    };
    /** @internal */
    ChangeDetectorJITGenerator.prototype._genNotifyOnPushDetectors = function (r) {
        var br = r.bindingRecord;
        if (!r.lastInDirective || br.isDefaultChangeDetection())
            return "";
        var retVal = "\n      if(" + IS_CHANGED_LOCAL + ") {\n        " + this._names.getDetectorName(br.directiveRecord.directiveIndex) + ".markAsCheckOnce();\n      }\n    ";
        return retVal;
    };
    return ChangeDetectorJITGenerator;
})();
exports.ChangeDetectorJITGenerator = ChangeDetectorJITGenerator;
//# sourceMappingURL=change_detection_jit_generator.js.map