/**
 * native commands for xtemplate.
 * @author yiminghe@gmail.com
 * @ignore
 */
KISSY.add("xtemplate/runtime/commands", function (S) {
    var commands;

    return commands = {
        'each': function (scopes, config) {
            var params = config.params;
            var param0 = params[0];
            var buffer = '';
            var xcount;
            // if undefined, will emit warning by compiler
            if (param0) {
                // skip array check for performance
                var opScopes = [0, 0].concat(scopes);
                if (S.isArray(param0)) {
                    xcount = param0.length;
                    for (var xindex = 0; xindex < xcount; xindex++) {
                        // two more variable scope for array looping
                        opScopes[0] = param0[xindex];
                        opScopes[1] = {
                            xcount: xcount,
                            xindex: xindex
                        };
                        buffer += config.fn(opScopes);
                    }
                } else {
                    for (var name in param0) {
                        opScopes[0] = param0[name];
                        opScopes[1] = {
                            xindex: name
                        };
                        buffer += config.fn(opScopes);
                    }
                }

            } else if (config.inverse) {
                buffer = config.inverse(scopes);
            }
            return buffer;
        },

        'with': function (scopes, config) {
            var params = config.params;
            var param0 = params[0];
            var opScopes = [0].concat(scopes);
            var buffer = '';
            if (param0) {
                // skip object check for performance
                opScopes[0] = param0;
                buffer = config.fn(opScopes);
            } else if (config.inverse) {
                buffer = config.inverse(scopes);
            }
            return buffer;
        },

        'if': function (scopes, config) {
            var params = config.params;
            var param0 = params[0];
            var buffer = '';
            if (param0) {
                if (config.fn) {
                    buffer = config.fn(scopes);
                }
            } else if (config.inverse) {
                buffer = config.inverse(scopes);
            }
            return buffer;
        },

        'set': function (scopes, config) {
            // in case scopes[0] is not object ,{{#each}}{{set }}{{/each}}
            for (var i = scopes.length - 1; i >= 0; i--) {
                if (typeof scopes[i] == 'object') {
                    S.mix(scopes[i], config.hash);
                    break;
                }
            }
            return '';
        },

        include: function (scopes, config) {
            var params = config.params;
            // allow hash to shadow parent scopes
            var extra = config.hash ? [config.hash] : [];
            scopes = extra.concat(scopes);

            if (!params || params.length != 1) {
                S.error('include must has one param');
                return '';
            }

            var myName = this.config.name;
            var subTplName = params[0];


            if (subTplName.charAt(0) == '.') {
                if (myName == 'unspecified') {
                    S.error('parent template does not have name' + ' for relative sub tpl name: ' + subTplName);
                    return '';
                }
                subTplName = S.Path.resolve(myName, '../', subTplName);
            }

            var tpl = this.config.loader.call(this, subTplName);

            config = S.merge(this.config);
            // template file name
            config.name = subTplName;
            // pass commands to sub template
            config.commands = this.config.commands;
            // share macros with parent template and sub template
            config.macros = this.config.macros;
            return this.invokeEngine(tpl, scopes, config)
        },

        'macro': function (scopes, config) {
            var params = config.params;
            var macroName = params[0];
            var params1 = params.slice(1);
            var macros = this.config.macros;
            // definition
            if (config.fn) {
                // parent template override child template
                if (!macros[macroName]) {
                    macros[macroName] = {
                        paramNames: params1,
                        fn: config.fn
                    };
                }
            } else {
                var paramValues = {};
                var macro = macros[macroName];
                if (!macro) {
                    macro = S.require(macroName);
                    if (!macro) {
                        S.error("can not find macro module:" + name);
                    }
                }
                S.each(macro.paramNames, function (p, i) {
                    paramValues[p] = params1[i];
                });
                var newScopes = scopes.concat();
                newScopes.unshift(paramValues);
                return macro.fn.call(this, newScopes);
            }
            return '';
        },

        parse: function (scopes, config) {
            // abandon parent scopes
            return commands.include.call(this, [], config);
        }
    };
});