import { __assign, __awaiter, __generator, __values } from "tslib";
import { Http, noop, isNullOrUndefined, getGlobalObject, } from "@intelligentgraphics/ig.utilities";
import { State } from "./State";
import { Float3 } from "./Types";
import { Point } from "./AttachPoint";
import { Material } from "./Material";
import { Active, Manual, Stage, Period, Interactor, Touch } from "./Interactor";
import BaseLogger from "./Logger";
var Logger = BaseLogger.createSubLogger("ScriptLoading");
// Replication of client/IG.GFX.Core.d.ts
var igx = {
    Float3: Float3,
    Point: Point,
    Material: Material,
};
var igxCore = {
    Touch: Touch,
    Active: Active,
    Manual: Manual,
    Stage: Stage,
    Period: Period,
    Interactor: Interactor,
};
var globalObject = getGlobalObject();
globalObject["IG"] = globalObject["IG"] || {};
// create scope objects if they're not yet available
globalObject["IGX"] = globalObject["IGX"] || {};
globalObject["IGX"].Core = globalObject["IGX"].Core || {};
// add contents to scope objects
Object.assign(globalObject["IGX"], igx);
Object.assign(globalObject["IGX"].Core, igxCore);
// define the API property of IGX as a getter so it will always return the current api of the state
Object.defineProperty(globalObject["IGX"], "API", {
    get: function () { return State.API; },
});
// IGX.Vector3 is no longer available -> send a deprecation warning once
var didWarn = false;
Object.defineProperty(globalObject["IGX"], "Vector3", {
    get: function () {
        if (didWarn === false) {
            didWarn = true;
            console.warn("[DEPRECATED]", "IGX.Vector3 is deprecated. Please use IGX.Float3 instead!");
        }
        return Float3;
    },
});
var scripts = new Map();
export var loadIGXCScripts = function (scene, basePath) { return __awaiter(void 0, void 0, void 0, function () {
    var logScripts, _a, _b, script, _loop_1, _c, _d, script, state_1, e_1_1;
    var e_2, _e, e_1, _f;
    return __generator(this, function (_g) {
        switch (_g.label) {
            case 0:
                logScripts = new Array();
                try {
                    for (_a = __values(scene.Scripts), _b = _a.next(); !_b.done; _b = _a.next()) {
                        script = _b.value;
                        if (script && script.Name && script.Version) {
                            logScripts.push("" + script.Name + "(" + script.Version + ")");
                        }
                    }
                }
                catch (e_2_1) { e_2 = { error: e_2_1 }; }
                finally {
                    try {
                        if (_b && !_b.done && (_e = _a.return)) _e.call(_a);
                    }
                    finally { if (e_2) throw e_2.error; }
                }
                Logger.info("Loading scripts " + logScripts.join(" + "));
                _loop_1 = function (script) {
                    var scriptJSName, scriptUrl, hash, availableScript, data, loadPromise, didLoad;
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0:
                                if (!script.RunTime || script.Mode !== "Interactor") {
                                    return [2 /*return*/, "continue"];
                                }
                                Logger.info("Loading script " + script.Name);
                                scriptJSName = script.Name;
                                if (!scriptJSName.endsWith(".js")) {
                                    scriptJSName += ".js";
                                }
                                scriptUrl = basePath === undefined
                                    ? scriptJSName
                                    : Http.joinPaths(basePath, scriptJSName);
                                hash = "";
                                if (scene.Hashes) {
                                    hash = scene.Hashes[scriptJSName];
                                }
                                if (hash) {
                                    scriptUrl += "?" + hash;
                                }
                                availableScript = scripts.get(script.Name);
                                data = __assign(__assign({}, script), { Hash: hash });
                                if (!(availableScript !== undefined &&
                                    availableScript.Status === "loading")) return [3 /*break*/, 2];
                                return [4 /*yield*/, availableScript.Done];
                            case 1:
                                _a.sent();
                                _a.label = 2;
                            case 2:
                                // script is available and does not have to be loaded
                                if (availableScript !== undefined &&
                                    (availableScript.Hash === hash ||
                                        !isNewer(availableScript.Version, script.Version))) {
                                    Logger.info("Not loading script " + script.Name + ", because it's already available");
                                    return [2 /*return*/, "continue"];
                                }
                                loadPromise = loadScript(scriptUrl, script.Name).catch(function (err) {
                                    Logger.error("Could not load script " + script.Name, err);
                                    scripts.delete(script.Name);
                                    return false;
                                });
                                scripts.set(script.Name, __assign(__assign({}, data), { Done: loadPromise, Status: "loading" }));
                                return [4 /*yield*/, loadPromise];
                            case 3:
                                didLoad = _a.sent();
                                if (!didLoad) {
                                    Logger.error("Stopping script loading because of an error");
                                    return [2 /*return*/, { value: void 0 }];
                                }
                                scripts.set(script.Name, __assign(__assign({}, data), { Status: "loaded" }));
                                return [2 /*return*/];
                        }
                    });
                };
                _g.label = 1;
            case 1:
                _g.trys.push([1, 6, 7, 8]);
                _c = __values(scene.Scripts), _d = _c.next();
                _g.label = 2;
            case 2:
                if (!!_d.done) return [3 /*break*/, 5];
                script = _d.value;
                return [5 /*yield**/, _loop_1(script)];
            case 3:
                state_1 = _g.sent();
                if (typeof state_1 === "object")
                    return [2 /*return*/, state_1.value];
                _g.label = 4;
            case 4:
                _d = _c.next();
                return [3 /*break*/, 2];
            case 5: return [3 /*break*/, 8];
            case 6:
                e_1_1 = _g.sent();
                e_1 = { error: e_1_1 };
                return [3 /*break*/, 8];
            case 7:
                try {
                    if (_d && !_d.done && (_f = _c.return)) _f.call(_c);
                }
                finally { if (e_1) throw e_1.error; }
                return [7 /*endfinally*/];
            case 8: return [2 /*return*/];
        }
    });
}); };
/**
 * Compares all the by point seperated numbers of 2 version strings. Returns true if b is newer
 *
 * @param {string} a
 * @param {string} b
 * @returns
 */
var isNewer = function (a, b) {
    if (isNullOrUndefined(a)) {
        return false;
    }
    if (isNullOrUndefined(b)) {
        return false;
    }
    var aParts = a.split(".").map(function (part) { return parseInt(part, 10); });
    var bParts = b.split(".").map(function (part) { return parseInt(part, 10); });
    for (var index = 0; index < aParts.length; index++) {
        if (bParts[index] === undefined) {
            return false;
        }
        if (aParts[index] < bParts[index]) {
            return true;
        }
    }
    return false;
};
/**
 * Load a script from a url
 *
 * @param {string} url
 * @param {string} name
 */
var loadScript = function (url, name) {
    return new Promise(function (resolve, reject) {
        var parent = document.getElementsByTagName("head")[0];
        var scriptElement = document.querySelector("script[data-script-name=\"" + name + "\"]");
        if (scriptElement) {
            parent.removeChild(scriptElement);
        }
        scriptElement = document.createElement("script");
        scriptElement.setAttribute("type", "text/javascript");
        // set a script-name data attribute so we can easily identify the script later
        scriptElement.setAttribute("data-script-name", name);
        parent.appendChild(scriptElement);
        var resetScope = function () {
            // replace the script method so this promise scope can be garbage collected
            scriptElement.onerror = noop;
            scriptElement.onload = noop;
        };
        scriptElement.onerror = function (event, source, fileno, columnNumber, error) {
            resetScope();
            parent.removeChild(scriptElement);
            reject(typeof event === "string" ? new Error(event) : error);
        };
        scriptElement.onload = function () {
            resetScope();
            resolve(true);
        };
        scriptElement.setAttribute("src", url);
    });
};
