"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Object_ = void 0;
const EventEmitter_1 = require("./EventEmitter");
const f_1 = require("./system/f/object/Keys/f");
const array_1 = require("./util/array");
class Object_ {
    constructor(obj) {
        this._node = {
            emitter: new EventEmitter_1.EventEmitter_(),
            children: {},
        };
        this._dispatch = (type, path, key, value) => {
            this._rec_dispatch(path, this._node, type, path, key, value);
        };
        this._rec_dispatch = (current, node, type, path, key, value) => {
            if (current.length === 0) {
                this._end_dispatch(node, type, path, key, value);
            }
            else {
                const [p, ...next] = current;
                const key_node = node.children[p];
                if (key_node) {
                    this._rec_dispatch(next, key_node, type, path, key, value);
                }
                const all_node = node.children['*'];
                if (all_node) {
                    this._rec_dispatch(next, all_node, type, path, key, value);
                }
            }
        };
        this._end_dispatch = (node, type, path, key, value) => {
            const { emitter } = node;
            if (emitter) {
                emitter.emit(key, type, path, key, value);
                emitter.emit('*', type, path, key, value);
            }
        };
        this._ensure_emitter = (path) => {
            let node = this._node;
            for (const p of path) {
                if (!node.children[p]) {
                    node.children[p] = { emitter: new EventEmitter_1.EventEmitter_(), children: {} };
                }
                node = node.children[p];
            }
            return node.emitter;
        };
        this._remove_node = (path) => {
            if (path.length > 0) {
                const [last, rest] = (0, array_1.pop)(path);
                const parent = this._node_at_path(rest);
                delete parent.children[last];
            }
        };
        this._node_at_path = (path) => {
            let node = this._node;
            for (const p of path) {
                node = node.children[p];
                if (!node) {
                    return null;
                }
            }
            return node;
        };
        this._set_path = (path, data) => {
            const parent_path = path.slice(0, -1);
            const key = (0, array_1.last)(path);
            const obj = this._obj_at_path(parent_path);
            obj[key] = data;
            this._dispatch('set', parent_path, key, data);
        };
        this._delete = (name) => __awaiter(this, void 0, void 0, function* () {
            this._delete_path([name]);
            return;
        });
        this._delete_path = (path) => {
            const parent_path = path.slice(0, -1);
            const key = (0, array_1.last)(path);
            const obj = this._obj_at_path(parent_path);
            const value = obj[key];
            delete obj[key];
            this._dispatch('delete', parent_path, key, value);
        };
        this._obj = obj;
    }
    _obj_at_path(path) {
        let _obj = this._obj;
        for (const p of path) {
            _obj = _obj[p];
        }
        return _obj;
    }
    read() {
        return __awaiter(this, void 0, void 0, function* () {
            return this._obj;
        });
    }
    write(data) {
        this._obj = data;
        return;
    }
    get(name) {
        return __awaiter(this, void 0, void 0, function* () {
            const value = this._obj[name];
            if (value === undefined) {
                throw new Error('key value not found');
            }
            return this._obj[name];
        });
    }
    set(name, data) {
        return __awaiter(this, void 0, void 0, function* () {
            this._set_path([name], data);
        });
    }
    hasKey(name) {
        return __awaiter(this, void 0, void 0, function* () {
            return this._obj[name] !== undefined;
        });
    }
    delete(name) {
        return __awaiter(this, void 0, void 0, function* () {
            return this._delete(name);
        });
    }
    deepGet(path) {
        return __awaiter(this, void 0, void 0, function* () {
            const value = this._obj_at_path(path);
            return value;
        });
    }
    deepSet(path, data) {
        return __awaiter(this, void 0, void 0, function* () {
            this._set_path(path, data);
            return;
        });
    }
    deepDelete(path) {
        return __awaiter(this, void 0, void 0, function* () {
            this._delete_path(path);
            return;
        });
    }
    keys() {
        return __awaiter(this, void 0, void 0, function* () {
            return (0, f_1.keys)(this._obj);
        });
    }
    subscribe(path, key, listener) {
        const emitter = this._ensure_emitter(path);
        emitter.addListener(key, listener);
        return () => {
            emitter.removeListener(key, listener);
            const eventNames = emitter.eventNames();
            if (eventNames.length === 0) {
                this._remove_node(path);
            }
        };
    }
}
exports.Object_ = Object_;
//# sourceMappingURL=Object.js.map