var include = function(filename, localDir) {
    return {filename: filename, localDir: localDir};
};

class ModuleLoader {
    constructor(name) {
        this.loadingStack = [];
        this.startedLoading = new Set();
        this.loaded = new Set();
        this.isRunningSync = false;
        this.syncLoaded = [];
        this.cacheLoading = false;
        this.callsAfterCacheResolve = [];
        
        console.log("globalVersion = " + globalVersion);
    }

    getFullname = function (filename, localDir)
    {
        let versionParam = "";
        if (typeof globalVersion !== 'undefined') {	
            versionParam = "?v=" + globalVersion;
        }
        let localDirParam = "";
        if (typeof localDir !== 'undefined') {
            localDirParam = localDir;
        }
        return "/" + SiteDir + "script/" + localDirParam + filename + "?v=" + globalVersion;
    }

    isAllLoaded = function (includes) 
    {
        self = this;
        return includes.every((inc) => {
            return self.loaded.has(inc);
        });
    }

    load = function (fullFilename, checkQueue, sync) {  
        let self = this;

        let loadError = function()
        {
            console.error("Cannot load " + fullFilename);
            checkQueue();
        }
        let loadSuccess = function()
        {
            self.loaded.add(fullFilename);
            console.log(self.loaded.size + ". " + fullFilename);
            checkQueue();
        }

        if (this.loaded.has(fullFilename)) {
            this.isRunning = true;
            checkQueue();
            return;
        }

        var script = document.createElement('script');
        script.src = fullFilename;
        script.onload  = loadSuccess;
        script.onerror = loadError;
        document.head.appendChild(script);
        this.isRunning = true;
        if (sync) {
            this.syncLoaded.push(fullFilename);
        }
    }    

    loadFromQueue = function (checkQueue) {
        if (this.loadingStack.length == 0) {
            return;
        }

        let fullFilename = this.loadingStack.pop();
        this.load(fullFilename, checkQueue, true);
        this.isRunning = true;
    }

    // Load modules one by one in sync mode.
    // Call fullLoadedCallback when all modules loaded.
    doInclude = function (includes, fullLoadedCallback) {
        let self = this;

        if (this.cacheLoading) {
            this.callsAfterCacheResolve.push(
                    function() {
                        self.doInclude(includes, fullLoadedCallback);
                    }
                );
            return;
        }

        let checkQueue = function () {
            if (self.loadingStack.length != 0) {
                self.loadFromQueue(checkQueue);
                return;
            }

            self.isRunning = false;
            if (typeof fullLoadedCallback === 'undefined' || fullLoadedCallback === null) {
                return;
            }

            fullLoadedCallback();
        }

        let pushToQueue = function (fullFilename) {
            self.loadingStack.push(fullFilename)
            self.startedLoading.add(fullFilename);
        }

        let reversedIncludes = includes.reverse();

        reversedIncludes.forEach((inc) => {
            let fullFilename = self.getFullname(inc.filename, inc.localDir);
            if (self.startedLoading.has(fullFilename)) {
                return;
            }
            pushToQueue(fullFilename)
        });        

        if (!self.isRunning) {
            checkQueue();
        }
    }

    // Load modules async start load all modules.
    // Call fullLoadedCallback when all includes loaded.
    doIncludeAsync = function (includes, fullLoadedCallback) {
        let self = this;
        if (this.cacheLoading) {
            this.callsAfterCacheResolve.push(
                    function() {
                        self.doIncludeAsync(includes, fullLoadedCallback);
                    }
                );
            return;
        }
        
        let includesFull = [];

        let checkLoading = function () {
            self.isRunning = false;
            if (typeof fullLoadedCallback === 'undefined' || fullLoadedCallback === null) {
                return;
            }

            if (!self.isAllLoaded(includesFull)) {
                return;
            }

            fullLoadedCallback();
        }

        let pushToQueue = function (fullFilename) {
            self.load(fullFilename, checkLoading, false);
            self.startedLoading.add(fullFilename);
        }

        let reversedIncludes = includes.reverse();

        reversedIncludes.forEach((inc) => {
            let fullFilename = self.getFullname(inc.filename, inc.localDir);
            includesFull.push(fullFilename);
            if (self.startedLoading.has(fullFilename)) {
                return;
            }
            pushToQueue(fullFilename)
        });

        checkLoading();
    }

    beginCacheLoading = function (cachedFiles) {
        let self = this;
        cachedFiles.forEach((file) => {
            self.startedLoading.add(file);
            self.loaded.add(file);
        });
        this.cacheLoading = true;
    }

    endCacheLoading = function () {
        this.cacheLoading = false;
        this.callsAfterCacheResolve.forEach((func) => {
            func();
        });
    }
}

let moduleLoader = new ModuleLoader();

function doInclude(includes, callback) {
    moduleLoader.doInclude(includes, callback);
}

function doIncludeAsync(includes, callback) {
    moduleLoader.doIncludeAsync(includes, callback);
}
