// Room scroll-of-events behaviors (the table-hex aperture's 2nd snap pane):
//   1. animate the "What happens next . . ?" buffer dots,
//   2. swap the gear menu by scroll position — the hex view keeps the default
//      NVM/DEL/BYE pane; scrolled to the feed it shows the Frame/Redact filter,
//   3. the Frame/Redact filter itself (per-room localStorage; mirrors the
//      Billscroll page's scroll.html filter so the two surfaces behave alike),
//   4. live async refresh — re-fetch the feed on a `scroll_update` WS nudge so
//      the SCROLL applet updates without a page reload.
// All guards no-op on surfaces without a scroll pane (gate phase, room_gate).
(function () {
    if (!document.getElementById('id_drama_scroll')) return;

    var page = document.querySelector('.room-page');
    var STORAGE_KEY = 'room-scroll-labels-' + (page ? page.dataset.roomId : '');
    var _dotsTimer = null;

    // 1 ── buffer dots (re-runnable — restarted on the fresh nodes after an
    //      async feed swap; clears any prior interval so it never doubles up).
    function startDots() {
        if (_dotsTimer) { clearInterval(_dotsTimer); _dotsTimer = null; }
        var scroll = document.getElementById('id_drama_scroll');
        var dotsWrap = scroll && scroll.querySelector('.scroll-buffer-dots');
        if (!dotsWrap) return;
        var dots = dotsWrap.querySelectorAll('span');
        var n = 0;
        _dotsTimer = setInterval(function () {
            dots.forEach(function (d, i) {
                d.textContent = i < n ? (i === 3 ? '?' : '.') : '';
            });
            n = (n + 1) % 5;
        }, 400);
    }
    startDots();

    // 3 ── Frame/Redact filter (re-applied after async swaps). `applyFilter`
    //      re-queries the CURRENT #id_drama_scroll so it works on swapped nodes.
    function savedLabels() {
        try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null'); }
        catch (_) { return null; }   // localStorage unavailable — non-fatal
    }
    function applyFilter(checked) {
        if (!checked) return;
        var scroll = document.getElementById('id_drama_scroll');
        if (!scroll) return;
        scroll.querySelectorAll('.drama-event[data-label]').forEach(function (el) {
            el.style.display = checked.indexOf(el.dataset.label) !== -1 ? '' : 'none';
        });
    }
    function applySavedFilter() {
        var checked = savedLabels();
        if (checked) applyFilter(checked);
    }
    applySavedFilter();

    // 4 ── live async refresh ────────────────────────────────────────────────
    // RoomConsumer relays `scroll_update` after ANY GameEvent is recorded
    // (drama.models.record → on_commit broadcast). Re-fetch just the feed
    // partial + swap #id_drama_scroll, then re-apply the saved redact filter and
    // restart the buffer dots on the fresh nodes. No-ops if the page has no
    // scroll-status URL (non-table surfaces).
    function refreshScroll() {
        var url = page && page.dataset.scrollStatusUrl;
        var cur = document.getElementById('id_drama_scroll');
        if (!url || !cur) return;
        fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } })
            .then(function (r) { return r.text(); })
            .then(function (html) {
                var node = document.getElementById('id_drama_scroll');
                if (node) node.outerHTML = html;
                applySavedFilter();
                startDots();
            })
            .catch(function () { /* transient — next nudge or a refresh recovers */ });
    }
    window.addEventListener('room:scroll_update', refreshScroll);

    // 2 ── scroll-driven title reel ─────────────────────────────────────────
    // The page title (h2) is a reel — toggling `.is-scroll` slides it GAME ROOM
    // ⇄ GAME <view>. The gear-menu pane swap used to live here too, but the
    // per-view gear (SCROLL/ATLAS menus, disabled YARN/POST/PULSE) is now owned
    // by room-views.js, which knows the active reelhouse view.
    var aperture = document.getElementById('id_room_aperture');
    var scrollSection = aperture && aperture.querySelector('.room-scroll-pane');
    var title = document.querySelector('.row .col-lg-6 h2');
    if (aperture && scrollSection) {
        var io = new IntersectionObserver(function (entries) {
            entries.forEach(function (e) {
                if (e.target !== scrollSection) return;
                // Title reel: GAME ROOM ⇄ GAME SCROLL (CSS slides the words).
                if (title) title.classList.toggle('is-scroll', e.intersectionRatio >= 0.5);
            });
        }, { root: aperture, threshold: [0, 0.5, 1] });
        io.observe(scrollSection);
    }

    // 3 (cont.) ── Frame/Redact filter form ─────────────────────────────────
    var form = document.getElementById('id_scroll_filter_form');
    if (!form) return;
    syncCheckboxes(savedLabels());

    function syncCheckboxes(checked) {
        if (!checked) return;
        form.querySelectorAll('input[name="labels"]').forEach(function (cb) {
            cb.checked = checked.indexOf(cb.value) !== -1;
        });
    }

    form.addEventListener('submit', function (e) {
        e.preventDefault();
        var checked = Array.prototype.slice.call(
            form.querySelectorAll('input[name="labels"]:checked')
        ).map(function (cb) { return cb.value; });
        applyFilter(checked);
        try { localStorage.setItem(STORAGE_KEY, JSON.stringify(checked)); } catch (_) {}
        // Close the gear menu (mirror applets.js close logic).
        if (roomMenu) roomMenu.style.display = 'none';
        var gear = document.querySelector(
            ".gear-btn[data-menu-target='id_room_menu']"
        );
        if (gear) gear.classList.remove('active');
    });
}());
