#!/usr/bin/env python3
"""Apply reversions by REBUILDING each page's text cleanly (layout not preserved).

For every page: take its words (with diacritics, in reading order), replace the
matched VEDA word-spans with re-accented GOLD, then re-render the page's text into
a fresh same-size page (GentiumPlus, auto-fit font). Page count is preserved so the
master_ranges still locate each book for the re-pass. No geometry artifacts.
"""
import fitz, re, unicodedata, json, os, collections

BASE = os.path.dirname(os.path.abspath(__file__))
MASTER = os.path.join(BASE, "master.pdf")
OUT = os.path.join(BASE, "master_corrected2.pdf")
FONT = os.path.expanduser("~/Library/Fonts/GentiumPlus-Regular.ttf")
RANGES = json.load(open(os.path.join(BASE, "master_ranges.json")))
amap = json.load(open(os.path.join(BASE, "accent_map.json")))
IASTPAT = re.compile(r"[A-Za-z']+")

def fold(s):
    s = unicodedata.normalize('NFKD', s)
    return ''.join(c for c in s if not unicodedata.combining(c)).lower()

def reaccent(s):
    return IASTPAT.sub(lambda m: amap.get(fold(m.group(0)), m.group(0)), s)

def parse_revs():
    revs = collections.defaultdict(list); cur = None
    for line in open(os.path.join(BASE, "reversiones_master.md")):
        h = re.match(r'^## (\S+)', line)
        if h: cur = h.group(1)
        d = re.match(r'^- en vedabase «(.*?)»  ->  restaurar «(.*?)»', line)
        if d and cur: revs[cur].append((d.group(1), d.group(2)))
    return revs

def main():
    M = fitz.open(MASTER)
    revs = parse_revs()
    # collect per-page edits: page -> list of (word_start, word_len, gold_words)
    page_edits = collections.defaultdict(list)
    applied = notfound = 0
    # precompute folded words per page (only for pages in any book range)
    needed = set()
    for k in revs:
        if k in RANGES:
            needed.update(range(RANGES[k]["vedabase_start"], min(RANGES[k]["vedabase_end"], len(M))))
    words = {i: M[i].get_text("words") for i in needed}
    fwords = {i: [fold(w[4]) for w in words[i]] for i in needed}
    for k, items in revs.items():
        if k not in RANGES:
            notfound += len(items); continue
        s, e = RANGES[k]["vedabase_start"], min(RANGES[k]["vedabase_end"], len(M))
        for veda, gold in items:
            target = fold(veda).split()
            if not target: notfound += 1; continue
            placed = False
            for i in range(s, e):
                fw = fwords[i]
                for j in range(len(fw) - len(target) + 1):
                    if fw[j:j+len(target)] == target:
                        page_edits[i].append((j, len(target), reaccent(gold).split()))
                        placed = True; break
                if placed: break
            applied += placed; notfound += (not placed)
    print(f"localizadas: {applied} | no: {notfound}", flush=True)

    # build corrected PDF
    out = fitz.open()
    for i in range(M.page_count):
        src = M[i]
        rect = src.rect
        np = out.new_page(width=rect.width, height=rect.height)
        if i in page_edits:
            wl = [w[4] for w in words[i]]
            for j, ln, gw in sorted(page_edits[i], key=lambda x: -x[0]):  # apply right-to-left
                wl[j:j+ln] = gw
            text = ' '.join(wl)
        else:
            text = src.get_text()
        text = text.strip()
        if not text:
            continue
        box = fitz.Rect(36, 36, rect.width-36, rect.height-36)
        np.insert_font(fontname="GP", fontfile=FONT)
        fs = 9.0
        while fs >= 4:
            rc = np.insert_textbox(box, text, fontname="GP", fontfile=FONT,
                                    fontsize=fs, align=0)
            if rc >= 0: break
            # overflow: clear page and retry smaller
            np.clean_contents()
            fs -= 0.5
    out.save(OUT, deflate=True, garbage=3)
    print(f"-> {OUT} ({os.path.getsize(OUT)//1024//1024} MB, {out.page_count} pp)", flush=True)

if __name__ == "__main__":
    main()
