#!/usr/bin/env bash
# ──────────────────────────────────────────────────────────
# hhb — Hermes-Houdini Bridge Client
# Sends Python/hou commands to a running Houdini session
# via SSH + hcommand. Results returned via temp file.
# ──────────────────────────────────────────────────────────
set -euo pipefail

HOST="${HHB_HOST:-niklas@192.168.178.39}"
PORT="${HHB_PORT:-12345}"
HCOMMAND="${HHB_HCOMMAND:-/opt/hfs21.0/bin/hcommand}"

usage() {
    cat <<'EOF'
Usage: hhb <command> [args]

Commands:
  eval <python_code>     Execute Python code in the running Houdini session.
  script <file.py>       Send a Python script file to Houdini and execute it.
  get-selected           Print paths and types of selected nodes.
  get-param <parm>       Get value of a parameter on the first selected node.
  set-param <parm> <val> Set a parameter on the first selected node.
  get-inputs             Print input connections of the first selected node.
  status                 Check if the bridge port is open.
  help                   Show this help.
EOF
    exit 0
}

# ── Core: write Python script, execute, read back output ─

run_houdini_python() {
    # $1: Python code (as a string)
    # Writes code to /tmp/hhb_script.py, executes via hcommand,
    # reads output from /tmp/hhb_out.txt
    local python_code="$1"
    local sid="$$"

    # Upload the script
    printf '%s' "$python_code" | ssh "$HOST" "cat > /tmp/hhb_script_${sid}.py"

    # Execute via hcommand
    ssh "$HOST" "$HCOMMAND $PORT 'python /tmp/hhb_script_${sid}.py'" 2>/dev/null || true

    # Read output file
    ssh "$HOST" "cat /tmp/hhb_out_${sid}.txt 2>/dev/null" || echo ""

    # Cleanup
    ssh "$HOST" "rm -f /tmp/hhb_script_${sid}.py /tmp/hhb_out_${sid}.txt" 2>/dev/null || true
}

# Wrap user code to capture output via temp file
wrap_python() {
    local code="$1"
    local sid="$$"
    cat <<PYEOF
import hou, json, sys
try:
${code}
except Exception as e:
    with open('/tmp/hhb_out_${sid}.txt', 'w') as f:
        f.write(json.dumps({"error": str(e)}))
PYEOF
}

# ── Commands ──────────────────────────────────────────────

cmd_status() {
    local output
    output=$(ssh "$HOST" "$HCOMMAND $PORT openport" 2>&1) || {
        echo "Bridge INACTIVE — Houdini not reachable or port $PORT closed."
        return 1
    }
    echo "$output" | sed 's/^->\s*//'
}

cmd_eval() {
    [[ $# -ge 1 ]] || { echo "Usage: hhb eval <python_code>"; exit 1; }
    local sid="$$"
    local code
    code=$(cat <<PYEOF
import hou, json, sys, io
_stdout = io.StringIO()
sys.stdout = _stdout
$*
sys.stdout = sys.__stdout__
with open('/tmp/hhb_out_${sid}.txt', 'w') as f:
    f.write(_stdout.getvalue())
PYEOF
)
    run_houdini_python "$code"
}

cmd_script() {
    [[ $# -ge 1 ]] || { echo "Usage: hhb script <file.py>"; exit 1; }
    local script_path="$1"
    local sid="$$"
    [[ -f "$script_path" ]] || { echo "File not found: $script_path"; exit 1; }

    # Read the script, wrap in output capture
    local code
    code=$(cat "$script_path")
    local wrapped
    wrapped=$(cat <<PYEOF
import hou, json
try:
${code}
except Exception as e:
    with open('/tmp/hhb_out_${sid}.txt', 'w') as f:
        f.write(json.dumps({"error": str(e)}))
PYEOF
)
    run_houdini_python "$wrapped"
}

cmd_get_selected() {
    local sid="$$"
    local code
    code=$(cat <<'PYEOF'
nodes = hou.selectedNodes()
lines = []
if not nodes:
    lines.append("No nodes selected.")
else:
    for n in nodes:
        lines.append(f"{n.path():50s}  [{n.type().name()}]")
with open('/tmp/hhb_out_PLACEHOLDER.txt', 'w') as f:
    f.write("\n".join(lines))
PYEOF
)
    code="${code//PLACEHOLDER/$sid}"
    run_houdini_python "$code"
}

cmd_get_param() {
    [[ $# -ge 1 ]] || { echo "Usage: hhb get-param <parm_name>"; exit 1; }
    local parm="$1"
    local sid="$$"
    local code
    code=$(cat <<PYEOF
nodes = hou.selectedNodes()
if not nodes:
    msg = "No nodes selected."
else:
    p = nodes[0].parm("PARM_PLACEHOLDER")
    if p is None:
        msg = f'Parameter "PARM_PLACEHOLDER" not found on {nodes[0].path()}'
    else:
        msg = f'{nodes[0].path()}.{p.name()} = {p.eval()}'
with open('/tmp/hhb_out_PLACEHOLDER_SID.txt', 'w') as f:
    f.write(msg)
PYEOF
)
    code="${code//PARM_PLACEHOLDER/$parm}"
    code="${code//PLACEHOLDER_SID/$sid}"
    run_houdini_python "$code"
}

cmd_set_param() {
    [[ $# -ge 2 ]] || { echo "Usage: hhb set-param <parm_name> <value>"; exit 1; }
    local parm="$1"
    local val="$2"
    local sid="$$"
    local code
    code=$(cat <<PYEOF
nodes = hou.selectedNodes()
if not nodes:
    msg = "No nodes selected."
else:
    p = nodes[0].parm("PARM_PLACEHOLDER")
    if p is None:
        msg = f'Parameter "PARM_PLACEHOLDER" not found on {nodes[0].path()}'
    else:
        p.set("VAL_PLACEHOLDER")
        msg = f'{nodes[0].path()}.{p.name()} = {p.eval()}  OK'
with open('/tmp/hhb_out_PLACEHOLDER_SID.txt', 'w') as f:
    f.write(msg)
PYEOF
)
    code="${code//PARM_PLACEHOLDER/$parm}"
    code="${code//VAL_PLACEHOLDER/$val}"
    code="${code//PLACEHOLDER_SID/$sid}"
    run_houdini_python "$code"
}

cmd_get_inputs() {
    local sid="$$"
    local code
    code=$(cat <<'PYEOF'
lines = []
nodes = hou.selectedNodes()
if not nodes:
    lines.append("No nodes selected.")
else:
    for n in nodes:
        lines.append(f"\n{n.path()} [{n.type().name()}]")
        for i in range(len(n.inputConnectors())):
            conns = n.inputConnections(i)
            if conns:
                for c in conns:
                    lines.append(f"  input {i} <- {c.inputNode().path()}  [{c.inputNode().type().name()}]")
            else:
                lines.append(f"  input {i} <- (none)")
with open('/tmp/hhb_out_PLACEHOLDER.txt', 'w') as f:
    f.write("\n".join(lines))
PYEOF
)
    code="${code//PLACEHOLDER/$sid}"
    run_houdini_python "$code"
}

# ── Main ─────────────────────────────────────────────────

[[ $# -ge 1 ]] || usage

CMD="$1"
shift || true

case "$CMD" in
    eval)           cmd_eval "$@" ;;
    script)         cmd_script "$@" ;;
    get-selected)   cmd_get_selected ;;
    get-param)      cmd_get_param "$@" ;;
    set-param)      cmd_set_param "$@" ;;
    get-inputs)     cmd_get_inputs ;;
    status)         cmd_status ;;
    help|--help|-h) usage ;;
    *)
        echo "Unknown command: $CMD"
        usage
        ;;
esac
