"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.destroy = exports.boot = void 0;
const Graph_1 = require("../Class/Graph");
const styleToCSS_1 = require("../client/id/styleToCSS");
const attachStyle_1 = require("../client/render/attachStyle");
const spec_1 = require("../client/spec");
const store_1 = require("../client/store");
const EventEmitter_1 = require("../EventEmitter");
const none_1 = require("../host/none");
const NOOP_1 = require("../NOOP");
const Object_1 = require("../Object");
const SharedObject_1 = require("../SharedObject");
const fromBundle_1 = require("../spec/fromBundle");
const _classes_1 = require("../system/_classes");
const _components_1 = require("../system/_components");
const _specs_1 = require("../system/_specs");
const weakMerge_1 = require("../types/weakMerge");
const id_1 = require("../util/id");
const object_1 = require("../util/object");
function boot(parent = null, api = (0, none_1.noHost)(), opt = {}) {
    const { path = '', specs: _specs = {} } = opt;
    const keyboard = {
        pressed: [],
        repeat: false,
    };
    const gamepads = [];
    const pointers = {};
    const customEvent = new Set();
    const context = [];
    const input = {
        keyboard,
        gamepads,
        pointers,
    };
    const flag = {
        dragAndDrop: {},
        pointerCapture: {},
        spriteSheetMap: {},
    };
    const feature = {};
    const merged_specs = (0, weakMerge_1.weakMerge)(_specs_1.default, _specs);
    const specs_ = new Object_1.Object_(merged_specs);
    const emitter = parent ? parent.emitter : new EventEmitter_1.EventEmitter();
    const componentLocalToRemote = {};
    const componentRemoteToLocal = {};
    const system = {
        path,
        parent,
        emitter,
        animated: true,
        root: null,
        theme: 'dark',
        customEvent,
        input,
        context,
        specs_,
        specs: merged_specs,
        classes: _classes_1.default,
        components: _components_1.default,
        graphs: [],
        specsCount: {},
        cache: flag,
        feature,
        foreground: {
            svg: undefined,
            canvas: undefined,
            app: undefined,
        },
        showLongPress: undefined,
        captureGesture: undefined,
        global: parent
            ? parent.global
            : {
                ref: {},
                component: {},
            },
        api,
        boot: (opt) => boot(system, api, opt),
        graph: (system, opt) => new SharedObject_1.SharedObject(new store_1.LocalStore(system, 'local')),
        registerComponent: function (component) {
            const id = (0, id_1.uuidNotIn)(system.global.component);
            system.global.component[id] = component;
            return id;
        },
        registerUnit: (id) => {
            var _a;
            system.specsCount[id] = (_a = system.specsCount[id]) !== null && _a !== void 0 ? _a : 0;
            system.specsCount[id]++;
            if (system.parent) {
                system.parent.registerUnit(id);
            }
        },
        unregisterUnit: function (id) {
            system.specsCount[id]--;
            if (system.parent) {
                system.parent.unregisterUnit(id);
            }
        },
        forkSpec: (spec) => {
            if (system.specsCount[spec.id] > 0) {
                const clonedSpec = (0, object_1.clone)(spec);
                const { id: newSpecId } = system.newSpec(clonedSpec);
                delete clonedSpec.system;
                return [newSpecId, clonedSpec];
            }
            else {
                system.setSpec(spec.id, spec);
                return [spec.id, spec];
            }
        },
        fromBundle: (bundle) => {
            return (0, fromBundle_1.fromBundle)(bundle, merged_specs, {});
        },
        newGraph: (bundle) => {
            const graph = new Graph_1.Graph({}, {}, system);
            const unlisten = () => {
                graph.destroy();
            };
            return [graph, unlisten];
        },
        getSpec: (id) => {
            return merged_specs[id];
        },
        hasSpec: (id) => {
            return !!merged_specs[id];
        },
        newSpecId: () => {
            return (0, id_1.uuidNotIn)(merged_specs);
        },
        emptySpec: () => {
            const id = (0, spec_1.newSpecId)(merged_specs);
            const spec = (0, spec_1.emptySpec)({ id });
            system.newSpec(spec);
            return spec;
        },
        newSpec: (spec) => {
            const specId = (0, spec_1.newSpecId)(merged_specs);
            spec.id = specId;
            // console.log('newSpec', { specId, spec })
            specs_.set(specId, spec);
            return spec;
        },
        setSpec: (specId, spec) => {
            // console.log('setSpec', { specId, spec })
            specs_.set(specId, spec);
        },
        injectSpecs: (newSpecs) => {
            // console.log('injectSpecs', { newSpecs })
            const map_spec_id = {};
            const visited = new Set();
            const _set = (spec_id, spec) => {
                if (visited.has(spec_id)) {
                    return;
                }
                if (map_spec_id[spec_id]) {
                    return;
                }
                let nextSpecId = spec_id;
                let hasSpec = false;
                while (system.hasSpec(nextSpecId)) {
                    nextSpecId = system.newSpecId();
                    hasSpec = true;
                }
                const { units } = spec;
                for (const unitId in units) {
                    const unit = units[unitId];
                    if (system.hasSpec(unit.id) && !!_specs_1.default[unit.id]) {
                        //
                    }
                    else {
                        const spec = newSpecs[unit.id];
                        _set(unit.id, spec);
                    }
                }
                visited.add(spec_id);
                if (hasSpec) {
                    if (JSON.stringify(spec) === JSON.stringify(system.getSpec(spec_id))) {
                        //
                    }
                    else {
                        // TODO
                        map_spec_id[spec_id] = nextSpecId;
                        specs_.set(spec_id, spec);
                    }
                }
                else {
                    specs_.set(spec_id, spec);
                }
                // specs_.set(nextSpecId, spec) // TODO
            };
            for (const spec_id in newSpecs) {
                const spec = newSpecs[spec_id];
                _set(spec_id, spec);
            }
            return map_spec_id;
        },
        injectPrivateCSSClass: function (globalId, className, style) {
            if (system.root) {
                if (!system.global.component[globalId]) {
                    throw new Error('Component not found.');
                }
                const css = `${(0, styleToCSS_1.styleToCSS)(style)}`;
                (0, attachStyle_1.appendRootStyle)(system, css);
                return () => {
                    (0, attachStyle_1.removeRootStyle)(system, css);
                };
            }
            else {
                return NOOP_1.NOOP;
            }
        },
        registerRemoteComponent: function (globalId, remoteGlobalId) {
            const component = system.global.component[globalId];
            if (!component) {
                throw new Error('Component not found.');
            }
            componentLocalToRemote[globalId] = remoteGlobalId;
            componentRemoteToLocal[remoteGlobalId] = globalId;
            emitter.emit(remoteGlobalId, component);
        },
        getRemoteComponent: function (remoteId) {
            const localId = componentRemoteToLocal[remoteId];
            if (!localId) {
                return undefined;
            }
            return system.global.component[localId];
        },
    };
    return system;
}
exports.boot = boot;
function destroy(system) {
    const { graphs } = system;
    for (const graph of graphs) {
        graph.destroy();
    }
    // TODO
}
exports.destroy = destroy;
//# sourceMappingURL=index.js.map