/**
 * A template class that supports advanced functionality like:
 *
 * - Auto-filling arrays using templates and sub-templates
 * - Conditional processing with basic comparison operators
 * - Basic math function support
 * - Execute arbitrary inline code with special built-in template variables
 * - Custom member functions
 * - Many special tags and built-in operators that aren't defined as part of the API, but are
 * supported in the templates that can be created
 *
 * XTemplate provides the templating mechanism built into {@link Ext.view.View}.
 *
 * The {@link Ext.Template} describes the acceptable parameters to pass to the constructor.
 * The following examples demonstrate all of the supported features.
 *
 * # Sample Data
 *
 * This is the data object used for reference in each code example:
 *
 *     var data = {
 *         name: 'Don Griffin',
 *         title: 'Senior Technomage',
 *         company: 'Sencha Inc.',
 *         drinks: ['Coffee', 'Water', 'More Coffee'],
 *         kids: [
 *             { name: 'Aubrey',  age: 17 },
 *             { name: 'Joshua',  age: 13 },
 *             { name: 'Cale',    age: 10 },
 *             { name: 'Nikol',   age: 5 },
 *             { name: 'Solomon', age: 0 }
 *         ]
 *     };
 *
 * # Auto filling of arrays
 *
 * The **tpl** tag and the **for** operator are used to process the provided data object:
 *
 * - If the value specified in for is an array, it will auto-fill, repeating the template block
 * inside the tpl tag for each item in the array.
 * - If for="." is specified, the data object provided is examined.
 * - If between="..." is specified, the provided value will be inserted between the items.
 *   This is also supported in the "foreach" looping template.
 * - While processing an array, the special variable {#} will provide the current array index + 1
 * (starts at 1, not 0).
 *
 * Examples:
 *
 *     // loop through array at root node
 *     <tpl for=".">...</tpl>
 *
 *     // loop through array at foo node
 *     <tpl for="foo">...</tpl>
 *
 *     // loop through array at foo.bar node
 *     <tpl for="foo.bar">...</tpl>
 *
 *     // loop through array at root node and insert ',' between each item
 *     <tpl for="." between=",">...</tpl>
 *
 * Using the sample data above:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Kids: ',
 *         '<tpl for=".">',       // process the data.kids node
 *             '<p>{#}. {name}</p>',  // use current array index to autonumber
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
 *
 * An example illustrating how the **for** property can be leveraged to access specified members
 * of the provided data object to populate the template:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Title: {title}</p>',
 *         '<p>Company: {company}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',     // interrogate the kids property within the data
 *             '<p>{name}</p>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);  // pass the root node of the data object
 *
 * Flat arrays that contain values (and not objects) can be auto-rendered using the special
 * **`{.}`** variable inside a loop. This variable will represent the value of the array
 * at the current index:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>{name}\'s favorite beverages:</p>',
 *         '<tpl for="drinks">',
 *             '<div> - {.}</div>',
 *         '</tpl>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * When processing a sub-template, for example while looping through a child array, you can access
 * the parent object's members via the **parent** object:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="age &gt; 1">',
 *                 '<p>{name}</p>',
 *                 '<p>Dad: {parent.name}</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *     
 * The **foreach** operator is used to loop over an object's properties.  The following
 * example demonstrates looping over the main data object's properties:
 * 
 *     var tpl = new Ext.XTemplate(
 *         '<dl>',
 *             '<tpl foreach=".">',
 *                 // the special **`{$}`** variable contains the property name
 *                 '<dt>{$}</dt>',
 *                 // within the loop, the **`{.}`** variable is set to the property value
 *                 '<dd>{.}</dd>',
 *             '</tpl>',
 *         '</dl>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * # Conditional processing with basic comparison operators
 *
 * The **tpl** tag and the **if** operator are used to provide conditional checks for deciding
 * whether or not to render specific parts of the template.
 *
 * Using the sample data above:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="age &gt; 1">',
 *                 '<p>{name}</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * More advanced conditionals are also supported:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<p>{name} is a ',
 *             '<tpl if="age &gt;= 13">',
 *                 '<p>teenager</p>',
 *             '<tpl elseif="age &gt;= 2">',
 *                 '<p>kid</p>',
 *             '<tpl else>',
 *                 '<p>baby</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<p>{name} is a ',
 *             '<tpl switch="name">',
 *                 '<tpl case="Aubrey" case="Nikol">',
 *                     '<p>girl</p>',
 *                 '<tpl default>',
 *                     '<p>boy</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *
 * A `break` is implied between each case and default, however, multiple cases can be listed
 * in a single &lt;tpl&gt; tag.
 *
 * # Using double quotes
 *
 * Examples:
 *
 *     var tpl = new Ext.XTemplate(
 *         "<tpl if='age &gt; 1 && age &lt; 10'>Child</tpl>",
 *         "<tpl if='age &gt;= 10 && age &lt; 18'>Teenager</tpl>",
 *         "<tpl if='this.isGirl(name)'>...</tpl>",
 *         '<tpl if="id == \'download\'">...</tpl>',
 *         "<tpl if='needsIcon'><img src='{icon}' class='{iconCls}'/></tpl>",
 *         "<tpl if='name == \"Don\"'>Hello</tpl>"
 *     );
 *
 * # Basic math support
 *
 * The following basic math operators may be applied directly on numeric data values:
 *
 *     + - * /
 *
 * For example:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="age &gt; 1">',  // <-- Note that the > is encoded
 *                 '<p>{#}: {name}</p>',  // <-- Auto-number each item
 *                 '<p>In 5 Years: {age+5}</p>',  // <-- Basic math
 *                 '<p>Dad: {parent.name}</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * # Execute arbitrary inline code with special built-in template variables
 *
 * Anything between `{[ ... ]}` is considered code to be executed in the scope of the template.
 * The expression is evaluated and the result is included in the generated result. There are
 * some special variables available in that code:
 *
 * - **out**: The output array into which the template is being appended (using `push` to later
 *   `join`).
 * - **values**: The values in the current scope. If you are using scope changing sub-templates,
 *   you can change what values is.
 * - **parent**: The scope (values) of the ancestor template.
 * - **xindex**: If you are in a "for" or "foreach" looping template, the index of the loop you
 *  are in (1-based).
 * - **xcount**: If you are in a "for" looping template, the total length of the array you are
 *  looping.
 * - **xkey**: If you are in a "foreach" looping template, the key of the current property
 * being examined.
 *
 * This example demonstrates basic row striping using an inline code block and the xindex variable:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
 *             '{name}',
 *             '</div>',
 *         '</tpl></p>'
 *      );
 *
 * Any code contained in "verbatim" blocks (using "{% ... %}") will be inserted directly in
 * the generated code for the template. These blocks are not included in the output. This
 * can be used for simple things like break/continue in a loop, or control structures or
 * method calls (when they don't produce output). The `this` references the template instance.
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '{% if (xindex % 2 === 0) continue; %}',
 *             '{name}',
 *             '{% if (xindex > 100) break; %}',
 *             '</div>',
 *         '</tpl></p>'
 *      );
 *
 * # Template member functions
 *
 * One or more member functions can be specified in a configuration object passed into the
 * XTemplate constructor for more complex processing:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="this.isGirl(name)">',
 *                 '<p>Girl: {name} - {age}</p>',
 *             '<tpl else>',
 *                 '<p>Boy: {name} - {age}</p>',
 *             '</tpl>',
 *             '<tpl if="this.isBaby(age)">',
 *                 '<p>{name} is a baby!</p>',
 *             '</tpl>',
 *         '</tpl></p>',
 *         {
 *             // XTemplate configuration:
 *             disableFormats: true,
 *             // member functions:
 *             isGirl: function(name){
 *                return name == 'Aubrey' || name == 'Nikol';
 *             },
 *             isBaby: function(age){
 *                return age < 1;
 *             }
 *         }
 *     );
 *     tpl.overwrite(panel.body, data);
 */
Ext.define('Ext.XTemplate', {
    extend: 'Ext.Template',
 
    requires: [
        'Ext.util.XTemplateCompiler'
    ],
 
    isXTemplate: true,
 
    /**
     * @private
     */
    emptyObj: {},
 
    /**
     * @cfg {Boolean} compiled
     * Only applies to {@link Ext.Template}, XTemplates are compiled automatically on the
     * first call to {@link #apply} or {@link #applyOut}.
     * @hide
     */
 
    /**
     * @cfg {String/Array} definitions
     * Optional. A statement, or array of statements which set up `var`s which may then
     * be accessed within the scope of the generated function.
     * 
     *     var data = {
     *         name: 'Don Griffin',
     *         isWizard: true,
     *         title: 'Senior Technomage',
     *         company: 'Sencha Inc.'
     *     };
     *     
     *     var tpl = new Ext.XTemplate('{[values.isWizard ? wizard : notSoWizard]}' +
     *         ' {name}', {
     *         definitions: 'var wizard = "Wizard", notSoWizard = "Townsperson";'
     *     });
     *     
     *     console.log(tpl.apply(data));
     *     // LOGS: Wizard Don Griffin
     */
 
    /**
     * @property {Function} fn
     * The function that applies this template. This is created on first use of the
     * template (calls to `apply` or `applyOut`).
     * @private
     * @readonly
     */
    fn: null,
 
    /**
     * @cfg {Boolean} [strict=false]
     * Expressions in templates that traverse "dot paths" and fail (due to `null` at some
     * stage) have always been expanded as empty strings. This is convenient in most cases
     * but doing so can also mask errors in the template. Setting this to `true` changes
     * this default so that any expression errors will be thrown as exceptions.
     */
    strict: false,
 
    apply: function(values, parent, xindex, xcount) {
        var buffer = this.applyOut(values, [], parent, xindex, xcount);
 
        // If only one token, return it as its uncoerced data type.
        // This will allow things like ObjectTemplate to use
        // formatters on non-string values.
        return buffer.length === 1 ? buffer[0] : buffer.join('');
    },
 
    applyOut: function(values, out, parent, xindex, xcount) {
        var me = this,
            compiler;
 
        if (!me.fn) {
            compiler = new Ext.util.XTemplateCompiler({
                useFormat: me.disableFormats !== true,
                definitions: me.definitions,
                strict: me.strict
            });
 
            me.fn = compiler.compile(me.html);
        }
 
        // xindex is 1-based, so 0 is impossible
        xindex = xindex || 1;
 
        // likewise, this tpl exists in the parent, so xcount==0 is not possible
        xcount = xcount || 1;
 
        if (me.strict) {
            me.fn(out, values, parent || me.emptyObj, xindex, xcount);
        }
        else {
            try {
                me.fn(out, values, parent || me.emptyObj, xindex, xcount);
            }
            catch (e) {
                //<debug>
                Ext.log.warn('XTemplate evaluation exception: ' + e.message);
                //</debug>
            }
        }
 
        return out;
    },
 
    /**
     * Does nothing. XTemplates are compiled automatically, so this function simply returns this.
     * @return {Ext.XTemplate} this
     */
    compile: function() {
        return this;
    },
 
    statics: {
        get: function(config, source, defaultTpl) {
            var ret = config;
 
            if (config == null) {
                if (source && defaultTpl) {
                    ret = this.getTpl(source, defaultTpl);
                }
            }
            else if ((config || config === '') && !config.isTemplate) {
                ret = new this(config);
            }
 
            return ret;
        },
 
        /**
         * Gets an `XTemplate` from an object (an instance of an {@link Ext#define}'d class).
         * Many times, templates are configured high in the class hierarchy and are to be
         * shared by all classes that derive from that base. To further complicate matters,
         * these templates are seldom actual instances but are rather configurations. For
         * example:
         *
         *      Ext.define('MyApp.Class', {
         *          extraCls: 'extra-class',
         *
         *          someTpl: [
         *              '<div class="{%this.emitClass(out)%}"></div>',
         *          {
         *              // Member fn - outputs the owing class's extra CSS class
         *              emitClass: function(out) {
         *                  out.push(this.owner.extraCls);
         *              }
         *          }]
         *      });
         *
         * The goal being to share that template definition with all instances and even
         * instances of derived classes, until `someTpl` is overridden. This method will
         * "upgrade" these configurations to be real `XTemplate` instances *in place* (to
         * avoid creating one instance per object).
         *
         * The resulting XTemplate will have an `owner` reference injected which refers back
         * to the owning object whether that is an object which has an *own instance*, or a
         * class prototype. Through this link, XTemplate member functions will be able to access
         * prototype properties of its owning class.
         *
         * @param {Object} instance The object from which to get the `XTemplate` (must be
         * an instance of an {@link Ext#define}'d class).
         * @param {String} name The name of the property by which to get the `XTemplate`.
         * @return {Ext.XTemplate} The `XTemplate` instance or null if not found.
         * @protected
         * @static
         */
        getTpl: function(instance, name) {
            var tpl = instance[name], // go for it! 99% of the time we will get it!
                owner;
 
            if (tpl) {
                // tpl is just a configuration (not an instance)
                if (!tpl.isTemplate) {
                    // create the template instance from the configuration:
                    tpl = Ext.XTemplate.get(tpl);
                }
 
                if (!tpl.owner) {
                    // and replace the reference with the new instance:
                    if (instance.hasOwnProperty(name)) { // the tpl is on the instance
                        owner = instance;
                    }
                    else {
                        // must be somewhere in the prototype chain
                        /* eslint-disable-next-line max-len, no-empty */
                        for (owner = instance.self.prototype; owner && !owner.hasOwnProperty(name); owner = owner.superclass) {
                        }
                    }
 
                    owner[name] = tpl;
                    tpl.owner = owner;
                }
            }
            // else !tpl (no such tpl) or the tpl is an instance already... either way, tpl
            // is ready to return
 
            return tpl || null;
        }
    }
});