/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { Cacher } from "./storage";
import SqrlErr from "./err";
import { compileScope, compileScopeIntoFunction } from "./compile-string";
import { hasOwnProp } from "./utils";
import { errWithBlocksOrFilters, asyncArrLoop, asyncObjLoop, XMLEscape, } from "./container-utils";
/* END TYPES */
const templates = new Cacher({});
/* ASYNC LOOP FNs */
const helpers = new Cacher({
    each: function (content, blocks) {
        let res = '';
        const arr = content.params[0];
        errWithBlocksOrFilters('each', blocks, false);
        if (content.async) {
            return new Promise(function (resolve) {
                asyncArrLoop(arr, 0, content.exec, res, resolve);
            });
        }
        else {
            for (let i = 0; i < arr.length; i++) {
                res += content.exec(arr[i], i);
            }
            return res;
        }
    },
    foreach: function (content, blocks) {
        const obj = content.params[0];
        errWithBlocksOrFilters('foreach', blocks, false);
        if (content.async) {
            return new Promise(function (resolve) {
                asyncObjLoop(obj, Object.keys(obj), 0, content.exec, '', resolve);
            });
        }
        else {
            let res = '';
            for (const key in obj) {
                if (!hasOwnProp(obj, key))
                    continue;
                res += content.exec(key, obj[key]); // todo: check on order
            }
            return res;
        }
    },
    include: function (content, blocks, config) {
        errWithBlocksOrFilters('include', blocks, false);
        const template = config.storage.templates.get(content.params[0]);
        if (!template) {
            throw SqrlErr('Could not fetch template "' + content.params[0] + '"');
        }
        return template(content.params[1], config);
    },
    extends: function (content, blocks, config) {
        const data = content.params[1] || {};
        data.content = content.exec();
        for (let i = 0; i < blocks.length; i++) {
            const currentBlock = blocks[i];
            data[currentBlock.name] = currentBlock.exec();
        }
        const template = config.storage.templates.get(content.params[0]);
        if (!template) {
            throw SqrlErr('Could not fetch template "' + content.params[0] + '"');
        }
        return template(data, config);
    },
    useScope: function (content, blocks) {
        errWithBlocksOrFilters('useScope', blocks, false);
        return content.exec(content.params[0]);
    },
});
const nativeHelpers = new Cacher({
    if: function (buffer, env) {
        errWithBlocksOrFilters('if', false, buffer.f, true);
        let returnStr = 'if(' + buffer.p + '){' + compileScope(buffer.d, env) + '}';
        if (buffer.b) {
            for (let i = 0; i < buffer.b.length; i++) {
                const currentBlock = buffer.b[i];
                if (currentBlock.n === 'else') {
                    returnStr += 'else{' + compileScope(currentBlock.d, env) + '}';
                }
                else if (currentBlock.n === 'elif') {
                    returnStr +=
                        'else if(' +
                            currentBlock.p +
                            '){' +
                            compileScope(currentBlock.d, env) +
                            '}';
                }
            }
        }
        return returnStr;
    },
    try: function (buffer, env) {
        errWithBlocksOrFilters('try', false, buffer.f, true);
        if (!buffer.b || buffer.b.length !== 1 || buffer.b[0].n !== 'catch') {
            throw SqrlErr("native helper 'try' only accepts 1 block, 'catch'");
        }
        let returnStr = 'try{' + compileScope(buffer.d, env) + '}';
        const currentBlock = buffer.b[0];
        returnStr +=
            'catch' +
                (currentBlock.res ? '(' + currentBlock.res + ')' : '') +
                '{' +
                compileScope(currentBlock.d, env) +
                '}';
        return returnStr;
    },
    block: function (buffer, env) {
        errWithBlocksOrFilters('block', buffer.b, buffer.f, true);
        const returnStr = 'if(!' +
            env.varName +
            '[' +
            buffer.p +
            ']){tR+=(' +
            compileScopeIntoFunction(buffer.d, '', env) +
            ')()}else{tR+=' +
            env.varName +
            '[' +
            buffer.p +
            ']}';
        return returnStr;
    },
});
const filters = new Cacher({ e: XMLEscape });
export { templates, helpers, nativeHelpers, filters };
