import * as mc from "@minecraft/server";
import * as utils from "../../utils/index";
import { ComponentCompanion } from "./base";
export class CompanionBackupComponent extends ComponentCompanion {
    constructor(backupEvents, removeEvents) {
        super();
        this.backupCallbacks = [];
        this.removeCallbacks = [];
        this.dimensionBinding = this.onDimensionChange.bind(this);
        this.backupEvents = backupEvents;
        this.removeEvents = removeEvents;
    }
    async onComponentAdded(npc) {
        super.onComponentAdded(npc);
        if (npc.Entity.isValid())
            npc.Entity.UnsafeEntity.addTag("ldz:lantern_ignore");
        mc.world.afterEvents.playerDimensionChange.subscribe(this.dimensionBinding);
        this.backupEvents.forEach(key => {
            const callback = () => { this.performBackup(); };
            this.backupCallbacks.push(callback);
            npc.subscribeToBroadcast(key, callback);
        });
        this.removeEvents.forEach(key => {
            const callback = () => { this.removeBackup(); };
            this.removeCallbacks.push(callback);
            npc.subscribeToBroadcast(key, callback);
        });
        this.performBackup();
        this.duplicateCheck();
    }
    async onComponentRemoved(npc) {
        mc.world.afterEvents.playerDimensionChange.unsubscribe(this.dimensionBinding);
        if (npc.Entity.isValid())
            npc.Entity.UnsafeEntity.removeTag("ldz:lantern_ignore");
        this.backupEvents.forEach((key, index) => {
            const callback = this.backupCallbacks[index];
            npc.unsubscribeToBroadcast(key, callback);
        });
        this.removeEvents.forEach((key, index) => {
            const callback = this.removeCallbacks[index];
            npc.unsubscribeToBroadcast(key, callback);
        });
    }
    onDimensionChange(event) {
        var _a;
        if (event.player.id === ((_a = this.npc) === null || _a === void 0 ? void 0 : _a.Owner.id)) {
            this.npc.broadcastEvent(`to_${event.toDimension.id.replace("minecraft:", "")}`);
            utils.mcwarn(`to_${event.toDimension.id.replace("minecraft:", "")}`);
            utils.waitForLoopEvent(20, () => {
                var _a, _b;
                if (event.player.isValid() && event.player.isOnGround && event.player.dimension === event.toDimension && new utils.Vector3(event.player.location).distance(event.toLocation) > 3) {
                    if (((_a = this.npc) === null || _a === void 0 ? void 0 : _a.Entity.dimension.id) !== event.toDimension.id) {
                        (_b = this.npc) === null || _b === void 0 ? void 0 : _b.destructor();
                        CompanionBackupComponent.restoreBackup(this.npc.Owner);
                    }
                    return true;
                }
                else {
                    return false;
                }
            });
        }
    }
    async duplicateCheck() {
        var _a, _b;
        while ((_a = this.npc) === null || _a === void 0 ? void 0 : _a.Entity.isValid()) {
            const id = (_b = utils.readDynamicProperty(this.npc.Entity, "backupId")) !== null && _b !== void 0 ? _b : this.npc.ID;
            const ownerLocation = this.npc.Owner.location;
            const duplicates = utils.getEntitiesSafe(this.npc.Owner.dimension, { type: this.npc.Entity.typeId })
                .filter(entity => utils.readDynamicProperty(entity, "backupId") === id)
                .sort((a, b) => a.location.distance(ownerLocation) - b.location.distance(ownerLocation));
            if (duplicates.length > 1) {
                utils.mcwarn(`Found ${duplicates.length} duplicates of ${this.npc.TypeId} with id ${id}`);
                duplicates.forEach((entity, index) => {
                    if (index > 0) {
                        entity.runCommand(`scriptevent ${utils.GlobalNamespace.Namespace}on_destruct`);
                    }
                });
            }
            await utils.waitForTicks(120);
        }
    }
    performBackup() {
        var _a, _b;
        if ((_a = this.npc) === null || _a === void 0 ? void 0 : _a.Entity.isValid()) {
            utils.mclog(`${this.id} backing up ${this.npc.TypeId} (${this.npc.ID})`, "npc");
            if (!utils.readDynamicProperty(this.npc.Entity, "backupId"))
                utils.writeDynamicProperty(this.npc.Entity, "backupId", this.npc.ID);
            this.npc.Entity.runCommand(`structure save ${this.npc.ID}_backup ~ ~ ~ ~ ~1 ~ true disk false`);
            const backups = (_b = utils.readDynamicProperty(this.npc.Owner, "companionBackups")) !== null && _b !== void 0 ? _b : [];
            utils.addUniqueToArray(backups, this.npc.ID);
            utils.writeDynamicProperty(this.npc.Owner, "companionBackups", backups);
        }
    }
    removeBackup() {
        var _a;
        if (this.npc) {
            this.npc.Owner.runCommand(`structure delete ${this.npc.ID}_backup`);
            const backups = (_a = utils.readDynamicProperty(this.npc.Owner, "companionBackups")) !== null && _a !== void 0 ? _a : [];
            utils.removeFromArray(backups, this.npc.ID);
            utils.writeDynamicProperty(this.npc.Owner, "companionBackups", backups);
        }
    }
    onDespawn() {
        var _a;
        const originalLocation = (_a = this.npc) === null || _a === void 0 ? void 0 : _a.Owner.location;
        utils.waitForLoopEvent(20, () => {
            var _a;
            if (((_a = this.npc) === null || _a === void 0 ? void 0 : _a.Owner.isOnGround) && originalLocation.distance(this.npc.Owner.location) > 3) {
                CompanionBackupComponent.restoreBackup(this.npc.Owner);
                return true;
            }
            else {
                return false;
            }
        });
    }
    static restoreBackup(player) {
        var _a;
        const backups = (_a = utils.readDynamicProperty(player, "companionBackups")) !== null && _a !== void 0 ? _a : [];
        const foundBackups = [];
        backups.filter(id => { var _a; return !((_a = mc.world.getEntity(id)) === null || _a === void 0 ? void 0 : _a.isValid()); }).forEach(id => {
            utils.mclog(`Restoring ${player.name}'s backup (${id})`);
            player.runCommand(`structure load ${id}_backup ~ ~ ~`);
            foundBackups.push(id);
        });
        foundBackups.forEach(backup => utils.removeFromArray(backups, backup));
        utils.writeDynamicProperty(player, "companionBackups", backups);
        return foundBackups.length > 0;
    }
}
