//
// manifoldDomain.js — Manifold Domain Script
//
// Assignment client script — do NOT wrap in an IIFE.
// Requires EntityViewer to be initialized before entity queries work.
//
// Deploy via Domain Settings > Scripts.
//

var WS_URL      = "wss://wizards.cyou/ws";
var SCAN_CENTER = { x: 0, y: 0, z: 0 };
var SCAN_RADIUS = 500;

var ws             = null;
var reconnectTimer = null;
var heartbeatTimer = null;
var isReady        = false;

// ── EntityViewer init ────────────────────────────────────────────
// AC scripts have no spatial presence — must set up EntityViewer
// before findEntitiesByType will return anything.

var initInterval = Script.setInterval(function () {
    if (Entities.serversExist() && Entities.canRez()) {
        EntityViewer.setPosition(SCAN_CENTER);
        EntityViewer.setCenterRadius(SCAN_RADIUS);
        EntityViewer.queryOctree();
        Script.clearInterval(initInterval);

        Script.setTimeout(function () {
            isReady = true;
            print("[Manifold] EntityViewer ready");
            connect();
            // Re-query octree periodically so newly spawned entities stay visible
            heartbeatTimer = Script.setInterval(function () {
                EntityViewer.queryOctree();
            }, 10000);
        }, 2000);
    }
}, 500);

// ── entity cleanup ───────────────────────────────────────────────

function deleteClonesFor(username) {
    if (!isReady) return;
    EntityViewer.queryOctree();
    Script.setTimeout(function () {
        // Use untyped findEntities to avoid type-specific visibility issues
        // (Box entities are not always returned by findEntitiesByType)
        var ids = Entities.findEntities(SCAN_CENTER, SCAN_RADIUS);
        var deleted = 0;
        ids.forEach(function (id) {
            try {
                var ud = JSON.parse(Entities.getEntityProperties(id, ["userData"]).userData || "{}");
                if (ud.manifoldClone && ud.owner === username) {
                    Entities.deleteEntity(id);
                    deleted++;
                }
            } catch (e) {}
        });
        print("[Manifold] deleted " + deleted + " clone(s) for " + username);
    }, 1000);
}

// ── websocket ────────────────────────────────────────────────────

function connect() {
    print("[Manifold] Connecting to " + WS_URL);
    ws = new WebSocket(WS_URL);

    ws.onopen = function () {
        print("[Manifold] Connected");
        if (reconnectTimer) {
            Script.clearTimeout(reconnectTimer);
            reconnectTimer = null;
        }
    };

    ws.onmessage = function (event) {
        try {
            var msg = JSON.parse(event.data);
            switch (msg.type) {
                case "user:connected":
                    print("[Manifold] >>> " + msg.username + " joined");
                    break;
                case "user:disconnected":
                    print("[Manifold] <<< " + msg.username + " left");
                    deleteClonesFor(msg.username);
                    break;
                case "recall":
                    print("[Manifold] recall for " + msg.username);
                    deleteClonesFor(msg.username);
                    break;
            }
        } catch (e) {
            print("[Manifold] message parse error: " + e);
        }
    };

    ws.onerror = function (err) {
        print("[Manifold] WS error: " + JSON.stringify(err));
    };

    ws.onclose = function (event) {
        print("[Manifold] Disconnected (code=" + event.code + "). Reconnecting in 5s...");
        ws = null;
        reconnectTimer = Script.setTimeout(connect, 5000);
    };
}

// ── lifecycle ────────────────────────────────────────────────────

Script.scriptEnding.connect(function () {
    print("[Manifold] script ending");
    if (reconnectTimer) { Script.clearTimeout(reconnectTimer); }
    if (heartbeatTimer) { Script.clearInterval(heartbeatTimer); }
    if (ws) { ws.close(); ws = null; }
});
