"""
12500_3d.dxf → dxf3d_12500.json 변환
LINE / POLYLINE(VERTEX) / ARC / CIRCLE → 선분 배열
Z값 보존 (3D)
"""
import json, math, os

SRC = os.path.join(os.path.dirname(__file__), '12500_3d.dxf')
DST = os.path.join(os.path.dirname(__file__), 'dxf3d_12500.json')

def arc_segs(cx,cy,cz, r, a0, a1, n=24):
    if a1 <= a0: a1 += 360
    segs = []
    da = (a1-a0)/n
    pts = [(cx+r*math.cos(math.radians(a0+i*da)),
            cy+r*math.sin(math.radians(a0+i*da)), cz) for i in range(n+1)]
    for i in range(len(pts)-1):
        segs.append([*pts[i], *pts[i+1]])
    return segs

def circle_segs(cx,cy,cz, r, n=24):
    segs = []
    pts = [(cx+r*math.cos(2*math.pi*i/n), cy+r*math.sin(2*math.pi*i/n), cz) for i in range(n)]
    for i in range(n):
        segs.append([*pts[i], *pts[(i+1)%n]])
    return segs

with open(SRC, encoding='utf-8', errors='replace') as f:
    lines = [l.rstrip('\n') for l in f]

segments = []  # [[x1,y1,z1,x2,y2,z2], ...]
skipped = {'ARC':0,'CIRCLE':0,'OTHER':0}

i = 0
N = len(lines)
def nxt():
    global i
    code = lines[i].strip(); val = lines[i+1].strip() if i+1<N else ''
    i += 2
    return code, val

entity = None
props = {}

while i < N-1:
    code, val = nxt()
    if code == '0':
        # 이전 엔티티 처리
        if entity == 'LINE':
            try:
                x1=float(props.get('10',0)); y1=float(props.get('20',0)); z1=float(props.get('30',0))
                x2=float(props.get('11',0)); y2=float(props.get('21',0)); z2=float(props.get('31',0))
                segments.append([x1,y1,z1,x2,y2,z2])
            except: pass
        elif entity == 'ARC':
            try:
                cx=float(props.get('10',0)); cy=float(props.get('20',0)); cz=float(props.get('30',0))
                r=float(props.get('40',1))
                a0=float(props.get('50',0)); a1=float(props.get('51',360))
                segments.extend(arc_segs(cx,cy,cz,r,a0,a1))
            except: skipped['ARC']+=1
        elif entity == 'CIRCLE':
            try:
                cx=float(props.get('10',0)); cy=float(props.get('20',0)); cz=float(props.get('30',0))
                r=float(props.get('40',1))
                segments.extend(circle_segs(cx,cy,cz,r))
            except: skipped['CIRCLE']+=1
        # POLYLINE은 VERTEX 누적으로 별도 처리
        entity = val
        props = {}
    elif entity == 'VERTEX':
        props[code] = val
        if code == '0':  # 다음 엔티티 시작 → 이미 위에서 처리됨
            pass
    else:
        props[code] = val

# POLYLINE/VERTEX 별도 파싱
print("POLYLINE 파싱 중...")
poly_segs = []
i2 = 0
in_poly = False
verts = []
poly_closed = False

while i2 < N-1:
    code = lines[i2].strip(); val = lines[i2+1].strip() if i2+1<N else ''; i2+=2
    if code == '0':
        if val == 'POLYLINE':
            in_poly = True; verts = []; poly_closed = False
        elif val == 'VERTEX' and in_poly:
            verts.append({})
        elif val == 'SEQEND' and in_poly:
            # Polyface mesh(70=64): 좌표 정점(70&192==192)과 face index(70&128,not 64) 분리
            coord_verts = []
            face_verts = []
            for v in verts:
                try: v70 = int(v.get('70', 0))
                except: v70 = 0
                if v70 & 64:  # 좌표 정점 (AcDbPolyFaceMeshVertex)
                    try:
                        x=float(v.get('10',0)); y=float(v.get('20',0)); z=float(v.get('30',0))
                        coord_verts.append((x,y,z))
                    except: pass
                elif v70 & 128:  # face index 정점
                    try:
                        idxs = [int(v.get(str(c),0)) for c in [71,72,73,74]]
                        face_verts.append(idxs)
                    except: pass
            if face_verts:
                # Polyface mesh: face index로 엣지 생성
                for idxs in face_verts:
                    pts_f = [abs(ix)-1 for ix in idxs if ix != 0]
                    for k in range(len(pts_f)):
                        a = pts_f[k]; b = pts_f[(k+1)%len(pts_f)]
                        if 0<=a<len(coord_verts) and 0<=b<len(coord_verts):
                            poly_segs.append([*coord_verts[a], *coord_verts[b]])
            elif coord_verts:
                # 일반 3D 폴리라인: 순차 연결
                for k in range(len(coord_verts)-1):
                    poly_segs.append([*coord_verts[k],*coord_verts[k+1]])
                if poly_closed and len(coord_verts)>2:
                    poly_segs.append([*coord_verts[-1],*coord_verts[0]])
            in_poly = False
        elif in_poly and val not in ('VERTEX','SEQEND','POLYLINE'):
            in_poly = False
    elif in_poly and verts:
        verts[-1][code] = val
    elif in_poly and code == '70':
        try:
            if int(val) & 1: poly_closed = True
        except: pass

segments.extend(poly_segs)

# 통계
xs = [s[0] for s in segments]+[s[3] for s in segments]
ys = [s[1] for s in segments]+[s[4] for s in segments]
zs = [s[2] for s in segments]+[s[5] for s in segments]

stats = {
    'count': len(segments),
    'bbox': {
        'xmin':min(xs),'xmax':max(xs),
        'ymin':min(ys),'ymax':max(ys),
        'zmin':min(zs),'zmax':max(zs)
    },
    'skipped': skipped
}

print(f"세그먼트: {len(segments)}개")
print(f"bbox X:{min(xs):.1f}~{max(xs):.1f} Y:{min(ys):.1f}~{max(ys):.1f} Z:{min(zs):.1f}~{max(zs):.1f}")
print(f"skipped: {skipped}")

# 일부(처음 2000개)와 전체 둘 다 저장
out = {'segments': segments, 'stats': stats}
out_preview = {'segments': segments[:2000], 'stats': stats, 'preview': True}

with open(DST, 'w', encoding='utf-8') as f:
    json.dump(out, f, ensure_ascii=False)

with open(DST.replace('.json','_preview.json'), 'w', encoding='utf-8') as f:
    json.dump(out_preview, f, ensure_ascii=False)

print(f"저장: {DST}")
print(f"미리보기(2000개): {DST.replace('.json','_preview.json')}")
