#!/usr/bin/env python3
"""Apply the selected reversions to master.pdf, preserving diacritics.

GOLD (from the scan, diacritic-stripped) is re-accented using an accent map built
from the master itself (e.g. Laksmana->Lakṣmaṇa). VEDA text is located by words
(handles line breaks), redacted, and replaced with re-accented GOLD using
GentiumPlus (the master's own font). Output: master_corrected.pdf in this folder.
"""
import fitz, re, unicodedata, json, os, collections, sys

BASE = os.path.dirname(os.path.abspath(__file__))
MASTER = os.path.join(BASE, "master.pdf")
OUT = os.path.join(BASE, "master_corrected.pdf")
FONT = os.path.expanduser("~/Library/Fonts/GentiumPlus-Regular.ttf")
REVS = os.path.join(BASE, "reversiones_master.md")
RANGES = json.load(open(os.path.join(BASE, "master_ranges.json")))
IAST = "ĀāĪīŪūṚṛṜṝḶḷḸḹṄṅÑñṬṭḌḍṆṇŚśṢṣṀṁḤḥṂ"

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

def build_accent_map(M):
    cache = os.path.join(BASE, "accent_map.json")
    if os.path.exists(cache):
        return json.load(open(cache))
    seen = collections.defaultdict(collections.Counter)
    pat = re.compile(rf"[A-Za-z{IAST}']+")
    for i in range(M.page_count):
        for w in pat.findall(M[i].get_text()):
            if any(ord(c) > 127 for c in w):
                seen[fold(w)][w] += 1
    amap = {k: c.most_common(1)[0][0] for k, c in seen.items()}
    json.dump(amap, open(cache, "w"))
    return amap

def parse_revs():
    revs = collections.defaultdict(list); cur = None
    for line in open(REVS):
        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)
    amap = build_accent_map(M)
    print(f"accent map: {len(amap)} words", flush=True)
    def reaccent(s):
        return re.sub(r"[A-Za-z']+", lambda m: amap.get(fold(m.group(0)), m.group(0)), s)

    revs = parse_revs()
    applied = notfound = 0
    miss = []
    # group corrections per page to redact+insert efficiently
    for k, items in revs.items():
        if k not in RANGES:
            for veda, gold in items: miss.append((k, veda)); notfound += 1
            continue
        s, e = RANGES[k]["vedabase_start"], RANGES[k]["vedabase_end"]
        # cache words per page in range
        pwords = {i: M[i].get_text("words") for i in range(s, min(e, len(M)))}
        pfold = {i: [fold(w[4]) for w in pwords[i]] for i in pwords}
        edits = collections.defaultdict(list)   # page -> list of (rects, gold)
        for veda, gold in items:
            target = fold(veda).split()
            if not target: notfound += 1; continue
            placed = False
            for i in range(s, min(e, len(M))):
                fw = pfold[i]
                for j in range(len(fw) - len(target) + 1):
                    if fw[j:j+len(target)] == target:
                        rects = [fitz.Rect(pwords[i][j+t][:4]) for t in range(len(target))]
                        edits[i].append((rects, reaccent(gold)))
                        placed = True; break
                if placed: break
            if placed: applied += 1
            else: notfound += 1; miss.append((k, veda))
        # apply per page: redact all, then insert all
        for i, lst in edits.items():
            pg = M[i]
            for rects, gold in lst:
                for r in rects: pg.add_redact_annot(r)
            pg.apply_redactions()
            for rects, gold in lst:
                r0 = rects[0]
                fs = max(7, min(11, round((r0.y1 - r0.y0) * 0.82, 1)))
                pg.insert_text((r0.x0, r0.y1 - 1), gold, fontfile=FONT,
                               fontname="GP", fontsize=fs)
    M.save(OUT, deflate=True, garbage=3)
    print(f"APLICADAS: {applied}  | no encontradas: {notfound}", flush=True)
    print(f"-> {OUT} ({os.path.getsize(OUT)//1024//1024} MB)", flush=True)
    if miss:
        print("\nEjemplos no encontrados:")
        for k, v in miss[:12]: print(f"  [{k}] «{v[:55]}»")

if __name__ == "__main__":
    main()
