"""Накласти клікабельні області малюнка на ГОТОВИЙ інтерактивний урок.

На відміну від прототипів у `verify/`, які будують тестову сторінку з нуля,
цей скрипт додає області до вже зібраного уроку і **зливає** їх з наявними
полями, а не замінює: сторінка вже має свої віджети, і AcroForm теж.

Конфігурація ратифікована (див. §3.2a специфікації): адаптивне розбиття 2 pt
без перекриття, непрозора фарба, спільний растровий трафарет на область.

    .venv/bin/python ua_build/art_apply.py editor/regions_p16_q2_full.json
"""
import argparse
import json
import os
import re
import sys

import fitz

HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)
sys.path.insert(0, os.path.join(ROOT, 'verify'))

import highlight_test as HT      # noqa: E402  трафарет, растр, widget_obj
import highlight_grid as HG      # noqa: E402  build_stencils
import highlight_adaptive as HA  # noqa: E402  add_adaptive

SRC = os.path.join(ROOT, 'forms', 'Level2_UA_form_v06.pdf')
STEM = 'Level2_UA_form'
STEP = 2.0


def next_version(d, stem):
    used = [int(m.group(1)) for f in os.listdir(d)
            for m in [re.fullmatch(re.escape(stem) + r'_v(\d+)\.pdf', f)] if m]
    return os.path.join(d, '%s_v%02d.pdf' % (stem, max(used, default=0) + 1))


def xrefs(value):
    return re.findall(r'(\d+) 0 R', value or '')


def merge_annots(doc, page, new):
    """Дописати анотації до наявних, а не затерти їх."""
    cur = doc.xref_get_key(page.xref, 'Annots')
    old = xrefs(cur[1]) if cur[0] == 'array' else []
    doc.xref_set_key(page.xref, 'Annots', '[%s]' % ' '.join(
        ['%s 0 R' % x for x in old] + ['%d 0 R' % x for x in new]))
    return len(old)


def merge_fields(doc, new):
    """Дописати поля в AcroForm/Fields; NeedAppearances лишити false."""
    af = doc.xref_get_key(doc.pdf_catalog(), 'AcroForm')
    old = []
    if af[0] == 'dict':
        m = re.search(r'/Fields\s*\[(.*?)\]', af[1], re.S)
        old = xrefs(m.group(1)) if m else []
    elif af[0] == 'xref':
        old = xrefs(doc.xref_get_key(int(af[1].split()[0]), 'Fields')[1])
    doc.xref_set_key(doc.pdf_catalog(), 'AcroForm/Fields', '[%s]' % ' '.join(
        ['%s 0 R' % x for x in old] + ['%d 0 R' % x for x in new]))
    doc.xref_set_key(doc.pdf_catalog(), 'AcroForm/NeedAppearances', 'false')
    return len(old)


def apply(src, json_path, out=None, step=STEP):
    j = json.load(open(json_path, encoding='utf-8'))
    pno = int(j.get('page', 16)) - 1
    regs = sorted(j['regions'],
                  key=lambda r: (HT.bbox(r).get_area(), HT.region_area(r)),
                  reverse=True)
    doc = fitz.open(src)
    page = doc[pno]

    art = HT.bbox(regs[0])
    for r in regs:
        art |= HT.bbox(r)

    # растр малюнка — потрібен для трафаретів
    scale = 72.0 / HT.RASTER_DPI
    clip = fitz.Rect(art.x0 - 4, art.y0 - 4, art.x1 + 4, art.y1 + 4)
    pix = page.get_pixmap(dpi=HT.RASTER_DPI, clip=clip, colorspace=fitz.csGRAY)
    paper = bytearray(1 if v >= HT.INK else 0 for v in pix.samples)
    pw, ph = pix.width, pix.height
    bg = HT.page_background(paper, pw, ph)
    ink_ok = bytearray(paper[i] and not bg[i] for i in range(pw * ph))

    st = HG.build_stencils(doc, regs, ink_ok, pw, ph, ox := clip.x0, oy := clip.y0, scale)
    task = str(j.get('task', 'q2'))
    if not task.startswith('q'):
        task = 'q' + task
    prefix = j.get('prefix') or 'p%02d_%s' % (pno + 1, task)
    ann, fld, lay, per = HA.add_adaptive(doc, regs, st, step, prefix)

    n_ann = merge_annots(doc, page, ann)
    n_fld = merge_fields(doc, fld)

    out = out or next_version(os.path.join(ROOT, 'forms'), STEM)
    doc.save(out, deflate=True, garbage=3)
    return out, dict(page=pno + 1, regions=len(regs), widgets=len(ann),
                     kept_annots=n_ann, kept_fields=n_fld,
                     art=[round(v, 1) for v in art], per=per)


def main(argv=None):
    ap = argparse.ArgumentParser()
    ap.add_argument('json')
    ap.add_argument('--src', default=SRC)
    ap.add_argument('--out')
    ap.add_argument('--step', type=float, default=STEP)
    a = ap.parse_args(argv)
    out, info = apply(a.src, a.json, a.out, a.step)
    print('стор. %d: %d областей -> %d віджетів (клітинка %g pt)'
          % (info['page'], info['regions'], info['widgets'], a.step))
    print('збережено наявних: анотацій %d, полів %d'
          % (info['kept_annots'], info['kept_fields']))
    print('малюнок:', info['art'])
    print('збережено ->', out)
    return out


if __name__ == '__main__':
    main()
