var _a, _b;
var _c;
import * as mc from "@minecraft/server";
import * as utils from "../../utils/index";
export class BackupSystem {
    static get BackupRegistry() {
        return Object.assign({}, this.backupRegistry);
    }
    static registerLoadEvent(category, callback) {
        this.onLoadEvents.set(category, callback);
    }
    static registerBackup(id, category) {
        this.backupRegistry[id] = category;
        utils.writeDynamicProperty(mc.world, "backupRegistry", this.backupRegistry);
        utils.mclog(`Adding ${category} at ${id} to:\n` + JSON.stringify(this.backupRegistry, null, "\t"), this.name);
    }
    static removeBackup(id) {
        delete this.backupRegistry[id];
        utils.writeDynamicProperty(mc.world, "backupRegistry", this.backupRegistry);
        mc.world.structureManager.delete("mystructure:" + id);
        utils.mclog(`Removing ${id} from:\n` + JSON.stringify(this.backupRegistry, null, "\t"), this.name);
    }
    static registerDelete(id, category) {
        const entity = utils.getEntitySafe(id);
        if (entity === null || entity === void 0 ? void 0 : entity.isValid()) {
            utils.mclog(`Found entity for deletion: ${id}, removing`, this.name);
            try {
                entity.remove();
                return;
            }
            catch (error) { }
        }
        utils.mclog(`Could not find entity for ${id}, adding to delete registry`, this.name);
        this.deleteRegistry[id] = category;
        utils.writeDynamicProperty(mc.world, "deleteRegistry", this.deleteRegistry);
    }
    static removeDelete(id) {
        delete this.deleteRegistry[id];
        utils.writeDynamicProperty(mc.world, "deleteRegistry", this.deleteRegistry);
    }
    static loadFromBackup(id, dimension, location) {
        const category = this.backupRegistry[id];
        if (!category)
            return Promise.resolve(undefined);
        const structureName = "mystructure:" + id;
        const structure = mc.world.structureManager.get(structureName);
        if (!(structure === null || structure === void 0 ? void 0 : structure.isValid()))
            return Promise.resolve(undefined);
        mc.world.structureManager.place(structure, dimension, location);
        const entities = utils.getEntitiesSafe(dimension, { location, maxDistance: 10 });
        const newEntity = entities.find(entity => utils.readDynamicProperty(entity, "structure_id") === id);
        entities.forEach(entity => {
            if (entity.isValid() && entity.hasTag("structure_copy")) {
                entity.remove();
            }
        });
        return new Promise(resolve => {
            if (newEntity) {
                utils.waitForEvent(1, () => {
                    var _a;
                    const actor = utils.Actor.getActor(newEntity.id, utils.Actor);
                    if (actor) {
                        utils.mclog(`Loaded backup ${id} to ${actor.ActorId}:${actor.id}`, this.name);
                        (_a = this.onLoadEvents.get(category)) === null || _a === void 0 ? void 0 : _a(id, actor.id);
                        this.registerDelete(id, category);
                        this.removeBackup(id);
                        resolve(actor);
                    }
                    else {
                        resolve(undefined);
                    }
                });
            }
            else {
                resolve(undefined);
            }
        });
    }
}
_c = BackupSystem;
BackupSystem.backupRegistry = (_a = utils.readDynamicProperty(mc.world, "backupRegistry")) !== null && _a !== void 0 ? _a : {};
BackupSystem.deleteRegistry = (_b = utils.readDynamicProperty(mc.world, "deleteRegistry")) !== null && _b !== void 0 ? _b : {};
BackupSystem.onLoadEvents = new Map();
BackupSystem.event = mc.world.afterEvents.entityLoad.subscribe(event => {
    const category = _c.deleteRegistry[event.entity.id];
    if (category) {
        utils.mclog(`Entity marked for deletion found: ${event.entity.id}, removing`, _c.name);
        event.entity.remove();
        _c.removeDelete(event.entity.id);
    }
});
export class BackupComponent extends utils.ActorComponent {
    constructor() {
        super(...arguments);
        this.saveBinding = this.save.bind(this);
        this.onKilledBinding = this.onKilled.bind(this);
    }
    onComponentAdded() {
        this.options.backupEvents.forEach(event => {
            this.actor.subscribe(event, this.saveBinding);
        });
        if (this.options.deleteBackupOnKilled) {
            mc.world.afterEvents.entityDie.subscribe(this.onKilledBinding, { entities: [this.actor.UnsafeEntity] });
        }
        this.save();
    }
    onComponentRemoved() {
        this.options.backupEvents.forEach(event => {
            this.actor.unsubscribe(event, this.saveBinding);
        });
        mc.world.afterEvents.entityDie.unsubscribe(this.onKilledBinding);
    }
    onKilled(event) {
        if (this.options.deleteBackupOnKilled) {
            BackupSystem.removeBackup(this.actor.id);
        }
    }
    save() {
        var _a, _b;
        const nearbyEntities = utils.getEntitiesSafe(this.actor.dimension, { location: this.actor.location, maxDistance: 10 });
        nearbyEntities.forEach(entity => {
            if (entity.id !== this.actor.id) {
                entity.addTag("structure_copy");
            }
        });
        utils.writeDynamicProperty(this.actor, "structure_id", this.actor.id);
        BackupSystem.registerBackup(this.actor.id, (_a = this.options.saveCategory) !== null && _a !== void 0 ? _a : "default");
        mc.world.structureManager.delete("mystructure:" + this.actor.id);
        mc.world.structureManager.createFromWorld("mystructure:" + this.actor.id, this.actor.dimension, this.actor.location, this.actor.location, {
            includeBlocks: false,
            includeEntities: true,
            saveMode: mc.StructureSaveMode.World,
        });
        nearbyEntities.forEach(entity => entity.removeTag("structure_copy"));
        utils.mclog(`Saving ${this.actor.ActorId}:${this.actor.id} to backup ${(_b = this.options.saveCategory) !== null && _b !== void 0 ? _b : "default"}`, this.id);
    }
    load(dimension, location) {
        return BackupSystem.loadFromBackup(this.actor.id, dimension, location);
    }
    static registerLoadEvent(category, callback) {
        BackupSystem.registerLoadEvent(category, callback);
    }
    static load(id, dimension, location) {
        return BackupSystem.loadFromBackup(id, dimension, location);
    }
}
