#!/usr/bin/env python3
"""Recorre el OCR de un libro entero y marca lo que el repo no tiene.

Distinto del alineador general: aquel produce candidatos palabra a palabra y se
le escapan los tramos enteros que faltan, porque un hueco no genera un par
`repo -> scan`. Aqui se hace al reves — se recorre el texto IMPRESO con una
ventana deslizante y se pregunta, por cada tramo, si aparece en el repo.

Como se separa el grano de la paja:

  - PAGINAS DE FUERA. La cubierta, el pliego de fotos, el indice y la lista de
    libros del autor no estan en el repo y saldrian enteros como huecos. Se
    detectan por su cobertura: si menos de un tercio de la pagina aparece en el
    repo, es que esa pagina no pertenece al texto.
  - TRAMOS CORTOS. Un salto de una o dos palabras casi siempre es el OCR
    partiendo una palabra o el titulillo de la pagina.
  - EL VERSO. Las lineas en sanscrito viven en otro sitio del fichero y el OCR
    las lee en otro orden; se toleran si la mayoria de sus palabras estan.

Lo que salga hay que confirmarlo mirando la imagen. Esto solo dice donde mirar.

Uso: python3 barrer_libro.py <libro> [--ventana 8] [--min 4]
"""
import json, re, sys, unicodedata, pathlib

HERE = pathlib.Path(__file__).resolve().parent
REPO = pathlib.Path('/Users/jaganat/git_projects/vedabase-original')
SURYA = HERE / 'surya'


def sd(s):
    s = unicodedata.normalize('NFD', s or '')
    s = ''.join(c for c in s if unicodedata.category(c) != 'Mn')
    return re.sub(r'[^A-Za-z0-9]+', ' ', s.lower()).split()


def recompon(w, vocab):
    """Junta las palabras que el OCR partio al final de la linea.

    El impreso corta con guion ('devo-\ntees', 'im-\nprisoned') y el OCR
    entrega los dos trozos sueltos. Sin juntarlos, ningun n-grama que los
    contenga casa, y salen como hueco 19 tramos que no lo son. Solo se juntan
    si el resultado es una palabra que el repo conoce: asi no se inventan
    uniones.
    """
    out = []; i = 0
    while i < len(w):
        if i + 1 < len(w) and (w[i] + w[i + 1]) in vocab and w[i] not in vocab:
            out.append(w[i] + w[i + 1]); i += 2
        else:
            out.append(w[i]); i += 1
    return out


def main():
    if len(sys.argv) < 2:
        print(__doc__); return 1
    libro = sys.argv[1]
    V = int(sys.argv[sys.argv.index('--ventana') + 1]) if '--ventana' in sys.argv else 8
    MIN = int(sys.argv[sys.argv.index('--min') + 1]) if '--min' in sys.argv else 4

    paginas = sorted(p for d in (SURYA / libro).glob('*') for p in d.glob('p*.surya.txt'))
    if not paginas:
        print('sin OCR para', libro); return 1
    carpeta = REPO / libro
    repo = sd('\n'.join(f.read_text(encoding='utf-8') for f in sorted(carpeta.rglob('*.md'))))
    repo = [x for x in repo if not x.isdigit()]
    vocab = set(repo)
    ngramas = {tuple(repo[i:i + V]) for i in range(len(repo) - V + 1)}
    print(f'{libro}: {len(paginas)} paginas de OCR | {len(repo)} palabras en el repo')

    fuera = 0; huecos = []
    for f in paginas:
        pag = int(re.search(r'p(\d+)', f.name).group(1))
        w = recompon(sd(f.read_text(encoding='utf-8')), vocab)
        # el TITULILLO de la pagina (titulo del capitulo + folio) se cuela
        # pegado al texto y rompe el n-grama; se quitan los numeros sueltos
        w = [x for x in w if not x.isdigit()]
        if len(w) < V + 2: continue
        marcas = [tuple(w[i:i + V]) in ngramas for i in range(len(w) - V + 1)]
        cob = sum(marcas) / len(marcas)
        if cob < 0.33:
            fuera += 1; continue          # cubierta, fotos, indice, lista de libros
        # tramos consecutivos sin casar
        i = 0
        while i < len(marcas):
            if marcas[i]: i += 1; continue
            j = i
            while j < len(marcas) and not marcas[j]: j += 1
            # el tramo cubre desde la palabra i hasta j+V-1
            trozo = w[i:j + V - 1]
            if len(trozo) >= MIN + V - 1:
                dentro = sum(1 for x in trozo if x in set(repo))
                if dentro / len(trozo) < 0.9:      # el verso reordenado se salva
                    huecos.append({'pagina': pag, 'palabras': len(trozo),
                                   'texto': ' '.join(trozo)[:200],
                                   'cobertura_pagina': round(cob, 2)})
            i = j + 1

    print(f'paginas fuera del texto (cubierta, fotos, indice): {fuera}')
    print(f'tramos que el repo no tiene: {len(huecos)}\n')
    for h in sorted(huecos, key=lambda x: -x['palabras'])[:40]:
        print(f"  p{h['pagina']:<4} {h['palabras']:3} pal  {h['texto'][:120]}")
    json.dump(huecos, open(HERE / f'huecos_{libro}.json', 'w'), ensure_ascii=False, indent=1)
    print(f'\n-> huecos_{libro}.json')
    return 0


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