"""JavaScript generator for touch/swipe to navigation conversion."""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/js/touch.ipynb.

# %% auto #0
__all__ = ['TOUCH_SWIPE_THRESHOLD', 'TOUCH_MOMENTUM_MIN_VELOCITY', 'TOUCH_MOMENTUM_FRICTION', 'TOUCH_VELOCITY_SAMPLES',
           'generate_touch_nav_js']

# %% ../../nbs/js/touch.ipynb #5qfafr5ofv4
from typing import Tuple

from ..core.html_ids import VirtualCollectionHtmlIds
from ..core.button_ids import VirtualCollectionButtonIds
from ..core.models import VirtualCollectionUrls

# %% ../../nbs/js/touch.ipynb #m3j3hwj09f8
TOUCH_SWIPE_THRESHOLD: int = 30        # Minimum px for a quick swipe to trigger one nav step
TOUCH_MOMENTUM_MIN_VELOCITY: float = 0.5  # Minimum px/ms velocity at touchend to trigger momentum
TOUCH_MOMENTUM_FRICTION: float = 0.95  # Per-frame (16ms) velocity decay multiplier during momentum
TOUCH_VELOCITY_SAMPLES: int = 5        # Number of recent touchmove samples for velocity estimation

# %% ../../nbs/js/touch.ipynb #g7z71ls89gv
def generate_touch_nav_js(
    ids: VirtualCollectionHtmlIds,       # HTML IDs for this collection
    button_ids: VirtualCollectionButtonIds,  # Button IDs for nav triggers (unused, kept for API consistency)
    urls: VirtualCollectionUrls,         # URL bundle for direct HTMX ajax calls
    step_distance: int = TOUCH_SWIPE_THRESHOLD,  # Drag distance in px to trigger one nav step
    disable_in_modes: Tuple[str, ...] = (),  # Mode names where touch is suppressed
) -> str:  # JavaScript IIFE
    """Generate JS for touch gesture to navigation conversion."""
    if disable_in_modes:
        modes_array = ', '.join(f"'{m}'" for m in disable_in_modes)
        mode_check = f"""
        function _isTouchDisabled() {{
            if (typeof window.kbNav !== 'undefined') {{
                const state = window.kbNav.getState();
                const disabledModes = [{modes_array}];
                return state && disabledModes.includes(state.currentMode);
            }}
            return false;
        }}
        """
        mode_guard = "if (_isTouchDisabled()) return;"
        momentum_mode_guard = (
            "if (typeof _isTouchDisabled === 'function' && _isTouchDisabled()) "
            "{ _touchState.momentumId = null; return; }"
        )
    else:
        mode_check = ""
        mode_guard = ""
        momentum_mode_guard = ""

    return f"""(function() {{
        // === Touch Navigation ===
        // Uses nav_to_index for scroll-only navigation (no cursor movement).
        // Reads window_start from hidden input to compute target index.
        const _touchState = {{
            active: false,
            primaryId: null,
            startY: 0,
            startX: 0,
            lastY: 0,
            lastStepY: 0,
            stepDistance: {step_distance},
            isNavigating: false,
            stepsTriggered: 0,
            history: [],
            momentumId: null,
            momentumAccum: 0,
        }};
        const _TOUCH_SWIPE_THRESHOLD = {TOUCH_SWIPE_THRESHOLD};
        const _TOUCH_MOMENTUM_MIN_VEL = {TOUCH_MOMENTUM_MIN_VELOCITY};
        const _TOUCH_MOMENTUM_FRICTION = {TOUCH_MOMENTUM_FRICTION};
        const _TOUCH_VEL_SAMPLES = {TOUCH_VELOCITY_SAMPLES};
        {mode_check}
        function _stopMomentum() {{
            if (_touchState.momentumId) {{
                cancelAnimationFrame(_touchState.momentumId);
                _touchState.momentumId = null;
            }}
        }}

        function _fireTouchNav(direction) {{
            const wsInput = document.getElementById('{ids.window_start_input}');
            if (!wsInput) return;
            const ws = parseInt(wsInput.value, 10);
            const target = direction === 'down' ? ws + 1 : ws - 1;
            htmx.ajax('POST', '{urls.nav_to_index}?target_index=' + target, {{swap: 'none'}});
        }}

        function _setupTouchNav() {{
            const el = document.getElementById('{ids.collection}');
            if (!el) return;

            if (el._touchNavAbort) el._touchNavAbort.abort();
            const controller = new AbortController();
            el._touchNavAbort = controller;
            const sig = {{ signal: controller.signal }};

            el.addEventListener('pointerdown', function(evt) {{
                if (evt.pointerType !== 'touch') return;
                {mode_guard}
                _stopMomentum();

                _touchState.primaryId = evt.pointerId;
                _touchState.active = true;
                _touchState.startY = evt.clientY;
                _touchState.startX = evt.clientX;
                _touchState.lastY = evt.clientY;
                _touchState.lastStepY = evt.clientY;
                _touchState.isNavigating = false;
                _touchState.stepsTriggered = 0;
                _touchState.history = [];
            }}, sig);

            el.addEventListener('pointermove', function(evt) {{
                if (evt.pointerType !== 'touch') return;
                if (!_touchState.active || evt.pointerId !== _touchState.primaryId) return;
                {mode_guard}

                const deltaY = evt.clientY - _touchState.startY;
                const deltaX = evt.clientX - _touchState.startX;

                if (!_touchState.isNavigating) {{
                    const totalDist = Math.abs(deltaY) + Math.abs(deltaX);
                    if (totalDist < 10) return;
                    if (Math.abs(deltaX) > Math.abs(deltaY)) {{
                        _touchState.active = false;
                        return;
                    }}
                    _touchState.isNavigating = true;
                    try {{ el.setPointerCapture(evt.pointerId); }} catch (e) {{}}
                }}

                evt.preventDefault();

                _touchState.history.push({{ t: evt.timeStamp, y: evt.clientY }});
                if (_touchState.history.length > _TOUCH_VEL_SAMPLES) {{
                    _touchState.history.shift();
                }}
                _touchState.lastY = evt.clientY;

                const stepDelta = evt.clientY - _touchState.lastStepY;
                if (Math.abs(stepDelta) >= _touchState.stepDistance) {{
                    const dir = stepDelta < 0 ? 'down' : 'up';
                    _fireTouchNav(dir);
                    _touchState.lastStepY += (stepDelta < 0 ? -1 : 1) * _touchState.stepDistance;
                    _touchState.stepsTriggered++;
                }}
            }}, sig);

            el.addEventListener('pointerup', function(evt) {{
                if (evt.pointerType !== 'touch') return;
                if (!_touchState.active || evt.pointerId !== _touchState.primaryId) return;
                _touchState.active = false;

                if (!_touchState.isNavigating) return;

                let velocity = 0;
                const hist = _touchState.history;
                if (hist.length >= 2) {{
                    const first = hist[0];
                    const last = hist[hist.length - 1];
                    const dt = Math.max(1, last.t - first.t);
                    velocity = (last.y - first.y) / dt;
                }}

                if (_touchState.stepsTriggered === 0) {{
                    const totalDelta = _touchState.lastY - _touchState.startY;
                    if (Math.abs(totalDelta) >= _TOUCH_SWIPE_THRESHOLD) {{
                        _fireTouchNav(totalDelta < 0 ? 'down' : 'up');
                    }}
                    return;
                }}

                const absVel = Math.abs(velocity);
                if (absVel >= _TOUCH_MOMENTUM_MIN_VEL) {{
                    const dir = velocity < 0 ? 'down' : 'up';
                    let curVel = absVel;
                    let lastFrame = performance.now();
                    const stepDist = _touchState.stepDistance;
                    _touchState.momentumAccum = 0;

                    function momentumTick(now) {{
                        {momentum_mode_guard}
                        const dt = now - lastFrame;
                        lastFrame = now;
                        curVel *= Math.pow(_TOUCH_MOMENTUM_FRICTION, dt / 16);
                        _touchState.momentumAccum += curVel * dt;

                        if (_touchState.momentumAccum >= stepDist) {{
                            _touchState.momentumAccum -= stepDist;
                            _fireTouchNav(dir);
                        }}

                        if (curVel >= _TOUCH_MOMENTUM_MIN_VEL * 0.1) {{
                            _touchState.momentumId = requestAnimationFrame(momentumTick);
                        }} else {{
                            _touchState.momentumId = null;
                        }}
                    }}

                    _touchState.momentumId = requestAnimationFrame(momentumTick);
                }}
            }}, sig);

            el.addEventListener('pointercancel', function(evt) {{
                if (evt.pointerType !== 'touch') return;
                if (evt.pointerId === _touchState.primaryId) {{
                    _touchState.active = false;
                    _touchState.isNavigating = false;
                    _stopMomentum();
                }}
            }}, sig);
        }}

        _setupTouchNav();
        document.body.addEventListener('htmx:afterSettle', function(evt) {{
            _setupTouchNav();
        }});
    }})();"""
