# -*- coding: utf-8 -*-
"""Pass 5: 도번 텍스트와 두께 텍스트를 '같은 콜아웃 블록' 안에서만 짝지운다.
   ±500mm 근접 매칭은 이웃 부재의 두께를 잘못 붙이므로 금지.
   블록 = X가 ~40mm 이내, Y가 ~60mm 이내로 뭉친 텍스트 줄들.
   블록 안에 도번 1개 + 두께 1개일 때만 채택. 그 외는 SKIP."""
import json, re, collections

import product          # 제품 고유값 모음 — 기본값 500pro
SCR = product.SCR
T = json.load(open(SCR + r'\index.json', encoding='utf-8'))['texts']

# --- MTEXT 서식코드 제거 ---
def clean(s):
    s = re.sub(r'\{\\[fF][^;]*;', '', s)      # {\fGulim|b0|...;
    s = re.sub(r'\\[A-Za-z][^;\\]*;', '', s)  # \H1.5x; \pxq; 등
    s = s.replace('{', ' ').replace('}', ' ').replace('\\P', ' ')
    return re.sub(r'\s+', ' ', s).strip()

PN  = product.PN            # 도번 형식은 제품마다 다르다 (product.py 참조)
# 두께 표기. 앞뒤 경계에 \b 를 쓰면 "SS3.2T" 같이 글자에 숫자가 붙은 표기에서
# 소수점 뒤 "2T" 만 잡아 3.2 -> 2.0 으로 값이 망가진다. 앞은 '숫자·점이 아님'으로만 막는다.
THK = re.compile(r'(?:(?:PL|PLATE|P)\s*[-.]?\s*(\d{1,3}(?:\.\d)?)(?![\d.]))'
                 r'|(?:(?<![\d.])(\d{1,3}(?:\.\d)?)\s*[Tt](?![A-Za-z0-9]))')
# "환봉 Φ6T" 는 봉 지름이지 판 두께가 아니다.
ROD = re.compile(r'환봉|Φ|%%c|φ')
# 판재가 아닌 단면(형강·각관·채널·파이프) — 숫자가 '살두께'라 평판으로 압출하면 형상이 틀린다.
# pass5b(BOM)에는 원래 이 제외가 있었으나 콜아웃 경로에는 없어서, 표제란 SPEC. 칸의
# 'PIPE 100X50X4.5' 가 4.5T 평판으로 새어 들어왔다(D800 RJ102-0091). 같은 기준을 여기도 맞춘다.
SECTION = re.compile(r'SPSR|CHANNEL|PIPE|ANGLE|H-?BEAM|C-?CHAN|\bSQ\b', re.I)
SECTION_DIM = re.compile(r'\d+\s*[x*×]\s*\d+\s*[x*×]\s*[.\-]?\s*(?:PL|T)?\s*\d', re.I)

def thk_of(s):
    if ROD.search(s):
        return []
    if SECTION.search(s) or SECTION_DIM.search(s):
        return []
    out = []
    for m in THK.finditer(s):
        v = m.group(1) or m.group(2)
        v = float(v)
        if 3 <= v <= 80:            # 판재 두께로 말이 되는 범위만
            out.append(v)
    return out

rows = []
for h, lay, x, y, s in T:
    c = clean(s)
    rows.append(dict(x=x, y=y, s=c, raw=s, pn=PN.findall(c), thk=thk_of(c)))

# --- 블록 묶기: X 40mm, Y 60mm 이내 이웃끼리 union ---
par = list(range(len(rows)))
def find(a):
    while par[a] != a: par[a] = par[par[a]]; a = par[a]
    return a
def uni(a, b):
    ra, rb = find(a), find(b)
    if ra != rb: par[ra] = rb

grid = collections.defaultdict(list)
for i, r in enumerate(rows):
    grid[(int(r['x']//40), int(r['y']//60))].append(i)
for (gx, gy), idxs in grid.items():
    for dx in (-1, 0, 1):
        for dy in (-1, 0, 1):
            for j in grid.get((gx+dx, gy+dy), []):
                for i in idxs:
                    if abs(rows[i]['x']-rows[j]['x']) <= 40 and abs(rows[i]['y']-rows[j]['y']) <= 60:
                        uni(i, j)

blocks = collections.defaultdict(list)
for i in range(len(rows)):
    blocks[find(i)].append(i)

ok, skip = [], []
for root, idxs in blocks.items():
    pns = sorted({p for i in idxs for p in rows[i]['pn']})
    ths = sorted({t for i in idxs for t in rows[i]['thk']})
    if not pns:
        continue
    lines = [rows[i]['s'] for i in sorted(idxs, key=lambda i: -rows[i]['y'])]
    xs = [rows[i]['x'] for i in idxs]; ys = [rows[i]['y'] for i in idxs]
    rec = dict(pn=pns, thk=ths, at=[min(xs), min(ys)], lines=lines)
    if len(pns) == 1 and len(ths) == 1:
        ok.append(rec)
    else:
        skip.append(rec)

ok.sort(key=lambda r: r['pn'][0])
print('=== 채택 (도번1 + 두께1) : %d ===' % len(ok))
for r in ok:
    print('%-20s %5.1fT @(%.0f,%.0f)  | %s' % (r['pn'][0], r['thk'][0], r['at'][0], r['at'][1], ' / '.join(r['lines'])[:110]))
print()
print('=== 보류 (모호) : %d ===' % len(skip))
for r in skip[:40]:
    print('pn=%s thk=%s @(%.0f,%.0f) | %s' % (r['pn'], r['thk'], r['at'][0], r['at'][1], ' / '.join(r['lines'])[:100]))

json.dump(dict(ok=ok, skip=skip), open(SCR + r'\thk_blocks.json', 'w', encoding='utf-8'), ensure_ascii=False)
