// node.el — Node type definitions and color/radius mapping.
//
// Node types mirror the Engram knowledge graph node_type field.
// Colors are chosen for dark-background (Studio) legibility.

// ── Node type constants ──────────────────────────────────────────────────────

fn node_type_memory()      -> String { "Memory" }
fn node_type_backlog()     -> String { "BacklogItem" }
fn node_type_knowledge()   -> String { "Knowledge" }
fn node_type_entity()      -> String { "Entity" }
fn node_type_default()     -> String { "Node" }

// ── Color map ────────────────────────────────────────────────────────────────

fn node_color(node_type: String) -> String {
    if str_eq(node_type, "Memory")      { return "#58A6FF" }
    if str_eq(node_type, "BacklogItem") { return "#C9A84C" }
    if str_eq(node_type, "Knowledge")   { return "#2ecc71" }
    if str_eq(node_type, "Entity")      { return "#e74c3c" }
    if str_eq(node_type, "WorkContext") { return "#9b59b6" }
    if str_eq(node_type, "Artifact")    { return "#1abc9c" }
    if str_eq(node_type, "Process")     { return "#e67e22" }
    "#7a8ba8"
}

// ── Radius ───────────────────────────────────────────────────────────────────
//
// Clamp salience (0.0–1.0) to radius range [6, 18].

fn node_radius(salience: Float) -> Float {
    let min_r: Float = int_to_float(6)
    let max_r: Float = int_to_float(18)
    let range: Float = max_r - min_r
    let clamped: Float = if salience < int_to_float(0) { int_to_float(0) } else { if salience > int_to_float(1) { int_to_float(1) } else { salience } }
    min_r + range * clamped
}

fn node_radius_int(salience: Float) -> Int {
    float_to_int(node_radius(salience))
}

// ── Label truncation ─────────────────────────────────────────────────────────

fn node_label_truncate(label: String) -> String {
    let max_len: Int = 30
    let l: Int = str_len(label)
    if l <= max_len { return label }
    str_slice(label, 0, max_len) + "..."
}

// ── Node JSON accessors ──────────────────────────────────────────────────────
//
// Nodes are passed as JSON objects: { id, label, node_type, salience, ... }

fn node_id(n_json: String) -> String {
    json_get_string(n_json, "id")
}

fn node_label(n_json: String) -> String {
    let lbl: String = json_get_string(n_json, "label")
    if !str_eq(lbl, "") { return lbl }
    // Fall back to first 40 chars of content
    let c: String = json_get_string(n_json, "content")
    if str_len(c) > 40 { return str_slice(c, 0, 40) }
    c
}

fn node_type_field(n_json: String) -> String {
    let t: String = json_get_string(n_json, "node_type")
    if str_eq(t, "") { return node_type_default() }
    t
}

fn node_salience(n_json: String) -> Float {
    json_get_float(n_json, "salience")
}
