# -*- coding: utf-8 -*-
# [2026-07-22] 판A 실마우스 클릭 3곳 실측 — 선택 삼각형수 / bbox 최대축(mm) / 클릭점~선택영역 거리(mm)
#   철칙 제5조: evaluate 는 '읽기'만. 클릭은 전부 진짜 마우스 이벤트.
import os, sys, io, json
from playwright.sync_api import sync_playwright

URL = "http://localhost:8091/DOZIKWORKS_OS_v5_locked.dc.html"
BASE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.join(BASE, "plate_click3_260722")
os.makedirs(OUT, exist_ok=True)

# 넓은 면 3곳 후보 찾기 (읽기 전용 raycast) — 화면 그리드에서 히트 후 법선 방향이 서로 다른 3곳
FIND = """() => {
  const a = window.__dzw;
  const cam = a.three.cam || a.three.camera, dom = a.three.rnd.domElement;
  const rect = dom.getBoundingClientRect();
  const RC = (window.THREE && THREE.Raycaster) ? THREE.Raycaster : a._ray.constructor;
  const ray = new RC(); const hits = [];
  for (let gy = 0.20; gy <= 0.82; gy += 0.04) {
    for (let gx = 0.20; gx <= 0.82; gx += 0.04) {
      const cx = rect.left + rect.width*gx, cy = rect.top + rect.height*gy;
      const mx = ((cx-rect.left)/rect.width)*2-1, my = -((cy-rect.top)/rect.height)*2+1;
      ray.setFromCamera({x:mx,y:my}, cam);
      const h = ray.intersectObjects([a._knuckleMesh], true);
      if (h.length) { const n=h[0].face.normal;
        hits.push({x:cx,y:cy,nx:n.x,ny:n.y,nz:n.z,fi:h[0].faceIndex,
                   px:h[0].point.x,py:h[0].point.y,pz:h[0].point.z}); }
    }
  }
  const picked=[];
  for(const h of hits){
    if(picked.every(p=>Math.abs(p.nx*h.nx+p.ny*h.ny+p.nz*h.nz)<0.7)) picked.push(h);
    if(picked.length>=3) break;
  }
  while(picked.length<3 && hits.length>picked.length) picked.push(hits[hits.length-picked.length]);
  return picked;
}"""

# 클릭 후 판A 영역 실측 (읽기만)
MEASURE = """(click) => {
  const a=window.__dzw; const R=a._faceARegion;
  if(!R||!R.size) return {n:0};
  const pos=a._knuckleMesh.geometry.attributes.position;
  // 씨앗 표와 단위를 맞추기 위해 geometry 로컬(mm) 기준으로 잰다. 클릭점을 로컬로 역변환.
  let x0=1e9,y0=1e9,z0=1e9,x1=-1e9,y1=-1e9,z1=-1e9, best=1e9;
  const inv=a._knuckleMesh.matrixWorld.clone().invert();
  const C=new THREE.Vector3(click.px,click.py,click.pz).applyMatrix4(inv);
  for(const t of R){ for(let k=0;k<3;k++){ const i=t*3+k;
    const vx=pos.getX(i), vy=pos.getY(i), vz=pos.getZ(i);
    if(vx<x0)x0=vx; if(vy<y0)y0=vy; if(vz<z0)z0=vz;
    if(vx>x1)x1=vx; if(vy>y1)y1=vy; if(vz>z1)z1=vz;
    const d=Math.hypot(vx-C.x,vy-C.y,vz-C.z); if(d<best)best=d;
  }}
  return {n:R.size, mm:+Math.max(x1-x0,y1-y0,z1-z0).toFixed(0), clickDistMM:+best.toFixed(1)};
}"""

def click_el(pg, sel):
    loc = pg.locator(sel).first
    try: box = loc.bounding_box()
    except Exception: return False
    if not box or box["width"] == 0: return False
    pg.mouse.click(box["x"]+box["width"]/2, box["y"]+box["height"]/2); return True

def click_text(pg, text, exact=True):
    loc = pg.get_by_text(text, exact=exact)
    for i in range(loc.count()):
        try: box = loc.nth(i).bounding_box()
        except Exception: continue
        if box and box["width"] > 0:
            pg.mouse.click(box["x"]+box["width"]/2, box["y"]+box["height"]/2); return True
    return False

def main():
    errs = []
    with sync_playwright() as p:
        br = p.chromium.launch(headless=True)
        pg = br.new_page(viewport={"width":1600,"height":900})
        pg.on("pageerror", lambda e: errs.append(str(e)))
        pg.goto(URL)
        pg.wait_for_function("window.__dzw && window.__dzw._knuckleMesh && window.DZW5_EDGEPATH && window.__dzw.three", timeout=60000)
        pg.wait_for_timeout(5000)
        if pg.evaluate("() => { const w=document.getElementById('dzwb-wrap'); return !!(w&&w.className.includes('shown')); }"):
            click_el(pg, "#dzwb-x"); pg.wait_for_timeout(800)
        click_text(pg, "FIT"); pg.wait_for_timeout(1500)
        click_el(pg, '[data-dzw-pick="select"]'); pg.wait_for_timeout(500)
        pts = pg.evaluate(FIND)
        if pts: pg.mouse.click(pts[0]["x"], pts[0]["y"]); pg.wait_for_timeout(1000)
        click_el(pg, '.dzw-tp-tab[data-tab="align"]'); pg.wait_for_timeout(500)

        rows = []
        for i, pt in enumerate(pts[:3]):
            click_el(pg, "#dzw-edge-reset-btn"); pg.wait_for_timeout(600)
            click_el(pg, "#dzw-curve-mode-btn"); pg.wait_for_timeout(500)
            mode = pg.evaluate("() => window.__dzw._faceMode")
            pg.mouse.move(pt["x"], pt["y"]); pg.mouse.down(); pg.mouse.up()
            pg.wait_for_timeout(1500)
            m = pg.evaluate(MEASURE, pt)
            m["mode"] = mode; m["screen"] = [round(pt["x"]), round(pt["y"])]
            rows.append(m)
            with open(os.path.join(OUT, f"click{i+1}.png"), "wb") as f: f.write(pg.screenshot())
            print(f"클릭{i+1} 화면({round(pt['x'])},{round(pt['y'])}) mode={mode} → {json.dumps(m, ensure_ascii=False)}")
        br.close()
    print("RESULT " + json.dumps(rows, ensure_ascii=False))
    if errs: print("pageerror:", errs[:3])

if __name__ == "__main__":
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
    main()
