#!/usr/bin/env python3
"""Classify candidates into a concrete field list using emptiness + geometry.
Writes fields.json and renders render/fld_pNN.png overlays of ACCEPTED fields only."""
import fitz, json
doc = fitz.open("data/Level2_WhatChristiansBelieve.pdf")
rep = json.load(open("candidates.json"))

def has_text(page, rect, pad=0.5):
    r = fitz.Rect(rect.x0-pad, rect.y0-pad, rect.x1+pad, rect.y1+pad)
    t = page.get_text("text", clip=r).strip()
    # ignore stray underscores/dots used as blanks
    t = t.replace("_","").replace(".","").replace("·","").strip()
    return len(t) > 0

fields = {}   # page -> list of {type,name,rect,maxlen?}
for pi, page in enumerate(doc):
    r = rep[str(pi)]
    H = page.rect.height; W = page.rect.width
    flist = []
    # ---- GRIDS: empty cells -> 1-char; skip word-search (mostly filled) ----
    for gi, g in enumerate(r["grids"]):
        cells = [fitz.Rect(*c) for c in g]
        filled = sum(1 for c in cells if has_text(page, c))
        if filled > 0.55*len(cells):
            continue  # word search / already-filled -> not interactive
        for ci, c in enumerate(cells):
            if has_text(page, c):
                continue  # given letter
            flist.append({"type":"cell","name":f"p{pi:02d}_g{gi}_c{ci}",
                          "rect":[c.x0,c.y0,c.x1,c.y1]})
    # ---- BOXES: empty writable rects -> text ----
    for k,(x0,y0,x1,y1) in enumerate(r["boxes"]):
        rect = fitz.Rect(x0,y0,x1,y1)
        if has_text(page, rect):
            continue
        if rect.width < 28 or rect.height < 8:
            continue
        flist.append({"type":"text","name":f"p{pi:02d}_box{k}",
                      "rect":[x0,y0,x1,y1]})
    # ---- LONE SQUARES (score boxes / single letter boxes): empty -> text ----
    for k,(x0,y0,x1,y1) in enumerate(r["lone_sq"]):
        rect = fitz.Rect(x0,y0,x1,y1)
        if has_text(page, rect):
            continue
        flist.append({"type":"text","name":f"p{pi:02d}_sq{k}",
                      "rect":[x0,y0,x1,y1]})
    # ---- LINES: empty band above the line -> text ----
    for k,(x0,x1,y) in enumerate(r["lines"]):
        if y < 60:               # header rule
            continue
        bw = x1-x0
        band = fitz.Rect(x0, y-13, x1, y-1.5)
        if has_text(page, band):
            continue             # decorative underline under title / label
        flist.append({"type":"text","name":f"p{pi:02d}_line{k}",
                      "rect":[x0, y-14, x1, y-1.0]})
    fields[pi] = flist

json.dump(fields, open("fields.json","w"), indent=0)

# overlay accepted fields
doc2 = fitz.open("data/Level2_WhatChristiansBelieve.pdf")
tot=0
for pi, page in enumerate(doc2):
    for f in fields[pi]:
        c = {"cell":(0,0,0.9),"text":(0,0.55,0)}[f["type"]]
        page.draw_rect(fitz.Rect(*f["rect"]), color=c, width=1.0)
    pix=page.get_pixmap(matrix=fitz.Matrix(150/72,150/72))
    pix.save(f"render/fld_p{pi:02d}.png")
    tot+=len(fields[pi])

for pi in range(doc.page_count):
    cells=sum(1 for f in fields[pi] if f["type"]=="cell")
    texts=sum(1 for f in fields[pi] if f["type"]=="text")
    print(f"p{pi:2d}: text={texts:2d} cells={cells:2d}")
print("TOTAL fields:", tot)
