"""Build fig6_pretrain.svg — Point Track Pretraining results.

Three panels side-by-side:

  (a) PRETRAIN → EVAL transfer:
        top  = circle-overlay pretraining sample (robot hidden)
        ↓
        bottom = real-robot eval scene (same bowl task)

  (b) Fine-tuning grouped bar chart, PARA vs ACT pretrained at
      1 / 3 / 5 / 10 demos. Headline callout: "5 demos · PARA 42% vs 0%"

  (c) Qualitative rollouts on the eval task:
        top    = PARA success (green)
        bottom = ACT failure (red)
"""

import base64
import os
import time
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np

_t = time.time()

PARA_GREEN = "#16653a"
ACT_RED = "#a12029"
GRAY = "#6b7280"
SLATE = "#334155"
LIGHT = "#e5e7eb"
TXT = "#0f172a"

ASSETS = "/data/cameron/penpot/figures/extracted/fig6"


# ═══════════════════════════════════════════════════════════════════════════
# Generate the bar chart (matplotlib, fig4b_pertheta style)
# ═══════════════════════════════════════════════════════════════════════════

def render_bar_chart(out_path):
    """4 bars per demo group: [PARA-pretrained, PARA-scratch, ACT-pretrained, ACT-scratch].

    Binary success rate (full-place) from the backbones report Table 3.
    Using binary rather than weighted maximises the pretraining-vs-scratch
    delta (see the 10-demo column).
    """
    fig, ax = plt.subplots(figsize=(6.4, 4.0), dpi=300)

    demos = [5, 10]
    para_pt      = [21, 66]
    para_scratch = [ 7, 10]
    act_pt       = [ 0, 21]
    act_scratch  = [ 0,  6]

    PARA_LIGHT = "#86efac"  # light green (scratch)
    ACT_LIGHT  = "#fca5a5"  # light red   (scratch)

    x = np.arange(len(demos))
    width = 0.18

    for y in (25, 50, 75):
        ax.axhline(y, color="#888888", alpha=0.3, linewidth=0.5, zorder=1)

    b1 = ax.bar(x - 1.5 * width, para_pt,     width, color=PARA_GREEN,
                edgecolor="white", linewidth=1.0, label="PARA  ·  pretrained", zorder=5)
    b2 = ax.bar(x - 0.5 * width, para_scratch, width, color=PARA_LIGHT,
                edgecolor=PARA_GREEN, linewidth=1.1, label="PARA  ·  scratch",  zorder=5)
    b3 = ax.bar(x + 0.5 * width, act_pt,      width, color=ACT_RED,
                edgecolor="white", linewidth=1.0, label="ACT    ·  pretrained", zorder=5)
    b4 = ax.bar(x + 1.5 * width, act_scratch, width, color=ACT_LIGHT,
                edgecolor=ACT_RED, linewidth=1.1, label="ACT    ·  scratch",   zorder=5)

    def _label(bars, values, color):
        for b, v in zip(bars, values):
            ax.annotate(f"{v}%", xy=(b.get_x() + b.get_width() / 2, b.get_height()),
                        xytext=(0, 3), textcoords="offset points", ha="center",
                        va="bottom", fontsize=9, fontweight="800", color=color)

    _label(b1, para_pt, PARA_GREEN)
    _label(b2, para_scratch, PARA_GREEN)
    _label(b3, act_pt, ACT_RED)
    _label(b4, act_scratch, ACT_RED)

    ax.set_xlabel("Fine-tuning demos", fontsize=12)
    ax.set_ylabel("Success Rate (%)", fontsize=12)
    ax.set_xticks(x)
    ax.set_xticklabels([str(d) for d in demos])
    ax.set_yticks([0, 25, 50, 75, 100])
    ax.set_ylim(0, 100)
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.spines["left"].set_color("#888888")
    ax.spines["bottom"].set_color("#888888")
    ax.tick_params(colors="#444444", labelsize=11)

    leg = ax.legend(loc="upper left", frameon=True, fontsize=9, ncol=2,
                    edgecolor="#cccccc", facecolor="white",
                    columnspacing=0.9, handletextpad=0.5)
    leg.get_frame().set_linewidth(0.8)

    plt.tight_layout()
    fig.savefig(out_path, format="png", bbox_inches="tight", dpi=300,
                facecolor="white", edgecolor="none")
    plt.close(fig)


chart_path = f"{ASSETS}/bar_chart.png"
render_bar_chart(chart_path)
print(f"[{time.time()-_t:.2f}s] generated bar chart")


def b64(p):
    return base64.b64encode(open(p, "rb").read()).decode()


circle_a_b64 = b64(f"{ASSETS}/circle_a.png")
chart_b64 = b64(chart_path)
# (a) bottom — real-robot eval scene (same bowl task)
eval_b64 = b64("/data/cameron/penpot/figures/extracted/fig1a_rgb_hires.png")
# (c) — qualitative success/failure on the same eval task
para_success_b64 = b64("/data/cameron/penpot/figures/extracted/fig4/para_spatial_success.png")
act_fail_b64 = b64("/data/cameron/penpot/figures/extracted/fig4/act_spatial_fail.png")

# ═══════════════════════════════════════════════════════════════════════════
# SVG layout — 3 columns: (a) pretrain→eval, (b) bar chart, (c) qualitative
# ═══════════════════════════════════════════════════════════════════════════

CANVAS_W = 1400
CANVAS_H = 500

# Column rectangles (card_x, card_y, card_w, card_h)
PA_X, PA_W = 30,  300
PB_X, PB_W = 350, 700
PC_X, PC_W = 1070, 300
CARD_Y = 50
CARD_H = 430

# Image geometry inside the narrow side cards (panels a and c)
SIDE_IMG_W = 272
SIDE_IMG_H = 165

# Panel (a) image positions
PA_TOP_IMG_X = PA_X + (PA_W - SIDE_IMG_W) // 2
PA_TOP_IMG_Y = CARD_Y + 70
PA_BOT_IMG_Y = CARD_Y + 250

# Panel (c) image positions
PC_TOP_IMG_X = PC_X + (PC_W - SIDE_IMG_W) // 2
PC_TOP_IMG_Y = CARD_Y + 70
PC_BOT_IMG_Y = CARD_Y + 250

svg = f'''<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     viewBox="0 0 {CANVAS_W} {CANVAS_H}" width="{CANVAS_W}" height="{CANVAS_H}"
     font-family="Inter, Arial, sans-serif">
  <defs>
    <filter id="card-shadow" x="-10%" y="-10%" width="120%" height="130%">
      <feDropShadow dx="0" dy="1.5" stdDeviation="2.5" flood-color="#000" flood-opacity="0.08"/>
    </filter>
    <marker id="arrow-slate" viewBox="0 0 10 10" refX="0" refY="5"
            markerWidth="6" markerHeight="6" markerUnits="userSpaceOnUse" orient="auto">
      <path d="M0,0 L10,5 L0,10 Z" fill="{SLATE}"/>
    </marker>
  </defs>

  <rect width="{CANVAS_W}" height="{CANVAS_H}" fill="#ffffff"/>

  <text x="30" y="32" font-size="15" font-weight="800" fill="{TXT}">Point-Track Pretraining → Real-Robot Transfer</text>
  <line x1="30" y1="40" x2="{CANVAS_W - 30}" y2="40" stroke="{LIGHT}" stroke-width="1"/>

  <!-- ──────────────────────────────────────────────────────
       Panel (a) — pretrain (top) → real-robot eval (bottom)
       ────────────────────────────────────────────────────── -->
  <g id="panel-a">
    <rect x="{PA_X}" y="{CARD_Y}" width="{PA_W}" height="{CARD_H}" rx="12"
          fill="#ffffff" stroke="{LIGHT}" stroke-width="1.2" filter="url(#card-shadow)"/>
    <text x="{PA_X + 16}" y="{CARD_Y + 24}" font-size="12" font-weight="800" fill="{GRAY}" letter-spacing="0.05em">
      (a) PRETRAIN → REAL-ROBOT EVAL
    </text>
    <text x="{PA_X + 16}" y="{CARD_Y + 42}" font-size="10" font-weight="500" fill="{GRAY}">
      train on circle data, fine-tune + eval on the bowl task
    </text>

    <!-- Top: pretraining sample -->
    <text x="{PA_X + PA_W // 2}" y="{PA_TOP_IMG_Y - 6}" text-anchor="middle"
          font-size="10" font-weight="700" fill="{SLATE}" letter-spacing="0.06em">
      PRETRAINING (circle overlay)
    </text>
    <clipPath id="clip-pa-top"><rect x="{PA_TOP_IMG_X}" y="{PA_TOP_IMG_Y}" width="{SIDE_IMG_W}" height="{SIDE_IMG_H}" rx="6"/></clipPath>
    <image xlink:href="data:image/png;base64,{circle_a_b64}"
           x="{PA_TOP_IMG_X}" y="{PA_TOP_IMG_Y}" width="{SIDE_IMG_W}" height="{SIDE_IMG_H}"
           preserveAspectRatio="xMidYMid slice" clip-path="url(#clip-pa-top)"/>
    <rect x="{PA_TOP_IMG_X}" y="{PA_TOP_IMG_Y}" width="{SIDE_IMG_W}" height="{SIDE_IMG_H}" rx="6"
          fill="none" stroke="{LIGHT}" stroke-width="1.5"/>

    <!-- Connecting arrow -->
    <line x1="{PA_X + PA_W // 2}" y1="{PA_TOP_IMG_Y + SIDE_IMG_H + 8}"
          x2="{PA_X + PA_W // 2}" y2="{PA_BOT_IMG_Y - 26}"
          stroke="{SLATE}" stroke-width="2.5" marker-end="url(#arrow-slate)"/>
    <text x="{PA_X + PA_W // 2 + 10}" y="{PA_BOT_IMG_Y - 14}" font-size="9"
          font-weight="600" fill="{GRAY}" font-style="italic">fine-tune</text>

    <!-- Bottom: eval scene -->
    <text x="{PA_X + PA_W // 2}" y="{PA_BOT_IMG_Y - 6}" text-anchor="middle"
          font-size="10" font-weight="700" fill="{SLATE}" letter-spacing="0.06em">
      REAL ROBOT EVAL
    </text>
    <clipPath id="clip-pa-bot"><rect x="{PA_TOP_IMG_X}" y="{PA_BOT_IMG_Y}" width="{SIDE_IMG_W}" height="{SIDE_IMG_H}" rx="6"/></clipPath>
    <image xlink:href="data:image/png;base64,{eval_b64}"
           x="{PA_TOP_IMG_X}" y="{PA_BOT_IMG_Y}" width="{SIDE_IMG_W}" height="{SIDE_IMG_H}"
           preserveAspectRatio="xMidYMid slice" clip-path="url(#clip-pa-bot)"/>
    <rect x="{PA_TOP_IMG_X}" y="{PA_BOT_IMG_Y}" width="{SIDE_IMG_W}" height="{SIDE_IMG_H}" rx="6"
          fill="none" stroke="{LIGHT}" stroke-width="1.5"/>
  </g>

  <!-- ──────────────────────────────────────────────────────
       Panel (b) — fine-tuning bar chart + headline callout
       ────────────────────────────────────────────────────── -->
  <g id="panel-b">
    <rect x="{PB_X}" y="{CARD_Y}" width="{PB_W}" height="{CARD_H}" rx="12"
          fill="#ffffff" stroke="{LIGHT}" stroke-width="1.2" filter="url(#card-shadow)"/>
    <text x="{PB_X + 18}" y="{CARD_Y + 24}" font-size="12" font-weight="800" fill="{GRAY}" letter-spacing="0.04em">
      (b) FINE-TUNING ON LIBERO · BINARY SUCCESS RATE
    </text>
    <text x="{PB_X + 18}" y="{CARD_Y + 42}" font-size="11" font-weight="500" fill="{GRAY}">
      4 bars per group · <tspan font-weight="800" fill="{PARA_GREEN}">PARA</tspan> pretrained / scratch vs <tspan font-weight="800" fill="{ACT_RED}">ACT</tspan> pretrained / scratch
    </text>

    <clipPath id="clip-chart"><rect x="{PB_X + 18}" y="{CARD_Y + 56}" width="{PB_W - 36}" height="{CARD_H - 116}" rx="6"/></clipPath>
    <image xlink:href="data:image/png;base64,{chart_b64}"
           x="{PB_X + 18}" y="{CARD_Y + 56}" width="{PB_W - 36}" height="{CARD_H - 116}"
           preserveAspectRatio="xMidYMid meet" clip-path="url(#clip-chart)"/>

    <!-- Headline callout pinned to bottom of panel -->
    <rect x="{PB_X + (PB_W - 420) // 2}" y="{CARD_Y + CARD_H - 56}" width="420" height="42" rx="10"
          fill="#f0fdf4" stroke="{PARA_GREEN}" stroke-width="2"/>
    <text x="{PB_X + PB_W // 2}" y="{CARD_Y + CARD_H - 38}" text-anchor="middle" font-size="11" font-weight="800" fill="{PARA_GREEN}" letter-spacing="0.05em">
      KEY RESULT · 10 FINE-TUNING DEMOS
    </text>
    <text x="{PB_X + PB_W // 2}" y="{CARD_Y + CARD_H - 19}" text-anchor="middle" font-size="16" font-weight="900" fill="{TXT}">
      <tspan fill="{PARA_GREEN}">PARA pretrained 66%</tspan>
      <tspan fill="{GRAY}" font-weight="500"> vs </tspan>
      <tspan fill="{PARA_GREEN}" font-style="italic">PARA scratch 10%</tspan>
    </text>
  </g>

  <!-- ──────────────────────────────────────────────────────
       Panel (c) — qualitative rollouts: PARA ✓ / ACT ✗
       ────────────────────────────────────────────────────── -->
  <g id="panel-c">
    <rect x="{PC_X}" y="{CARD_Y}" width="{PC_W}" height="{CARD_H}" rx="12"
          fill="#ffffff" stroke="{LIGHT}" stroke-width="1.2" filter="url(#card-shadow)"/>
    <text x="{PC_X + 16}" y="{CARD_Y + 24}" font-size="12" font-weight="800" fill="{GRAY}" letter-spacing="0.05em">
      (c) ROLLOUTS · 5-DEMO FINE-TUNE
    </text>
    <text x="{PC_X + 16}" y="{CARD_Y + 42}" font-size="10" font-weight="500" fill="{GRAY}">
      same eval position, different pretraining
    </text>

    <!-- Top: PARA success -->
    <clipPath id="clip-pc-top"><rect x="{PC_TOP_IMG_X}" y="{PC_TOP_IMG_Y}" width="{SIDE_IMG_W}" height="{SIDE_IMG_H}" rx="6"/></clipPath>
    <image xlink:href="data:image/png;base64,{para_success_b64}"
           x="{PC_TOP_IMG_X}" y="{PC_TOP_IMG_Y}" width="{SIDE_IMG_W}" height="{SIDE_IMG_H}"
           preserveAspectRatio="xMidYMid slice" clip-path="url(#clip-pc-top)"/>
    <rect x="{PC_TOP_IMG_X}" y="{PC_TOP_IMG_Y}" width="{SIDE_IMG_W}" height="{SIDE_IMG_H}" rx="6"
          fill="none" stroke="{PARA_GREEN}" stroke-width="3"/>
    <rect x="{PC_TOP_IMG_X + 6}" y="{PC_TOP_IMG_Y + 6}" width="68" height="20" rx="4" fill="{PARA_GREEN}"/>
    <text x="{PC_TOP_IMG_X + 40}" y="{PC_TOP_IMG_Y + 21}" text-anchor="middle"
          font-size="11" font-weight="800" fill="#ffffff">PARA ✓</text>

    <!-- Bottom: ACT failure -->
    <clipPath id="clip-pc-bot"><rect x="{PC_TOP_IMG_X}" y="{PC_BOT_IMG_Y}" width="{SIDE_IMG_W}" height="{SIDE_IMG_H}" rx="6"/></clipPath>
    <image xlink:href="data:image/png;base64,{act_fail_b64}"
           x="{PC_TOP_IMG_X}" y="{PC_BOT_IMG_Y}" width="{SIDE_IMG_W}" height="{SIDE_IMG_H}"
           preserveAspectRatio="xMidYMid slice" clip-path="url(#clip-pc-bot)"/>
    <rect x="{PC_TOP_IMG_X}" y="{PC_BOT_IMG_Y}" width="{SIDE_IMG_W}" height="{SIDE_IMG_H}" rx="6"
          fill="none" stroke="{ACT_RED}" stroke-width="3"/>
    <rect x="{PC_TOP_IMG_X + 6}" y="{PC_BOT_IMG_Y + 6}" width="62" height="20" rx="4" fill="{ACT_RED}"/>
    <text x="{PC_TOP_IMG_X + 37}" y="{PC_BOT_IMG_Y + 21}" text-anchor="middle"
          font-size="11" font-weight="800" fill="#ffffff">ACT ✗</text>
  </g>
</svg>
'''

out = "/data/cameron/para/paper/figs/svg/fig6_pretrain.svg"
with open(out, "w") as f:
    f.write(svg)
print(f"[{time.time()-_t:.2f}s] wrote {out} ({len(svg)} bytes)")
