"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.boot = boot;
const EventEmitter_1 = require("../EventEmitter");
const NOOP_1 = require("../NOOP");
const Object_1 = require("../Object");
const Registry_1 = require("../Registry");
const icons_1 = require("../client/icons");
const theme_1 = require("../client/theme");
const InvalidStateError_1 = require("../exception/InvalidStateError");
const start_1 = require("../start");
const wrapper_1 = require("../types/interface/async/wrapper");
const array_1 = require("../util/array");
const clone_1 = require("../util/clone");
const weakMerge_1 = require("../weakMerge");
const style_1 = require("./style");
const SYSTEM_USER_VISIBLE_EVENTS = ['hashchange'];
function boot(parent = null, api, opt) {
    const { specs = {}, classes = {}, components = {}, flags = {}, env = {}, } = opt;
    const path = opt.path || '/';
    const keyboard = {
        pressed: [],
        repeat: false,
    };
    const gamepads = [];
    const pointers = {};
    const customEvent = new Set();
    const context = [];
    const input = {
        keyboard,
        gamepads,
        pointers,
    };
    const cache = {
        dragAndDrop: {},
        pointerCapture: {},
        servers: {},
        events: {},
        requests: {},
        responses: {},
        ws: {},
        wss: {},
        interceptors: [],
        eventSources: {},
    };
    const feature = {};
    const specs__ = (0, weakMerge_1.weakMerge)(specs, {});
    const registry = new Registry_1.Registry(specs__);
    const { specs_ } = registry;
    const { specsCount, lock: specsLock, newSpecId, hasSpec, emptySpec, newSpec, getSpec, setSpec, forkSpec, injectSpecs, deleteSpec, registerUnit, unregisterUnit, shouldFork, lockSpec, unlockSpec, } = registry;
    for (const specId in specs) {
        specsLock[specId] = true;
    }
    const emitter = parent ? parent.emitter : new EventEmitter_1.EventEmitter_();
    const componentRemoteToLocal = {};
    const theme = 'dark';
    const color = (0, theme_1.themeColor)(theme);
    const system = {
        path,
        parent,
        emitter,
        root: (parent && parent.root) || null,
        color,
        theme,
        customEvent,
        async: wrapper_1.ASYNC,
        input,
        context,
        specs_,
        specs: specs__,
        classes,
        components,
        icons: icons_1.icons,
        specsCount,
        lock: specsLock,
        cache,
        feature,
        foreground: (0, clone_1.clone)(parent?.foreground ?? {}),
        style: style_1.style,
        showLongPress: undefined,
        captureGesture: undefined,
        global: {
            ref: {},
            graph: {},
            data: new Object_1.Object_({}),
            scope: {},
            env,
        },
        api,
        flags: {
            defaultInputModeNone: false,
            ...flags,
        },
        boot: (opt_ = {}) => {
            return boot(system, api, (0, weakMerge_1.weakMerge)(opt, opt_));
        },
        start: (bundle) => {
            return (0, start_1.start)(system, bundle);
        },
        getSpec: getSpec.bind(registry),
        hasSpec: hasSpec.bind(registry),
        newSpecId: newSpecId.bind(registry),
        emptySpec: emptySpec.bind(registry),
        forkSpec: forkSpec.bind(registry),
        newSpec: newSpec.bind(registry),
        setSpec: setSpec.bind(registry),
        injectSpecs: injectSpecs.bind(registry),
        deleteSpec: deleteSpec.bind(registry),
        registerUnit: registerUnit.bind(registry),
        unregisterUnit: unregisterUnit.bind(registry),
        shouldFork: shouldFork.bind(registry),
        lockSpec: lockSpec.bind(registry),
        unlockSpec: unlockSpec.bind(registry),
        getLocalComponents: function (remoteGlobalId) {
            const components = componentRemoteToLocal[remoteGlobalId];
            return components ?? [];
        },
        registerLocalComponent: function (component, remoteGlobalId) {
            componentRemoteToLocal[remoteGlobalId] =
                componentRemoteToLocal[remoteGlobalId] ?? [];
            componentRemoteToLocal[remoteGlobalId].push(component);
            emitter.emit(remoteGlobalId, component);
        },
        unregisterLocalComponent: function (component, remoteGlobalId) {
            const components = componentRemoteToLocal[remoteGlobalId];
            (0, array_1.remove)(components, component);
        },
        intercept: function (opt, handler) {
            const interceptor = {
                opt,
                handler,
            };
            cache.interceptors.push(interceptor);
            return () => {
                (0, array_1.remove)(cache.interceptors, interceptor);
            };
        },
        addListener: function (event, listener) {
            if (SYSTEM_USER_VISIBLE_EVENTS.includes('hashchange')) {
                return emitter.addListener(event, listener);
            }
            return NOOP_1.NOOP;
        },
        prependListener: function (event, listener) {
            throw new InvalidStateError_1.InvalidStateError();
        },
        removeListener: function (event, listener) {
            throw new InvalidStateError_1.InvalidStateError();
        },
        eventNames: function () {
            return SYSTEM_USER_VISIBLE_EVENTS;
        },
        emit: function (event, ...args) {
            throw new InvalidStateError_1.InvalidStateError();
        },
        listenerCount: function (name) {
            if (SYSTEM_USER_VISIBLE_EVENTS.includes('hashchange')) {
                return emitter.listenerCount(name);
            }
            return 0;
        },
    };
    return system;
}
//# sourceMappingURL=index.js.map