/**
 * This class is created to manage a template against a `ViewModel`. A binding of this
 * type uses `{@link Ext.app.bind.Template}` to process the template text so see that
 * class for details on template syntax.
 *
 * The bindings to provide the data needed by the template are managed here.
 */
Ext.define('Ext.app.bind.TemplateBinding', {
    extend: 'Ext.app.bind.BaseBinding',

    requires: [
        'Ext.app.bind.Multi',
        'Ext.app.bind.Template'
    ],

    isTemplateBinding: true,

    lastValue: undefined,

    value: undefined,

    constructor: function(template, owner, callback, scope, options) {
        var me = this,
            tpl = new Ext.app.bind.Template(template),
            tokens = tpl.getTokens();

        me.callParent([ owner, callback, scope, options ]);

        me.tpl = tpl;
        me.tokens = tokens;
        tokens.$literal = true;

        // If we don't have any tokens, then we've just got a static string.
        if (!tpl.isStatic()) {
            me.multiBinding = new Ext.app.bind.Multi(tokens, owner, me.onBindData, me);
        }
        else {
            me.isStatic = true;
            me.onData(tpl.getText());
        }
    },

    destroy: function() {
        var me = this;

        Ext.destroy(me.multiBinding);
        me.tpl = me.multiBinding = null;

        me.callParent();
    },

    getFullName: function() {
        var multi = this.multiBinding;

        return this.fullName ||
               (this.fullName = '$' + (multi ? multi.getFullName() : this.callParent()));
    },

    getRawValue: function() {
        return this.value;
    },

    getTemplateScope: function() {
        return null;
    },

    isAvailable: function() {
        var multi = this.multiBinding;

        return multi ? multi.isAvailable() : false;
    },

    isDescendantOf: function() {
        return false;
    },

    isLoading: function() {
        var multi = this.multiBinding;

        return multi ? multi.isLoading() : false;
    },

    onBindData: function(data) {
        this.onData(this.tpl.apply(data, this.getTemplateScope()));
    },

    onData: function(value) {
        var me = this,
            lastValue = me.value;

        if (lastValue !== (me.value = value)) {
            me.lastValue = lastValue;
            me.schedule();
        }
    },

    react: function() {
        this.notify(this.value);
    },

    refresh: function() {
        var multi = this.multiBinding;

        if (multi) {
            multi.refresh();
        }
    },

    privates: {
        sort: function() {
            var multi = this.multiBinding;

            if (multi) {
                this.scheduler.sortItem(multi);
            }

            // Schedulable#sort === emptyFn
            // me.callParent();
        }
    }
});