#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""升级蛋白工作台：详细图例 + 平移模式 + 还原默认视角"""
import io
import os
import re

W = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
p = W + '/脚本/build_protein_workbench_v2.py'
s = io.open(p, encoding='utf-8').read()

# ── 1. 结构域加"功能"说明（第 5 个元素）──
s = s.replace("""        'domains': [('5′→3′ 外切酶域', 1, 290, '#E08A1E'), ('聚合酶域 · 手指', 291, 420, '#1852B6'),
                    ('聚合酶域 · 手掌', 421, 610, '#2E7DD1'), ('聚合酶域 · 拇指', 611, 832, '#4FA3E3')],""",
"""        'domains': [('5′→3′ 外切酶域', 1, 290, '#E08A1E', '水解探针与损伤碱基，决定 TaqMan 类探针法可用性'),
                    ('聚合酶域 · 手指', 291, 420, '#1852B6', '结合模板与 dNTP，决定底物识别'),
                    ('聚合酶域 · 手掌', 421, 610, '#2E7DD1', '催化中心，dNTP 掺入与磷酸二酯键形成'),
                    ('聚合酶域 · 拇指', 611, 832, '#4FA3E3', '结合双链 DNA，决定持续合成能力')],""")
s = s.replace("""        'domains': [('5′→3′ 外切酶域（预测）', 1, 290, '#E08A1E'), ('聚合酶域 · 手指', 291, 420, '#1852B6'),
                    ('聚合酶域 · 手掌', 421, 610, '#2E7DD1'), ('聚合酶域 · 拇指', 611, 834, '#4FA3E3')],""",
"""        'domains': [('5′→3′ 外切酶域（预测）', 1, 290, '#E08A1E', '切探针/去损伤；该区段无实验结构'),
                    ('聚合酶域 · 手指', 291, 420, '#1852B6', '结合模板与 dNTP'),
                    ('聚合酶域 · 手掌', 421, 610, '#2E7DD1', '催化中心'),
                    ('聚合酶域 · 拇指', 611, 834, '#4FA3E3', '持续合成')],""")
s = s.replace("""        'domains': [('RT 域 · fingers/palm/thumb', 741, 1100, '#1852B6'), ('连接区（预测）', 1101, 1173, '#9AA7B8'),
                    ('RNase H 域（预测）', 1174, 1320, '#E08A1E')],""",
"""        'domains': [('RT 域 · fingers/palm/thumb', 741, 1100, '#1852B6', '逆转录催化：以 RNA 为模板合成 cDNA'),
                    ('连接区（预测）', 1101, 1173, '#9AA7B8', '连接 RT 与 RNase H，影响构象协调'),
                    ('RNase H 域（预测）', 1174, 1320, '#E08A1E', '降解 RNA–DNA 杂交链中的 RNA，影响 cDNA 长度')],""")
s = s.replace("""        'domains': [('UDG 催化域', 1, 229, '#0F9D8F')],""",
"""        'domains': [('UDG 催化域', 1, 229, '#0F9D8F', '切除 DNA 中的尿嘧啶，防遗留污染')],""")

# ── 2. 解包处适配 5 元组 ──
s = s.replace("for name, a, b, col in prot['domains']:", "for name, a, b, col, _fn in prot['domains']:")
s = s.replace("for name, a, b, c in prot['domains']:", "for name, a, b, c, _fn in prot['domains']:")
s = s.replace("for n, a, b, c in p['domains']:", "for n, a, b, c, _f in p['domains']:")

# ── 3. 控制区：加平移模式 / 方向键 / 还原视角 ──
s = s.replace("""          <button data-k="%(key)s" data-a="spin">自转</button>
          <input class="resi" placeholder="残基号" inputmode="numeric" style="width:78px;padding:6px 8px;border:1px solid #cfd8e6;border-radius:6px;font-size:12px">
          <button data-k="%(key)s" data-a="go">定位残基</button>""",
"""          <button data-k="%(key)s" data-a="spin">自转</button>
          <button data-k="%(key)s" data-a="pan">平移模式</button>
          <button data-k="%(key)s" data-a="left">←</button>
          <button data-k="%(key)s" data-a="right">→</button>
          <button data-k="%(key)s" data-a="up">↑</button>
          <button data-k="%(key)s" data-a="down">↓</button>
          <button data-k="%(key)s" data-a="reset">还原默认视角</button>
          <input class="resi" placeholder="残基号" inputmode="numeric" style="width:78px;padding:6px 8px;border:1px solid #cfd8e6;border-radius:6px;font-size:12px">
          <button data-k="%(key)s" data-a="go">定位残基</button>""")
s = s.replace("""        <div class="viewer" id="v-%(key)s"></div>""",
"""        <div class="viewer" id="v-%(key)s"><div class="panlayer" id="pan-%(key)s"></div></div>""")

# ── 4. 详细图例 ──
s = s.replace("""        <h3>颜色图例</h3><div class="legend">%(legend)s</div>""",
"""        <h3>图例（图形 / 颜色 / 操作）</h3>%(legendbox)s""")

# ── 5. 生成图例 HTML ──
s = s.replace("""    legend = ''.join('<span><i style="background:%s"></i>%s（%d–%d）</span>' % (c, n.replace('（预测）', ''), a, b)
                     for n, a, b, c in p['domains'])""",
"""    legend = ''.join('<span><i style="background:%s"></i>%s（%d–%d）</span>' % (c, n.replace('（预测）', ''), a, b)
                     for n, a, b, c, _f in p['domains'])
    legendbox = '''
<div class="lgroup"><b>图形代表什么</b>
  <span class="li"><svg width="26" height="12"><path d="M2,6 Q6,0 10,6 T18,6 T24,6" stroke="#1852B6" stroke-width="2" fill="none"/></svg>螺旋 = α 螺旋</span>
  <span class="li"><svg width="26" height="12"><path d="M2,6 L18,6 M14,2 L18,6 L14,10" stroke="#1852B6" stroke-width="2" fill="none"/></svg>箭头 = β 折叠</span>
  <span class="li"><svg width="26" height="12"><path d="M2,6 C8,2 12,10 24,6" stroke="#9AA7B8" stroke-width="2" fill="none"/></svg>细线 = 无规卷曲</span>
</div>
<div class="lgroup"><b>颜色 = 结构域（含功能）</b>
  %s
</div>
<div class="lgroup"><b>标记与操作</b>
  <span class="li"><i style="background:#C0392B"></i>红色棒状 = 选中残基 / 催化位点</span>
  <span class="li"><i class="hatchbox"></i>斜纹 = AlphaFold 预测区</span>
  <span class="li">左键拖动 = 旋转 ｜ 滚轮 = 缩放</span>
  <span class="li">平移模式拖动 或 右键拖动 = 平移视野</span>
  <span class="li">点击残基 = 显示名称与编号</span>
</div>''' % ''.join(
        '<span class="li"><i style="background:%s"></i><b>%s</b>（%d–%d）：%s</span>' % (c, n.replace('（预测）', ''), a, b, fn)
        for n, a, b, c, fn in p['domains'])""")
s = s.replace("""        'len': _len(p), 'badge': badge, 'facts': facts, 'site_note': site_note, 'legend': legend,""",
"""        'len': _len(p), 'badge': badge, 'facts': facts, 'site_note': site_note, 'legend': legend,
        'legendbox': legendbox,""")

# ── 6. CSS ──
s = s.replace(""".legend{display:flex;gap:12px;flex-wrap:wrap;font-size:11.3px;color:#43505f}""",
""".legend{display:flex;gap:12px;flex-wrap:wrap;font-size:11.3px;color:#43505f}
.lgroup{border-top:1px solid #eef1f6;padding:7px 0 5px}
.lgroup b{display:block;font-size:11.6px;color:#1F4E79;margin-bottom:4px}
.lgroup .li{display:inline-flex;align-items:center;gap:5px;font-size:11.4px;color:#43505f;margin:2px 12px 2px 0}
.lgroup .li i{width:11px;height:11px;border-radius:2px;display:inline-block}
.hatchbox{background:repeating-linear-gradient(45deg,#c9d3e0,#c9d3e0 2px,#fff 2px,#fff 5px);border:1px solid #b7c3d3}
.panlayer{position:absolute;inset:0;display:none;cursor:grab;z-index:5}
.panlayer.on{display:block}
.panlayer.grabbing{cursor:grabbing}""")

# ── 7. JS：平移、还原、保存初始视角 ──
s = s.replace("""var V={}, INIT={}, STICK={}, SPIN={};""",
"""var V={}, INIT={}, STICK={}, SPIN={}, SAVED={}, PAN={};""")
s = s.replace("""  V[k]=v; INIT[k]=true;
  return v;""",
"""  V[k]=v; INIT[k]=true;
  SAVED[k]=v.getView();
  var layer=document.getElementById('pan-'+k);
  if(layer && !layer.dataset.bound){
    layer.dataset.bound='1';
    var drag=null;
    layer.addEventListener('mousedown',function(e){ drag={x:e.clientX,y:e.clientY}; layer.classList.add('grabbing'); e.preventDefault(); });
    window.addEventListener('mousemove',function(e){
      if(!drag) return;
      var dx=e.clientX-drag.x, dy=e.clientY-drag.y;
      drag={x:e.clientX,y:e.clientY};
      v.translateScene(dx*k0, -dy*k0);
      v.render();
    });
    window.addEventListener('mouseup',function(){ if(drag){ drag=null; layer.classList.remove('grabbing'); } });
  }
  return v;""")
s = s.replace("""var DOMAINS={__DOMAINS__};""",
"""var DOMAINS={__DOMAINS__};
var k0=1.0;""")
s = s.replace("""  if(a==='spin'){ SPIN[k]=!SPIN[k]; v.spin(SPIN[k]?'y':false); }""",
"""  if(a==='spin'){ SPIN[k]=!SPIN[k]; v.spin(SPIN[k]?'y':false); }
  if(a==='pan'){
    PAN[k]=!PAN[k];
    var ly=document.getElementById('pan-'+k);
    if(ly) ly.classList.toggle('on',PAN[k]);
    b.textContent=PAN[k]?'退出平移':'平移模式';
    b.style.background=PAN[k]?'#1852B6':'#fff';
    b.style.color=PAN[k]?'#fff':'#1b2430';
  }
  if(a==='left'){ v.translateScene(24,0); v.render(); }
  if(a==='right'){ v.translateScene(-24,0); v.render(); }
  if(a==='up'){ v.translateScene(0,-24); v.render(); }
  if(a==='down'){ v.translateScene(0,24); v.render(); }
  if(a==='reset'){ if(SAVED[k]){ v.setView(SAVED[k]); v.render(); } else { v.zoomTo(); v.render(); } }""")
io.open(p, 'w', encoding='utf-8').write(s)
print('patched builder')
