// ═══════════════════════════════════════════════════════════════════
//  Extra chart types — Sparkline, Area, Scatter, Pie, StackedBar
// ═══════════════════════════════════════════════════════════════════
//
//  Companions to NeonBarChart / NeonLineChart in chart.slint. All
//  share the same min-deps philosophy: input is a flat array of
//  numbers (or simple structs), no axis machinery, no animation
//  sweep — just data into pixels.
//
//  When to use which:
//    • NeonSparkline   — inline trend in a row (KPI card, status bar).
//                        No axes, no labels, just the curve.
//    • NeonAreaChart   — same as line but filled. Lower visual
//                        weight than bars; good for cumulative metrics.
//    • NeonScatterChart — uncorrelated x/y points. Use when neither
//                        axis is "time" / sequential.
//    • NeonPieChart    — categorical share. Host must pre-compute
//                        cumulative fractions (Slint can't sum
//                        inside `for`).
//    • NeonStackedBar  — horizontal "linear pie". Easier to read
//                        than a pie at small sizes; same data shape.
//
//  Theme tokens drive colors by default. Each chart accepts an
//  `accent` (single line/area) or `palette` (per-segment) override
//  so the host can colour-code semantically.
//
// ═══════════════════════════════════════════════════════════════════

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

// ─────────────────────────────────────────────────────────────────────
// NeonSparkline — compact line, no chrome
// ─────────────────────────────────────────────────────────────────────
export component NeonSparkline inherits Rectangle {
    in property <[float]> values;
    /// Y-axis scale. Defaults to 100; for percent metrics keep the
    /// default, for arbitrary ranges set this to your max value.
    in property <float> max-val: 100.0;
    in property <color> line-color: Theme.neon-cyan;
    in property <color> fill-color: Theme.glow-cyan;
    /// Show the last point as a highlighted dot. Useful in KPI
    /// cards where the latest reading is the headline.
    in property <bool> show-last-dot: true;
    in property <bool> show-fill: true;
    in property <length> stroke-thickness: 1.5px;

    property <int> n: max(root.values.length, 1);

    background: transparent;

    // Filled area (trapezoids). Slint can't chain `if : for` inline,
    // so we wrap the loop in an `if`-guarded Rectangle. The loop's
    // own `visible` flag handles the per-segment "is there a next
    // point?" check.
    if (root.show-fill && root.n > 1): Rectangle {
        background: transparent;
        for seg[idx] in root.values: Rectangle {
            visible: idx < root.n - 1;
            property <float> v0: clamp(root.values[idx] / root.max-val, 0.0, 1.0);
            property <float> v1: clamp(root.values[idx + 1] / root.max-val, 0.0, 1.0);
            property <float> top-v: max(v0, v1);
            property <length> seg-w: parent.width / (root.n - 1);
            x: seg-w * idx;
            width: seg-w;
            y: (1.0 - top-v) * parent.height;
            height: top-v * parent.height;
            background: root.fill-color;
        }
    }

    // Line stroke approximated as connected dots — Slint has no
    // line-segment primitive between two arbitrary points without
    // Path geometry. Two dots per segment provide visual continuity
    // at low data densities; at higher densities the dots overlap.
    for pt[idx] in root.values: Rectangle {
        x: (parent.width / max(root.n - 1, 1)) * idx - root.stroke-thickness;
        y: (1.0 - clamp(root.values[idx] / root.max-val, 0.0, 1.0)) * parent.height
           - root.stroke-thickness;
        width: 2 * root.stroke-thickness;
        height: 2 * root.stroke-thickness;
        border-radius: root.stroke-thickness;
        background: root.line-color;
    }

    // Highlighted last point
    if (root.show-last-dot && root.n > 0): Rectangle {
        x: parent.width - 5px;
        y: (1.0 - clamp(root.values[root.n - 1] / root.max-val, 0.0, 1.0))
           * parent.height - 5px;
        width: 10px;
        height: 10px;
        border-radius: 5px;
        background: root.line-color;
        border-width: 2px;
        border-color: Theme.bg-base;
    }
}

// ─────────────────────────────────────────────────────────────────────
// NeonAreaChart — filled curve with grid
// ─────────────────────────────────────────────────────────────────────
export component NeonAreaChart inherits Rectangle {
    in property <[float]> values;
    in property <[string]> labels;
    in property <length> chart-height: 160px;
    in property <color> line-color: Theme.neon-purple;
    in property <color> fill-color: Theme.glow-purple;
    in property <float> max-val: 100.0;
    in property <int> grid-lines: 5;

    property <int> n: max(root.values.length, 1);

    background: transparent;

    VerticalLayout {
        spacing: 2px;
        Rectangle {
            height: root.chart-height;
            // Grid lines (horizontal)
            for gl in [1, 2, 3, 4, 5]: Rectangle {
                y: (parent.height / 4.0) * (gl - 1);
                height: 1px;
                width: 100%;
                background: Theme.border;
                opacity: 0.4;
            }
            // Filled trapezoids (segment by segment)
            for seg[idx] in root.values: Rectangle {
                visible: idx < root.n - 1;
                property <float> v0: clamp(root.values[idx] / root.max-val, 0.0, 1.0);
                property <float> v1: clamp(root.values[idx + 1] / root.max-val, 0.0, 1.0);
                property <float> top-v: max(v0, v1);
                property <length> seg-w: parent.width / (root.n - 1);
                x: seg-w * idx;
                width: seg-w;
                y: (1.0 - top-v) * parent.height;
                height: top-v * parent.height;
                background: root.fill-color;
            }
            // Top edge dots (line approximation)
            for pt[idx] in root.values: Rectangle {
                x: (parent.width / max(root.n - 1, 1)) * idx - 2px;
                y: (1.0 - clamp(root.values[idx] / root.max-val, 0.0, 1.0))
                   * parent.height - 2px;
                width: 4px;
                height: 4px;
                border-radius: 2px;
                background: root.line-color;
            }
        }
        // Labels row
        if (root.labels.length > 0): HorizontalLayout {
            for lbl in root.labels: Text {
                text: lbl;
                font-size: Theme.font-xs;
                color: Theme.text-dim;
                horizontal-alignment: center;
                overflow: elide;
            }
        }
    }
}

// ─────────────────────────────────────────────────────────────────────
// NeonScatterChart — x/y dots, no connection
// ─────────────────────────────────────────────────────────────────────
export struct ScatterPoint {
    x: float,    // 0..max-x
    y: float,    // 0..max-y
    /// Optional per-point colour. When `accent.alpha == 0` the
    /// chart's `dot-color` is used.
    accent: color,
}

export component NeonScatterChart inherits Rectangle {
    in property <[ScatterPoint]> points;
    in property <float> max-x: 100.0;
    in property <float> max-y: 100.0;
    in property <length> chart-height: 200px;
    in property <color> dot-color: Theme.neon-green;
    in property <length> dot-size: 6px;
    in property <bool> show-grid: true;

    background: transparent;

    Rectangle {
        height: root.chart-height;
        // Grid
        if (root.show-grid): for gx in [1, 2, 3, 4]: Rectangle {
            x: (parent.width / 4.0) * gx;
            width: 1px;
            height: 100%;
            background: Theme.border;
            opacity: 0.3;
        }
        if (root.show-grid): for gy in [1, 2, 3, 4]: Rectangle {
            y: (parent.height / 4.0) * gy;
            height: 1px;
            width: 100%;
            background: Theme.border;
            opacity: 0.3;
        }
        // Points
        for pt in root.points: Rectangle {
            x: clamp(pt.x / root.max-x, 0.0, 1.0)
               * (parent.width - root.dot-size);
            y: (1.0 - clamp(pt.y / root.max-y, 0.0, 1.0))
               * (parent.height - root.dot-size);
            width: root.dot-size;
            height: root.dot-size;
            border-radius: root.dot-size / 2;
            background: pt.accent.alpha > 0 ? pt.accent : root.dot-color;
            border-width: 1px;
            border-color: Theme.bg-base;
        }
    }
}

// ─────────────────────────────────────────────────────────────────────
// NeonPieChart — categorical share via Path arcs
// ─────────────────────────────────────────────────────────────────────
//
// Caller pre-computes cumulative `start-fraction` / `end-fraction`
// (0..1) per slice — Slint can't run a cumulative sum inside a
// `for` loop. Format:
//
//   slices: [
//       { start-fraction: 0.0, end-fraction: 0.4, color: red, label: "A" },
//       { start-fraction: 0.4, end-fraction: 0.7, color: blue, label: "B" },
//       { start-fraction: 0.7, end-fraction: 1.0, color: green, label: "C" },
//   ]
//
// Path coordinates are in local units (no `px`). We pin the chart
// to a 200×200 box internally (0..200 coord space) and let the
// host control the `chart-size` rectangle that wraps it — Slint
// scales the Path with the parent.
//
// Note: Slint Path's `sweep` follows SVG semantics. We use
// sweep: false because screen-y inverts the trig direction.
//
export struct PieSlice {
    start-fraction: float,
    end-fraction: float,
    accent: color,
    label: string,
}

export component NeonPieChart inherits Rectangle {
    in property <[PieSlice]> slices;
    in property <length> chart-size: 200px;
    /// Inner cutout radius as a fraction of the outer radius.
    /// 0.0 → full pie; 0.5 → donut; 0.7 → thin ring.
    in property <float> donut-fraction: 0.0;

    width: root.chart-size;
    height: root.chart-size;
    background: transparent;

    // Outer ring slices — each Path covers the full chart bounds.
    for slice in root.slices: Path {
        width: parent.width;
        height: parent.height;
        viewbox-width: 200;
        viewbox-height: 200;
        fill: slice.accent;

        property <float> a0: slice.start-fraction * 6.283185307 - 1.5707963;
        property <float> a1: slice.end-fraction   * 6.283185307 - 1.5707963;
        property <float> r: 90;
        property <float> cx: 100;
        property <float> cy: 100;
        property <bool> large: (slice.end-fraction - slice.start-fraction) > 0.5;

        MoveTo { x: parent.cx; y: parent.cy; }
        LineTo {
            x: parent.cx + parent.r * cos(parent.a0);
            y: parent.cy + parent.r * sin(parent.a0);
        }
        ArcTo {
            radius-x: parent.r;
            radius-y: parent.r;
            x-rotation: 0;
            large-arc: parent.large;
            sweep: true;
            x: parent.cx + parent.r * cos(parent.a1);
            y: parent.cy + parent.r * sin(parent.a1);
        }
        LineTo { x: parent.cx; y: parent.cy; }
        Close { }
    }

    // Inner donut hole — overlay a circle in bg-base on top to
    // produce the cutout. Skipped when donut-fraction == 0.
    if (root.donut-fraction > 0.0): Rectangle {
        width: parent.width * root.donut-fraction;
        height: parent.height * root.donut-fraction;
        x: (parent.width - self.width) / 2;
        y: (parent.height - self.height) / 2;
        border-radius: self.width / 2;
        background: Theme.bg-base;
    }
}

// ─────────────────────────────────────────────────────────────────────
// NeonStackedBar — single horizontal bar split into segments
// ─────────────────────────────────────────────────────────────────────
//
// "Linear pie" — visually cleaner than a pie at small sizes.
// Same input shape as NeonPieChart minus the donut fraction.
//
export component NeonStackedBar inherits Rectangle {
    in property <[PieSlice]> slices;
    in property <length> bar-height: 20px;
    in property <length> radius: 10px;
    in property <bool> show-legend: false;

    background: transparent;

    VerticalLayout {
        spacing: Theme.sp-2;

        Rectangle {
            height: root.bar-height;
            background: Theme.bg-elevated;
            border-radius: root.radius;
            clip: true;
            for slice in root.slices: Rectangle {
                x: slice.start-fraction * parent.width;
                width: (slice.end-fraction - slice.start-fraction) * parent.width;
                height: parent.height;
                background: slice.accent;
            }
        }

        if (root.show-legend): HorizontalLayout {
            spacing: Theme.sp-3;
            alignment: start;
            for slice in root.slices: HorizontalLayout {
                spacing: 4px;
                Rectangle {
                    width: 10px;
                    height: 10px;
                    border-radius: 2px;
                    background: slice.accent;
                    y: 4px;
                }
                Text {
                    text: slice.label + " · "
                        + round((slice.end-fraction - slice.start-fraction) * 100.0)
                        + "%";
                    font-size: Theme.font-xs;
                    color: Theme.text-muted;
                    vertical-alignment: center;
                }
            }
        }
    }
}
