import asyncio
from playwright.async_api import async_playwright
import math

# 4방향 카메라 설정 (theta, phi, r, tgt_z)
CAMERAS = [
    ('FRONT',  -math.pi/2,  math.pi/2 * 0.6,  3.5,  0.5),   # 정면 (Y+ 방향)
    ('RIGHT',  0.0,          math.pi/2 * 0.6,  3.5,  0.5),   # 오른쪽 (X+ 방향)
    ('LEFT',   math.pi,      math.pi/2 * 0.6,  3.5,  0.5),   # 왼쪽  (X- 방향)
    ('TOP',    -math.pi/2,   0.15,             4.0,  0.5),   # 위에서
]

JS_MAIN = """
() => {
    var app = window.__dzw;
    var T = app._T;
    if(!app || !T) return {err:'no app'};

    app._grp.updateMatrixWorld(true);

    function wpos(obj) {
        var v = new T.Vector3();
        obj.getWorldPosition(v);
        // _grp.scale=0.001이므로 world(m) *1000 = mm
        return [+(v.x*1000).toFixed(2), +(v.y*1000).toFixed(2), +(v.z*1000).toFixed(2)];
    }

    function dist(a, b) {
        var dx=a[0]-b[0], dy=a[1]-b[1], dz=a[2]-b[2];
        return +Math.sqrt(dx*dx+dy*dy+dz*dz).toFixed(2);
    }

    // ── 1) 각 관절 pivot world 위치 ──────────────────────────────────────
    var gj = app.gj;
    var joints = {};
    ['S','L','U','R','B','T'].forEach(function(a) {
        var ag = gj[a];
        if(!ag) return;
        var piv = ag.parent;
        joints[a] = {
            piv_world: wpos(piv),
            ang_world: wpos(ag),
            ang_rz: +(ag.rotation.z * 180/Math.PI).toFixed(3)
        };
    });

    // ── 2) 각 STL 메시의 world 바운딩박스 (어디까지 뻗어있나) ─────────────
    var linkBB = {};
    var T3 = T;
    ['S','L','U','R','B','T'].forEach(function(axName) {
        var ag = gj[axName];
        if(!ag) return;
        var box = new T3.Box3();
        var hasMesh = false;
        ag.children.forEach(function(c) {
            if(c.isMesh && c.geometry && c.geometry.attributes && c.geometry.attributes.position) {
                // 이 메시의 world bounding box
                var mb = new T3.Box3();
                mb.setFromObject(c);
                box.union(mb);
                hasMesh = true;
            }
        });
        if(hasMesh) {
            var cen = new T3.Vector3();
            var siz = new T3.Vector3();
            box.getCenter(cen);
            box.getSize(siz);
            linkBB[axName] = {
                center: [+(cen.x*1000).toFixed(2), +(cen.y*1000).toFixed(2), +(cen.z*1000).toFixed(2)],
                size:   [+(siz.x*1000).toFixed(2), +(siz.y*1000).toFixed(2), +(siz.z*1000).toFixed(2)],
                min: [+(box.min.x*1000).toFixed(2), +(box.min.y*1000).toFixed(2), +(box.min.z*1000).toFixed(2)],
                max: [+(box.max.x*1000).toFixed(2), +(box.max.y*1000).toFixed(2), +(box.max.z*1000).toFixed(2)]
            };
        }
    });

    // base STL bb
    var baseBox = new T3.Box3();
    var baseHas = false;
    var rg = app._robotGrp;
    rg.children.forEach(function(c) {
        if(c.isMesh && c.geometry && c.geometry.attributes && c.geometry.attributes.position) {
            var mb = new T3.Box3(); mb.setFromObject(c); baseBox.union(mb); baseHas=true;
        }
    });
    if(baseHas) {
        var bc = new T3.Vector3(); var bs = new T3.Vector3();
        baseBox.getCenter(bc); baseBox.getSize(bs);
        linkBB['base'] = {
            center: [+(bc.x*1000).toFixed(2), +(bc.y*1000).toFixed(2), +(bc.z*1000).toFixed(2)],
            size:   [+(bs.x*1000).toFixed(2), +(bs.y*1000).toFixed(2), +(bs.z*1000).toFixed(2)]
        };
    }

    // ── 3) 관절 간 거리 (piv 기준) ───────────────────────────────────────
    var pivSeq = ['S','L','U','R','B','T'];
    var gapSummary = {};
    for(var i=1; i<pivSeq.length; i++) {
        var from = pivSeq[i-1], to = pivSeq[i];
        var fp = joints[from] && joints[from].piv_world;
        var tp = joints[to]   && joints[to].piv_world;
        if(fp && tp) gapSummary[from+'->'+to] = dist(fp, tp);
    }

    // ── 4) STL 중심과 연결 예상 중점의 차이 ──────────────────────────────
    // 각 링크 STL 중심이 (joint_from + joint_to) / 2 와 얼마나 다른지
    var midDiff = {};
    var pairs = [
        ['S', null, 'L'],   // base→S 링크 STL 중심 vs (robotBase + S_piv) 중점
        ['L', 'S', 'U'],
        ['U', 'L', 'R'],    // 이게 핵심 — U링크 STL 중심이 U piv~R piv 중간에 있어야
        ['R', 'U', 'B'],
        ['B', 'R', 'T'],
    ];
    pairs.forEach(function(p) {
        var ax=p[0], from=p[1], to=p[2];
        var bb = linkBB[ax];
        if(!bb) return;
        var jp_to = joints[to] && joints[to].piv_world;
        var jp_from = from ? (joints[from] && joints[from].piv_world) : null;
        if(!jp_to) return;
        var expected_center = jp_from
            ? [(jp_from[0]+jp_to[0])/2, (jp_from[1]+jp_to[1])/2, (jp_from[2]+jp_to[2])/2]
            : jp_to; // S 링크는 base~S 사이 — 근사값
        var d = dist(bb.center, expected_center);
        midDiff[ax+'_link'] = {
            stl_center: bb.center,
            expected_mid: expected_center.map(function(v){return +v.toFixed(2);}),
            diff_mm: d
        };
    });

    return { joints, linkBB, gapSummary, midDiff };
}
"""

JS_SET_CAM = """
(args) => {
    var app=window.__dzw;
    if(!app||!app.orbit) return false;
    app.orbit.theta = args.theta;
    app.orbit.phi   = args.phi;
    app.orbit.r     = args.r;
    if(app.orbit.tgt) app.orbit.tgt.set(0, 0, args.tgt_z);
    if(app.updateCam) app.updateCam();
    return true;
}
"""

async def main():
    async with async_playwright() as p:
        br = await p.chromium.launch(headless=True)
        pg = await br.new_page()
        await pg.set_viewport_size({'width': 1536, 'height': 1024})
        await pg.goto('http://localhost:8090/DOZIKWORKS_OS_v4_pathcreate.dc.html')
        await pg.wait_for_timeout(20000)   # STL 로드 대기

        # ── 데이터 수집 ──────────────────────────────────────────────────
        r = await pg.evaluate(JS_MAIN)

        print('=' * 70)
        print('1) 각 관절 pivot world 위치 (mm, 82.8mm 베이스 포함)')
        print('=' * 70)
        for ax in ['S','L','U','R','B','T']:
            j = r['joints'].get(ax, {})
            print(f"  {ax}: piv={j.get('piv_world')}  ang.rz={j.get('ang_rz')}°")

        print()
        print('=' * 70)
        print('2) 관절 간 거리 (mm) — 이 값이 U링크 길이 등 실제 링크 길이임')
        print('=' * 70)
        for k, v in r['gapSummary'].items():
            print(f"  {k}: {v} mm")

        print()
        print('=' * 70)
        print('3) STL 메시 world 바운딩박스 중심 (mm)')
        print('=' * 70)
        for ax in ['base','S','L','U','R','B','T']:
            bb = r['linkBB'].get(ax)
            if bb:
                print(f"  {ax}: center={bb['center']}  size={bb['size']}")
            else:
                print(f"  {ax}: 메시 없음")

        print()
        print('=' * 70)
        print('4) STL 중심 vs 연결 예상 중점 차이 (diff 크면 STL이 잘못 향함)')
        print('=' * 70)
        for k, v in r['midDiff'].items():
            flag = '  ★ 큰 오차!' if v['diff_mm'] > 100 else ''
            print(f"  {k}: STL중심={v['stl_center']}  예상중점={v['expected_mid']}  diff={v['diff_mm']}mm{flag}")

        print()
        print('=' * 70)
        print('5) 4방향 스크린샷 (눈으로 연결 확인)')
        print('=' * 70)

        for (name, theta, phi, r_val, tgt_z) in CAMERAS:
            await pg.evaluate(JS_SET_CAM, {'theta': theta, 'phi': phi, 'r': r_val, 'tgt_z': tgt_z})
            await pg.wait_for_timeout(300)
            fname = f'robot_{name.lower()}.png'
            await pg.screenshot(path=fname)
            print(f"  {name} → {fname}")

        await br.close()

asyncio.run(main())
