Source: base/System.js

import { ERROR_CODES } from "../constants.js";
import { Exception } from "./Exception.js";
import { GameStage } from "./GameStage.js";
import { ISystem } from "./ISystem.js";
import { SystemSettings } from "../configs.js";

import { LoadingStage } from "../design/LoadingStage.js";

const loadingPageName = "loadingPage";
/**
 * A main app class, <br>
 * Holder class for GameStage,<br>
 * can register new GameStages,<br>
 * init and preload data for them,<br>
 */
export class System {
    /**
     * @type {Map<string, GameStage>}
     */
    #registeredStages;
    /**
     * @type {ISystem}
     */
    #iSystem;
    /**
     * @param {SystemSettings} iSystemSettings - holds iSystem settings
     * @param {HTMLElement} [canvasContainer] - If it is not passed, iSystem will create div element and attach it to body
     */
    constructor(iSystemSettings, canvasContainer) {
        if (!iSystemSettings) {
            Exception(ERROR_CODES.CREATE_INSTANCE_ERROR, "iSystemSettings should be passed to class instance");
        }
        this.#registeredStages = new Map();

        if (!canvasContainer) {
            canvasContainer = document.createElement("div");
            document.body.appendChild(canvasContainer);
        }

        this.#iSystem = new ISystem(iSystemSettings, this.#registeredStages, canvasContainer);

        this.registerStage(loadingPageName, LoadingStage);

        this.#iSystem.iLoader.addEventListener("loadstart", this.#loadStart);
        this.#iSystem.iLoader.addEventListener("progress", this.#loadProgress);
        this.#iSystem.iLoader.addEventListener("load", this.#loadComplete);
    }

    /**
     * @type {ISystem}
     */
    get iSystem() {
        return this.#iSystem;
    }

    /**
     * A main factory method for create GameStage instances, <br>
     * register them in a System and call GameStage.register() stage
     * @param {string} screenPageName
     * @param {GameStage} stage
     */
    registerStage(screenPageName, stage) {
        if (screenPageName && typeof screenPageName === "string" && screenPageName.trim().length > 0) {
            const stageInstance = new stage();
            stageInstance._register(screenPageName, this.iSystem);
            this.#registeredStages.set(screenPageName, stageInstance);
        } else {
            Exception(ERROR_CODES.CREATE_INSTANCE_ERROR, "valid class name should be provided");
        }
    }

    /**
     * Preloads assets for all registered pages
     * @return {Promise<void>}
     */
    preloadAllData() {
        return this.#iSystem.iLoader.preload();
    }

    #loadStart = (event) => {
        this.#iSystem.startGameStage(loadingPageName, {total: event.total});
    };

    #loadProgress = (event) => {
        const uploaded = event.loaded,
            left = event.total,
            loadingPage = this.#registeredStages.get(loadingPageName);
            
        loadingPage._progress(uploaded, left);
    };

    #loadComplete = () => {
        this.#iSystem.stopGameStage(loadingPageName);
    };
}