#!/usr/bin/env python3
"""Compara PAREJA A PAREJA las glosas del repo con las del impreso.

Por que no basta el diff. El alineador compara dos chorros de palabras y avisa
donde se separan, asi que una glosa larga se parte por la mitad y aparece como
tres candidatos distintos: en sb/1/18/42 salieron «by the prowess»/«deserve» y
«of whom»/«by whose» como si fueran cosas sueltas. Puestas en su pareja
clave—glosa se ve de golpe lo que pasa:

    impreso   arhasi—deserve          yat—by whose
    repo      arhasi—by the prowess   yat—of whom

El repo le habia puesto a `arhasi` la glosa de `tejasā`, que viene dos parejas
mas alla. Es un salto de copia, y solo se ve emparejado.

Como localiza el verso. Por su transliteracion, que es unica en el libro; la
glosa suelta no vale de ancla porque «fearful» sale en sb/1/6 hablando de
bosques y no dice nada de sb/1/14/19.

Como lee las glosas del impreso. El OCR llega sin guiones ni puntos y coma:
«arhasi deserve avipakva unripe or immature buddhe intelligence». Se parte
usando las CLAVES del repo como cortes, que se conocen de antemano, en vez de
adivinar donde acaba cada glosa.

Uso:
    python3 comparar_glosas.py sb/1/18/42          # un verso
    python3 comparar_glosas.py --lista             # los versos con glosas abiertas
"""
import json
import pathlib
import re
import sys

HERE = pathlib.Path(__file__).resolve().parent
sys.path.insert(0, str(HERE))
from arbitrar_deriva import texto_escaneo, LIBRO, REPO  # noqa: E402
from cerrar_glosas import sd  # noqa: E402

_t = {}


def texto(libro):
    if libro not in _t:
        _t[libro] = sd(texto_escaneo(libro) or '')
    return _t[libro]


def parejas_repo(f):
    """[(clave, glosa)] tal como los trae el .md."""
    linea = next((l for l in f.read_text(encoding='utf-8').splitlines()
                  if l.count('—') > 2), '')
    fuera = []
    for t in linea.split(';'):
        t = t.strip().replace('*', '')
        if '—' in t:
            k, v = t.split('—', 1)
            fuera.append((k.strip(), v.strip().rstrip('.')))
    return fuera


def region_impreso(libro, f, largo=2600):
    for l in f.read_text(encoding='utf-8').splitlines():
        l = l.strip()
        if l.startswith('>') and not re.search(r'[ऀ-ॿঀ-৿]', l):
            piezas = sd(re.sub(r'[^\w\s]', ' ', l.lstrip('> '))).split()
            if len(piezas) >= 4:
                a = ' '.join(piezas[:5])
                m = re.search(r'(?<![\w])' + re.escape(a) + r'(?![\w])', texto(libro))
                if m:
                    reg = texto(libro)[m.start():m.start() + largo]
                    # Recortado por los dos lados. Por delante, saltando la
                    # ULTIMA linea del verso: si no, las primeras claves casan
                    # con la transliteracion —que las contiene— y salen como
                    # diferencias vacias. Por detras, cortando en «translation»:
                    # si no, la ultima glosa se lleva pegada media traduccion.
                    ult = ultima_linea_verso(f)
                    if ult:
                        mu = re.search(r'(?<![\w])' + re.escape(ult) + r'(?![\w])', reg)
                        if mu:
                            reg = reg[mu.end():]
                    corte = re.search(r'(?<![\w])(translation|purport)(?![\w])', reg)
                    return reg[:corte.start()] if corte else reg
    return None


def ultima_linea_verso(f):
    ult = None
    for l in f.read_text(encoding='utf-8').splitlines():
        l = l.strip()
        if l.startswith('>') and not re.search(r'[ऀ-ॿঀ-৿]', l):
            piezas = sd(re.sub(r'[^\w\s]', ' ', l.lstrip('> '))).split()
            if len(piezas) >= 3:
                ult = ' '.join(piezas[-4:])
    return ult


def parejas_impreso(region, claves):
    """Parte la region del impreso usando las claves del repo como cortes."""
    if not region:
        return {}
    pos = []
    desde = 0
    for k in claves:
        kn = sd(k).replace('-', ' ')
        m = re.search(r'(?<![\w])' + re.escape(kn) + r'(?![\w])', region[desde:])
        if not m:
            pos.append(None)
            continue
        pos.append((desde + m.start(), desde + m.end()))
        desde += m.end()
    fuera = {}
    for i, k in enumerate(claves):
        if not pos[i]:
            continue
        fin = next((pos[j][0] for j in range(i + 1, len(claves)) if pos[j]), None)
        fuera[k] = region[pos[i][1]:fin].strip() if fin else region[pos[i][1]:pos[i][1] + 60].strip()
    return fuera


def revisar(ref):
    # `ref` es la ruta del .md tal como la trae el ledger. Reconstruirla a
    # partir de un identificador no vale: «cc-antya-3.252-253» son dos versos en
    # un fichero y el nombre no se deja partir sin equivocarse.
    f = REPO / (ref if ref.endswith('.md') else ref + '.md')
    libro = CARP.get(ref.split('/')[0], '')
    if not f.is_file():
        print(f'  {ref}: no existe {f}')
        return []
    pr = parejas_repo(f)
    reg = region_impreso(libro, f)
    if not reg:
        print(f'  {ref}: el verso no se localiza en el escaneo')
        return []
    pi = parejas_impreso(reg, [k for k, _ in pr])
    difs = []
    for k, v in pr:
        w = pi.get(k)
        if w is None or not w:
            continue
        if not distinta(v, w):
            continue
        difs.append((k, v, w))
    return difs


ENCABEZADO = re.compile(r'\b\d{1,4}\s+(srimad bhagavatam|sri caitanya|bhagavad gita)\b.*'
                        r'|\btext \d+\b.*')


def limpia(s):
    """Deja solo las letras, para que el ruido del OCR no cuente como diferencia."""
    return re.sub(r'[^a-z]', '', sd(s))


def distinta(v, w):
    """¿Difieren de verdad el repo y el impreso en esta glosa?

    Se descartan antes las cuatro formas de ruido que ya conocemos, porque si no
    ahogan lo que importa: de 153 diferencias solo una parte son de texto.

      · el impreso llega sin parentesis ni mayusculas
        «mankind (the sons of Manu)» = «mankind the sons of manu»
      · el OCR parte palabras por la mitad
        «charming» leido «charm ing», «excellent» leido «ex cellent»
      · la glosa del impreso se come la siguiente clave cuando esa clave no se
        localizo: «smile janonmadakari the most alluring» empieza por «smile»,
        que es justo la glosa del repo, y lo demas es la pareja siguiente
      · se cuela el encabezado de la pagina
        «prowess 54 srimad bhagavatam canto 2 ch 1»
    """
    w = ENCABEZADO.sub('', w).strip()
    a, b = limpia(v), limpia(w)
    if not b:
        return False
    if a == b:
        return False
    # el impreso arrastra la pareja siguiente: empieza igual y sigue
    if b.startswith(a) and len(b) > len(a):
        return False
    # y el lado impreso tiene que ser ingles. Cuando la clave se localiza en el
    # sitio equivocado —dentro del verso, no en las glosas— lo que sale es
    # transliteracion: «paro dharmo hy», «assattamam p 888». Eso no es una
    # diferencia de glosa, es un fallo de localizacion, y no se puede adjudicar.
    if not ingles(w):
        return False
    return True


_dicc = None


def ingles(s):
    """¿Es esto ingles corriente? Se mide con el diccionario del sistema."""
    global _dicc
    if _dicc is None:
        p = pathlib.Path('/usr/share/dict/words')
        _dicc = set(w.strip().lower() for w in
                    p.read_text(encoding='utf-8', errors='ignore').splitlines()) if p.is_file() else set()
    if not _dicc:
        return True
    ws = re.findall(r"[a-z']+", sd(s))
    if not ws:
        return False
    return sum(w in _dicc for w in ws) >= max(1, len(ws) * 0.6)


CARP = {v: k for k, v in LIBRO.items()}


def main():
    if '--lista' in sys.argv:
        cual = 'glosas_ingles.json' if '--ingles' in sys.argv else 'glosas_abiertas.json'
        casos = json.loads((HERE / cual).read_text(encoding='utf-8'))
        vistos = []
        for x in casos:
            if x['file'] not in vistos:
                vistos.append(x['file'])
        refs = vistos
    else:
        refs = [a for a in sys.argv[1:] if not a.startswith('--')]
    total = 0
    for ref in refs:
        difs = revisar(ref)
        if difs:
            print(f'\n{ref}')
            for k, v, w in difs:
                print(f'    {k:22} repo={v[:44]!r:48} impreso={w[:44]!r}')
            total += len(difs)
    print(f'\n  parejas que difieren: {total}')
    return 0


if __name__ == '__main__':
    sys.exit(main())
