/**
 * @category Core
 * @subcategory Application
 */
class Controller {

    static _template = null;
    static _contents = [];
    static _currentContent = null;

    /**
     * 
     */
    constructor() {  }

    /**
     * 
     */
    destructor() {}
   
    /**
     * @async
     * @param {*} name 
     * @param {*} args 
     * @param {*} data 
     * @returns {View}
     */
    loadView(name, args, data) {
        args = args || {};
        args.name = name+'.html';
        args.className = name.split('/').pop();
        args.onContentLoaded = args.onContentLoaded || this.appendHTML;
        args.path = app.config.router.viewsPath;

        let request = app.Assets.loadHtml(args);
        
        return request.then(args.onContentLoaded.bind(this, args, data));
    }

    /**
     * 
     * @param {*} args 
     * @param {*} data 
     * @param {*} html 
     * @returns {View}
     */
    appendHTML(args,data, html) {
        
        if(!args.target) {
            args.target = document.body;
        } else {
            if(typeof args.target == 'string') {
                let selector = null;
                let content = document.body;

                let crumbs = args.target.split('@')
                
                if(crumbs.length > 1) {
                    let targetId = crumbs.pop()
                    content = Controller._contents.find(o => o.view._sparcId == targetId).view.el;
                }

                selector = crumbs.join('@');
                args.target = content.querySelector(selector);
            }
        }
        
        let container = document.createElement('div');
        
        container.innerHTML = Controller.processTemplate(args.name, html, data);

        let parent = args.target;
        
        parent.innerHTML = '';
        
        while(container.children.length > 0) {
            parent.appendChild(container.children[0]);
        }

        let view = new app.LoadedClasses[args.className]();
        view._className = args.name.replace('.html', '');
        
        Controller._currentContent = view;
        Controller._contents.push({view: view, dom: parent, type: 'nested'});
        view.el = parent;
        view.el.setAttribute('sparc-id', view._sparcId);
        view._controller = this;
    
        //view.DOMContentLoaded(data);

        return view;
    }

    /**
     * 
     * @param {*} content 
     */
    unloadView(content) {
        app.events.clear(content.view._sparcId);
        Controller._contents = Controller._contents.filter(o => o.view._sparcId != content.view._sparcId);
        content.view.DOMContentRemoved();
        content.view.el.remove();
    }

    /**
     * 
     * @param {*} name 
     * @param {*} args 
     */
    loadModel(name, args) { }

    /**
     * 
     * @param {*} id 
     * @returns {View}
     */
    static getContentById(id) { return Controller._contents.find(o => o.view._sparcId == id); }

    /**
     * 
     * @param {*} name 
     * @param {*} html 
     * @param {*} templateData 
     * @returns {string}
     */
    static processTemplate(name, html, templateData) {  
        
        // Define a function inside of which templateData keys will become local variables
        // AND where templateData values will become thos variable values ;-)
        // Drawback is that complex epression
        function evalaluateTemplate(tpl, params) {  
            var interpretor = Function(...Object.keys(params), 'return('+tpl+');' ); 
            return interpretor(...Object.values(params));
        }

        // Define a function inside of which a string of a variable name becomes that variable
        // and where we check tht the corresponding (evaluable) expression in templateData is defined
        function checkExpression(varname, params) { 
            var code = ' try{var t=typeof('+varname+');} catch(e){ t="badexpr"; } return(t)';
            var interpretor = Function(...Object.keys(params), code );
            return interpretor(...Object.values(params));
        }

        // Preflight check to avoid template crashes on undefined expressions 
        const rgxp = /\$\{(.*?)\}/gm;
        let m = null;

        templateData = templateData || {};

        while ((m = rgxp.exec(html)) !== null) {        
            let safeType = checkExpression(m[1],templateData);
            
            if(safeType == 'undefined') { 
                // variable undefined in templateData: add an empty one
                console.warn(`In template ${name}, ${m[1]} is not defined !`);
                templateData[m[1]]='';
            } else if(safeType == 'badexpr') { 
                // unevaluable expression : remove it from template
                console.warn(`In template ${name}, Bad expression: ${m[1]}`);
                var escsearch = m[1].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
                var replrexp = new RegExp('\\$\\{'+escsearch+'\\}','gm')
                html = html.replace(replrexp,'');
            }

        }
        
        // Now that all remaining expressions are OK, evaluate the template via backtits !
        return(evalaluateTemplate('`' + html + '`', templateData));
    }

}

app.registerClass("Controller", Controller);