#!/usr/bin/env python3
"""
Live view + logger of the Inspire hand's 12 joints (rt/inspire/state).

  conda activate g1_env
  cd ~/Robotics_workspace/yslootahtech/G1/G1_Lootah/Manual_Recorder
  python3 hand_monitor.py enp3s0                 # live bars + auto CSV log
  python3 hand_monitor.py enp3s0 --hz 30         # faster refresh/log
  python3 hand_monitor.py enp3s0 --log run1.csv  # choose log file
  python3 hand_monitor.py enp3s0 --no-log        # view only, no file

Needs inspire_g1 running on the robot (the Docker container).
q in [0,1]:  1.00 = open,  0.00 = closed.  Ctrl+C to stop.
Idx 0-5 = right hand, 6-11 = left; per hand: pinky,ring,middle,index,thumb_bend,thumb_rot.
"""
import sys, time, argparse, csv
from datetime import datetime
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.unitree_go.msg.dds_ import MotorStates_

NAMES = ["R.pinky", "R.ring", "R.mid", "R.index", "R.thumbB", "R.thumbR",
         "L.pinky", "L.ring", "L.mid", "L.index", "L.thumbB", "L.thumbR"]

CLR = "\x1b[K"   # clear to end of line (avoids redraw artifacts)


def bar(v, width=26):
    v = 0.0 if v < 0 else (1.0 if v > 1 else v)
    n = int(round(v * width))
    return "█" * n + "·" * (width - n)


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("iface", nargs="?", default="enp3s0", help="network interface (default enp3s0)")
    ap.add_argument("--hz", type=float, default=20.0, help="refresh + log rate (default 20)")
    ap.add_argument("--log", default=None, help="CSV log path (default hand_log_<timestamp>.csv)")
    ap.add_argument("--no-log", action="store_true", help="view only, do not write a log")
    args = ap.parse_args()

    ChannelFactoryInitialize(0, args.iface)

    st = {"q": [0.0] * 12, "n": 0}

    def on_state(msg):
        try:
            st["q"] = [float(msg.states[i].q) for i in range(12)]
            st["n"] += 1
        except Exception:
            pass

    sub = ChannelSubscriber("rt/inspire/state", MotorStates_)
    sub.Init(on_state, 10)

    writer = logf = log_path = None
    if not args.no_log:
        log_path = args.log or f"hand_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
        logf = open(log_path, "w", newline="")
        writer = csv.writer(logf)
        writer.writerow(["t_epoch", "iso"] + NAMES)

    dt = 1.0 / args.hz
    rows = 0
    print("\x1b[2J", end="")  # clear once
    try:
        while True:
            now = time.time()
            q = st["q"]
            live_r = any(abs(x) > 1e-3 for x in q[:6])
            live_l = any(abs(x) > 1e-3 for x in q[6:])

            lines = ["\x1b[H"]  # cursor home; redraw in place
            lines.append(f"Inspire hand — live joints   iface={args.iface}   msgs={st['n']:>6d}   "
                         f"{datetime.now().strftime('%H:%M:%S')}   (Ctrl+C to stop){CLR}")
            lines.append(f"q: 1.00 open ░ 0.00 closed" + (f"   log: {log_path}" if log_path else "   (no log)") + CLR)
            lines.append(CLR)
            for i, nm in enumerate(NAMES):
                if i == 6:
                    lines.append(CLR)  # blank line between hands
                lines.append(f"  {nm:9s} [{bar(q[i])}] {q[i]:5.2f}{CLR}")
            lines.append(CLR)
            lines.append(f"  RIGHT: {'● live' if live_r else '○ FLAT 0 (adapter dropped?)'}     "
                         f"LEFT: {'● live' if live_l else '○ FLAT 0 (adapter dropped?)'}{CLR}")
            if log_path:
                lines.append(f"  logged rows: {rows}{CLR}")
            print("\n".join(lines), end="", flush=True)

            if writer:
                writer.writerow([f"{now:.3f}", datetime.now().isoformat()] + [f"{x:.4f}" for x in q])
                rows += 1

            time.sleep(dt)
    except KeyboardInterrupt:
        pass
    finally:
        if logf:
            logf.close()
        print(f"\n\nstopped." + (f"  Saved {rows} rows to {log_path}" if log_path else ""))


if __name__ == "__main__":
    main()
