"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var ig_utilities_1 = require("@intelligentgraphics/ig.utilities");
var State_1 = require("./State");
var Types_1 = require("./Types");
var AttachPoint_1 = require("./AttachPoint");
var Material_1 = require("./Material");
var Interactor_1 = require("./Interactor");
var Logger_1 = tslib_1.__importDefault(require("./Logger"));
var Logger = Logger_1.default.createSubLogger("ScriptLoading");
// Replication of client/IG.GFX.Core.d.ts
var igx = {
    Float3: Types_1.Float3,
    Point: AttachPoint_1.Point,
    Material: Material_1.Material,
};
var igxCore = {
    Touch: Interactor_1.Touch,
    Active: Interactor_1.Active,
    Manual: Interactor_1.Manual,
    Stage: Interactor_1.Stage,
    Period: Interactor_1.Period,
    Interactor: Interactor_1.Interactor,
};
var globalObject = ig_utilities_1.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_1.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 Types_1.Float3;
    },
});
var scripts = new Map();
exports.loadIGXCScripts = function (scene, basePath) { return tslib_1.__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 tslib_1.__generator(this, function (_g) {
        switch (_g.label) {
            case 0:
                logScripts = new Array();
                try {
                    for (_a = tslib_1.__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 tslib_1.__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
                                    : ig_utilities_1.Http.joinPaths(basePath, scriptJSName);
                                hash = "";
                                if (scene.Hashes) {
                                    hash = scene.Hashes[scriptJSName];
                                }
                                if (hash) {
                                    scriptUrl += "?" + hash;
                                }
                                availableScript = scripts.get(script.Name);
                                data = tslib_1.__assign(tslib_1.__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, tslib_1.__assign(tslib_1.__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, tslib_1.__assign(tslib_1.__assign({}, data), { Status: "loaded" }));
                                return [2 /*return*/];
                        }
                    });
                };
                _g.label = 1;
            case 1:
                _g.trys.push([1, 6, 7, 8]);
                _c = tslib_1.__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 (ig_utilities_1.isNullOrUndefined(a)) {
        return false;
    }
    if (ig_utilities_1.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 = ig_utilities_1.noop;
            scriptElement.onload = ig_utilities_1.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);
    });
};
