// ═══════════════════════════════════════════════════════════════════
//  Skeleton — animated loading placeholders + connected wrapper
// ═══════════════════════════════════════════════════════════════════
//
//  Three components ship from this file:
//
//    NeonSkeleton          — base shimmer block. Variants: rect /
//                            line / circle. The shimmer animates
//                            via a moving translucent gradient
//                            stripe (Slint can't tween linear-
//                            gradient stops, so we approximate with
//                            an animated x-offset Rectangle on top).
//    NeonSkeletonText      — convenience wrapper that stacks N
//                            line skeletons of varied widths to
//                            mimic a paragraph.
//    NeonSkeletonGate      — host-aware wrapper: shows a skeleton
//                            placeholder while `loading` is true,
//                            otherwise renders @children. One-stop
//                            wiring to host state ("connected").
//
//  When to use:
//    • While fetching IFC data, vector imports, project loads —
//      anywhere a panel becomes useful only after a slow load.
//    • Inside table rows when paginating to a fresh page.
//    • As a placeholder card stack in a "library" view.
//
// ═══════════════════════════════════════════════════════════════════

import { Theme } from "../theme.slint";

// ─────────────────────────────────────────────────────────────────────
// NeonSkeleton — base shimmer block
// ─────────────────────────────────────────────────────────────────────
export component NeonSkeleton inherits Rectangle {
    /// `"rect"` (default), `"line"`, or `"circle"`. `line` ⇒ shorter
    /// fixed height useful for inline text placeholders;
    /// `circle` ⇒ avatar/badge placeholder (width == height).
    in property <string> variant: "rect";
    in property <length> skeleton-width: 200px;
    in property <length> skeleton-height: 16px;
    in property <bool> animated: true;
    /// When the variant is `circle`, this overrides both width
    /// and height (circle is square).
    in property <length> circle-size: 40px;

    width: root.variant == "circle"
        ? root.circle-size
        : root.skeleton-width;
    height: root.variant == "circle"
        ? root.circle-size
        : (root.variant == "line" ? 12px : root.skeleton-height);
    background: Theme.bg-hover;
    border-radius: root.variant == "circle"
        ? root.circle-size / 2
        : (root.variant == "line" ? 6px : Theme.radius-sm);
    clip: true;

    // Shimmer stripe — moves across the block left → right. We use
    // an `animate x` cycle that re-fires via a Timer-driven
    // property toggle below.
    if (root.animated): stripe := Rectangle {
        property <bool> at-end: false;
        width: parent.width * 0.4;
        height: parent.height;
        x: stripe.at-end ? parent.width : -self.width;
        animate x {
            duration: 1200ms;
            easing: ease-in-out;
        }
        background: @linear-gradient(
            90deg,
            #ffffff00 0%,
            #ffffff20 50%,
            #ffffff00 100%,
        );
        // Toggle via Timer — Slint's `Timer { running: ...; }`
        // pulses every `interval` ms so the animation re-fires.
        // Without this, x would tween once and stay at the end
        // forever.
        Timer {
            interval: 1200ms;
            running: true;
            triggered => { stripe.at-end = !stripe.at-end; }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────
// NeonSkeletonText — stacked lines mimicking paragraph
// ─────────────────────────────────────────────────────────────────────
export component NeonSkeletonText inherits Rectangle {
    /// Number of lines to render. The last line is rendered
    /// shorter (75 % width) to mimic a paragraph's last line.
    in property <int> lines: 3;
    in property <length> line-spacing: 8px;
    in property <length> line-width: 280px;
    in property <bool> animated: true;

    background: transparent;

    VerticalLayout {
        spacing: root.line-spacing;
        // Slint `for` doesn't loop a fixed integer — we materialise
        // a small iota array via the longest plausible default,
        // and visibility-gate by index < lines so the caller's
        // `lines` value drives the actual count.
        for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]: Rectangle {
            visible: i <= root.lines;
            height: 12px;
            width: i == root.lines
                ? root.line-width * 0.75
                : root.line-width;
            NeonSkeleton {
                variant: "line";
                skeleton-width: parent.width;
                skeleton-height: parent.height;
                animated: root.animated;
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────
// NeonSkeletonGate — toggles between placeholder and content
// ─────────────────────────────────────────────────────────────────────
//
// Caller binds `loading` to host state (e.g. a tokio future that
// resolves the data). While true, the Gate renders a configurable
// placeholder. When false, it swaps in the @children. No fade —
// the swap is instant; if you want a transition wrap @children
// in your own animated Rectangle.
//
// Three default placeholder shapes ship in:
//   placeholder-kind = "card"      → header bar + 3 text lines.
//   placeholder-kind = "list"      → 5 stacked rows with avatar.
//   placeholder-kind = "metric"    → big number block + caption.
// Anything else falls back to a single skeleton-text block, which
// is also what you get when the kind is empty.
//
export component NeonSkeletonGate inherits Rectangle {
    in property <bool> loading: false;
    in property <string> placeholder-kind: "card";
    in property <length> placeholder-width: 320px;

    background: transparent;

    // Real content — ALWAYS in the tree (Slint requires @children
    // to live outside any conditional). We just hide it visually
    // while loading by stacking the placeholder on top.
    Rectangle {
        background: transparent;
        visible: !root.loading;
        VerticalLayout {
            padding: 0px;
            spacing: 0px;
            @children
        }
    }

    // Placeholder overlay — visible only while loading.
    if (root.loading): Rectangle {
        background: Theme.bg-base;
        VerticalLayout {
            padding: Theme.sp-3;
            spacing: Theme.sp-3;

            if (root.placeholder-kind == "card"): VerticalLayout {
                spacing: Theme.sp-2;
                NeonSkeleton {
                    variant: "rect";
                    skeleton-width: root.placeholder-width;
                    skeleton-height: 80px;
                }
                NeonSkeletonText {
                    lines: 3;
                    line-width: root.placeholder-width;
                }
            }

            if (root.placeholder-kind == "list"): VerticalLayout {
                spacing: Theme.sp-2;
                for r in [1, 2, 3, 4, 5]: HorizontalLayout {
                    spacing: Theme.sp-2;
                    NeonSkeleton {
                        variant: "circle";
                        circle-size: 32px;
                    }
                    NeonSkeletonText {
                        lines: 2;
                        line-width: root.placeholder-width - 48px;
                        line-spacing: 4px;
                    }
                }
            }

            if (root.placeholder-kind == "metric"): VerticalLayout {
                spacing: Theme.sp-2;
                NeonSkeleton {
                    variant: "rect";
                    skeleton-width: 140px;
                    skeleton-height: 36px;
                }
                NeonSkeleton {
                    variant: "line";
                    skeleton-width: 80px;
                }
            }

            if (root.placeholder-kind != "card"
                && root.placeholder-kind != "list"
                && root.placeholder-kind != "metric"): NeonSkeletonText {
                lines: 4;
                line-width: root.placeholder-width;
            }
        }
    }
}
