import ngModule from '../module';
import moduleImport from 'module-import';

factory.$inject = ['$http', '$window', '$ocLazyLoad', '$translatePartialLoader', '$translate', '$q'];
export function factory($http, $window, $ocLazyLoad, $translatePartialLoader, $translate, $q) {
    /**
     * Used to load application modules lazily.
     */
    class ModuleLoader {
        constructor() {
            this.loaded = {};
            this.imports = {};
            this.moduleImport = moduleImport;
            this.modelInfo = $http.get(`Schemas/modelInfo`)
                .then(json => {
                    this.onModelInfoReady(json);
                    this.modelInfo = true;
                });
        }

        /**
         * Loads the passed module and it's dependencies. Loading a module
         * implies load the webpack chunk, translations, recursively load
         * module dependencies and finally register all of them into Angular.
         *
         * @param {String} mod The module name to load
         * @return {Promise} Will be resolved when loaded, when module is
         * already loaded it returns a resolved promise
         */
        load(mod) {
            let mods = [];
            return this.loadRec(mod, mods);
        }

        loadRec(mod, mods) {
            let loaded = this.loaded[mod];

            if (loaded === true || mods.indexOf(mod) != -1)
                return $q.resolve(true);
            if (loaded instanceof $q)
                return loaded;

            let moduleConf = $window.routes.find(i => i && i.module == mod);
            if (!moduleConf)
                return $q.reject(new Error(`Module not found: ${mod}`));

            let promises = [];

            if (this.modelInfo instanceof $q)
                promises.push(this.modelInfo);

            $translatePartialLoader.addPart(mod);
            promises.push($translate.refresh());

            let modImport = this.imports[mod];

            if (!modImport) {
                modImport = this.imports[mod] = this.moduleImport(mod)
                    .then(() => this.imports[mod] = true);
            }
            if (modImport && modImport.then)
                promises.push(modImport);

            let deps = moduleConf.dependencies;

            if (deps) {
                mods.push(mod);
                for (let dep of deps)
                    promises.push(this.loadRec(dep, mods));
                mods.pop();
            }

            this.loaded[mod] = $q.all(promises)
                .then(() => $ocLazyLoad.load({name: mod}))
                .then(() => this.loaded[mod] = true);
            return this.loaded[mod];
        }

        onModelInfoReady(json) {
            let entities = json.data;
            for (let entity in entities) {
                let fields = entities[entity].validations;
                for (let field in fields) {
                    let validations = fields[field];
                    for (let validation of validations)
                        this.parseValidation(validation);
                }
            }

            Object.assign($window.validations, json.data);
        }

        parseValidation(val) {
            switch (val.validation) {
            case 'custom':
                // TODO: Don't use eval() because it's "evil".
                // How to do the same without eval?
                val.bindedFunction = eval(`(${val.bindedFunction})`);
                break;
            case 'format':
                val.with = new RegExp(val.with);
                break;
            }
        }
    }

    return new ModuleLoader();
}
ngModule.factory('vnModuleLoader', factory);