// ═══════════════════════════════════════════════════════════════════
//  NeonColorPicker — HSV picker with readable RGB / hex outputs
// ═══════════════════════════════════════════════════════════════════
//
//  Visual: 2D Saturation × Value field on top of a hue gradient,
//  hue slider underneath, and a numeric readout block. Identical
//  to the previous version BUT with all derived values now
//  exposed as `out property` so the host (Rust) can bind to them.
//
//  Inputs (in-out):
//    hue (0..360), sat (0..1), val (0..1)
//
//  Outputs (out):
//    actual-color   — the live `color` value as a Slint `color`.
//    red, green, blue — 0..255 ints (rounded), readable from Rust.
//    hex            — "#RRGGBB" string (lowercase).
//
//  Signals:
//    changed              — fires on every drag step.
//    committed            — fires on pointer-up (one event per
//                           gesture). Use this to debounce expensive
//                           operations (re-mesh, persist, etc).
//
//  Bidirectional binding: caller can set `set-from-rgb-r/g/b` (with
//  a tick on `apply-rgb`) to push an external RGB into the
//  picker. The component reverse-computes HSV when `apply-rgb`
//  flips. Hex parsing is left to Rust (cheaper than implementing
//  in Slint expressions).
//
// ═══════════════════════════════════════════════════════════════════

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

export component NeonColorPicker inherits Rectangle {
    // ── Inputs ───────────────────────────────────────────────────
    in-out property <float> hue: 180.0;
    in-out property <float> sat: 0.6;
    in-out property <float> val: 0.7;

    // ── External RGB write-back (pull pattern) ────────────────────
    /// Caller writes `set-from-rgb-r/g/b` (each 0..1) and then
    /// flips `apply-rgb` to true to push the value into the
    /// picker. The component reverse-computes HSV and resets
    /// the flag back to false.
    in-out property <bool> apply-rgb: false;
    in-out property <float> set-from-rgb-r: 0.0;
    in-out property <float> set-from-rgb-g: 0.0;
    in-out property <float> set-from-rgb-b: 0.0;

    // ── HSV → RGB intermediate values (private) ──────────────────
    property <float> h6: root.hue / 60.0;
    property <int> sector: mod(floor(root.h6), 6);
    property <float> f: root.h6 - floor(root.h6);
    property <float> p: root.val * (1.0 - root.sat);
    property <float> q: root.val * (1.0 - root.sat * root.f);
    property <float> t: root.val * (1.0 - root.sat * (1.0 - root.f));

    property <float> rs: root.sector == 0 ? root.val
                       : root.sector == 1 ? root.q
                       : root.sector == 2 ? root.p
                       : root.sector == 3 ? root.p
                       : root.sector == 4 ? root.t
                       : root.val;
    property <float> gs: root.sector == 0 ? root.t
                       : root.sector == 1 ? root.val
                       : root.sector == 2 ? root.val
                       : root.sector == 3 ? root.q
                       : root.sector == 4 ? root.p
                       : root.p;
    property <float> bs: root.sector == 0 ? root.p
                       : root.sector == 1 ? root.p
                       : root.sector == 2 ? root.t
                       : root.sector == 3 ? root.val
                       : root.sector == 4 ? root.val
                       : root.q;

    // ── Outputs (readable from Rust + bindable in Slint) ─────────
    out property <color> actual-color: rgb(
        root.rs * 255.0,
        root.gs * 255.0,
        root.bs * 255.0,
    );
    /// Live RGB triplet, integers 0..255. Fastest path for a Rust
    /// host that wants to mirror the picker value into a model.
    out property <int> red:   round(root.rs * 255.0);
    out property <int> green: round(root.gs * 255.0);
    out property <int> blue:  round(root.bs * 255.0);
    /// `"#rrggbb"` lowercase. Built by chained ternaries because
    /// Slint expressions can't loop over digits.
    out property <string> hex:
        "#" + nibble(round(root.rs * 255.0) / 16)
            + nibble(mod(round(root.rs * 255.0), 16))
            + nibble(round(root.gs * 255.0) / 16)
            + nibble(mod(round(root.gs * 255.0), 16))
            + nibble(round(root.bs * 255.0) / 16)
            + nibble(mod(round(root.bs * 255.0), 16));

    /// Pure hue (brightest, most saturated) — used to tint the
    /// 2D Sat×Val field background.
    property <float> ph-r: root.sector == 0 ? 1.0
                         : root.sector == 1 ? 1.0 - root.f
                         : root.sector == 2 ? 0.0
                         : root.sector == 3 ? 0.0
                         : root.sector == 4 ? root.f
                         : 1.0;
    property <float> ph-g: root.sector == 0 ? root.f
                         : root.sector == 1 ? 1.0
                         : root.sector == 2 ? 1.0
                         : root.sector == 3 ? 1.0 - root.f
                         : root.sector == 4 ? 0.0
                         : 0.0;
    property <float> ph-b: root.sector == 0 ? 0.0
                         : root.sector == 1 ? 0.0
                         : root.sector == 2 ? root.f
                         : root.sector == 3 ? 1.0
                         : root.sector == 4 ? 1.0
                         : 1.0 - root.f;
    property <color> pure-hue: rgb(
        root.ph-r * 255.0,
        root.ph-g * 255.0,
        root.ph-b * 255.0,
    );

    // ── Geometry ─────────────────────────────────────────────────
    property <length> fw: 240px;
    property <length> fh: 180px;

    // ── Helper: int 0..15 → hex digit ────────────────────────────
    function nibble(n: int) -> string {
        return n == 0 ? "0" : n == 1 ? "1" : n == 2 ? "2" : n == 3 ? "3"
             : n == 4 ? "4" : n == 5 ? "5" : n == 6 ? "6" : n == 7 ? "7"
             : n == 8 ? "8" : n == 9 ? "9" : n == 10 ? "a" : n == 11 ? "b"
             : n == 12 ? "c" : n == 13 ? "d" : n == 14 ? "e" : "f";
    }

    // ── Signals ──────────────────────────────────────────────────
    callback changed;
    callback committed;

    // ── External RGB → HSV reverse pull ──────────────────────────
    // When `apply-rgb` flips to true, recompute HSV from the
    // provided r/g/b and reset the flag. The host pattern is:
    //   1. Set set-from-rgb-r/g/b.
    //   2. Set apply-rgb = true.
    //   3. Picker re-derives hue/sat/val and snaps the UI.
    changed apply-rgb => {
        if (root.apply-rgb) {
            // Compute HSV from RGB inline. Slint doesn't support
            // local `let`, so we use intermediate properties below.
            root.hue = derive-hue-from-rgb;
            root.sat = derive-sat-from-rgb;
            root.val = derive-val-from-rgb;
            root.apply-rgb = false;
        }
    }

    property <float> rgb-max: max(max(root.set-from-rgb-r, root.set-from-rgb-g), root.set-from-rgb-b);
    property <float> rgb-min: min(min(root.set-from-rgb-r, root.set-from-rgb-g), root.set-from-rgb-b);
    property <float> rgb-delta: root.rgb-max - root.rgb-min;
    property <float> derive-val-from-rgb: root.rgb-max;
    property <float> derive-sat-from-rgb: root.rgb-max == 0
        ? 0.0 : root.rgb-delta / root.rgb-max;
    property <float> derive-hue-from-rgb:
        root.rgb-delta == 0 ? 0.0
        : root.rgb-max == root.set-from-rgb-r
            ? mod(60.0 * ((root.set-from-rgb-g - root.set-from-rgb-b) / root.rgb-delta) + 360.0, 360.0)
        : root.rgb-max == root.set-from-rgb-g
            ? 60.0 * ((root.set-from-rgb-b - root.set-from-rgb-r) / root.rgb-delta) + 120.0
            : 60.0 * ((root.set-from-rgb-r - root.set-from-rgb-g) / root.rgb-delta) + 240.0;

    // ── Render ───────────────────────────────────────────────────
    VerticalLayout { spacing: Theme.sp-3;
        // Preview swatch
        Rectangle {
            width: root.fw;
            height: 48px;
            border-radius: Theme.radius-md;
            border-width: 1px;
            border-color: Theme.border;
            background: root.actual-color;
        }

        // Sat × Val field
        Rectangle {
            width: root.fw;
            height: root.fh;
            border-radius: Theme.radius-md;
            border-width: 1px;
            border-color: Theme.border;
            clip: true;
            background: root.pure-hue;
            // Saturation columns (white → transparent)
            for ci in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]: Rectangle {
                x: (root.fw / 10.0) * (ci - 1);
                width: root.fw / 10.0;
                height: root.fh;
                background: rgba(255, 255, 255, 1.0 - (ci - 0.5) / 10.0);
            }
            // Value rows (transparent → black)
            for bi in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]: Rectangle {
                y: (root.fh / 10.0) * (bi - 1);
                height: root.fh / 10.0;
                width: root.fw;
                background: rgba(0, 0, 0, (bi - 0.5) / 10.0);
            }
            // Position cursor
            Rectangle {
                x: root.sat * (root.fw - 14px);
                y: (1.0 - root.val) * (root.fh - 14px);
                width: 14px;
                height: 14px;
                border-radius: 7px;
                border-width: 2px;
                border-color: #ffffff;
                background: root.actual-color;
            }
            ft := TouchArea {
                moved => {
                    root.sat = clamp(ft.mouse-x / ft.width, 0.0, 1.0);
                    root.val = clamp(1.0 - ft.mouse-y / ft.height, 0.0, 1.0);
                    root.changed();
                }
                pointer-event(e) => {
                    if (e.kind == PointerEventKind.up) {
                        root.committed();
                    }
                }
                mouse-cursor: crosshair;
            }
        }

        // Hue slider (48 segments)
        Rectangle {
            width: root.fw;
            height: 16px;
            border-radius: Theme.radius-sm;
            border-width: 1px;
            border-color: Theme.border;
            clip: true;
            HorizontalLayout {
                spacing: 0;
                for step in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
                             21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,
                             37,38,39,40,41,42,43,44,45,46,47,48]: Rectangle {
                    horizontal-stretch: 1;
                    property <float> seg-h: ((step - 0.5) / 48.0) * 360.0;
                    property <float> sh6: seg-h / 60.0;
                    property <int> sec: mod(floor(sh6), 6);
                    property <float> sf: sh6 - floor(sh6);
                    property <float> sr: sec == 0 ? 1.0 : sec == 1 ? (1.0 - sf)
                                       : sec == 2 ? 0.0 : sec == 3 ? 0.0
                                       : sec == 4 ? sf  : 1.0;
                    property <float> sg: sec == 0 ? sf  : sec == 1 ? 1.0
                                       : sec == 2 ? 1.0 : sec == 3 ? (1.0 - sf)
                                       : sec == 4 ? 0.0 : 0.0;
                    property <float> sb: sec == 0 ? 0.0 : sec == 1 ? 0.0
                                       : sec == 2 ? sf  : sec == 3 ? 1.0
                                       : sec == 4 ? 1.0 : (1.0 - sf);
                    background: rgb(sr * 255.0, sg * 255.0, sb * 255.0);
                }
            }
            Rectangle {
                x: (root.hue / 360.0) * (parent.width - 16px);
                width: 16px;
                height: 16px;
                border-radius: 8px;
                border-width: 2px;
                border-color: #ffffff;
                background: root.pure-hue;
            }
            ht := TouchArea {
                moved => {
                    root.hue = clamp(ht.mouse-x / ht.width, 0.0, 1.0) * 360.0;
                    root.changed();
                }
                pointer-event(e) => {
                    if (e.kind == PointerEventKind.up) {
                        root.committed();
                    }
                }
                mouse-cursor: pointer;
            }
        }

        // Numeric readout
        Rectangle {
            width: root.fw;
            border-radius: Theme.radius-md;
            border-width: 1px;
            border-color: Theme.border;
            background: Theme.bg-surface;
            VerticalLayout {
                padding: Theme.sp-2;
                spacing: Theme.sp-1;
                HorizontalLayout {
                    spacing: Theme.sp-3;
                    Text {
                        text: "RGB";
                        font-size: 10px;
                        font-weight: 600;
                        color: Theme.neon-cyan;
                        min-width: 36px;
                    }
                    Text {
                        text: "(" + root.red + ", " + root.green + ", " + root.blue + ")";
                        font-size: Theme.font-sm;
                        color: Theme.text-primary;
                    }
                }
                HorizontalLayout {
                    spacing: Theme.sp-3;
                    Text {
                        text: "HSV";
                        font-size: 10px;
                        font-weight: 600;
                        color: Theme.neon-cyan;
                        min-width: 36px;
                    }
                    Text {
                        text: round(root.hue) + " "
                            + round(root.sat * 100.0) + "% "
                            + round(root.val * 100.0) + "%";
                        font-size: Theme.font-sm;
                        color: Theme.text-primary;
                    }
                }
                HorizontalLayout {
                    spacing: Theme.sp-3;
                    Text {
                        text: "HEX";
                        font-size: 10px;
                        font-weight: 600;
                        color: Theme.neon-cyan;
                        min-width: 36px;
                    }
                    Text {
                        text: root.hex;
                        font-size: Theme.font-sm;
                        font-family: Theme.font-mono;
                        color: Theme.text-primary;
                    }
                }
            }
        }
    }
}
