#!/usr/bin/env python3
"""Render the page scans and crops that justify a specific decision, named so
each maps to the correction or design choice it supports.

Crops are located by searching the OCR line data for a phrase, then taking a
window of lines around it — so the boxes stay correct rather than being guessed
at, and each call documents what it is meant to show.

Page numbers throughout the archive are SCAN page numbers (position in the PDF).
The author's own hand-written folio numbers run three lower.
"""

import os as _os
# resolve paths relative to this archive rather than the machine it was built on
_ROOT = _os.path.dirname(_os.path.dirname(_os.path.abspath(__file__)))
ARCHIVE = _ROOT
_DATA = _os.path.join(_ROOT, 'data')
import json, os, sys
from PIL import Image

ARCHIVE = _ROOT
FAINT = f'{ARCHIVE}/evidence/faint-pages'
DEC = f'{ARCHIVE}/evidence/decisions'
for d in (FAINT, DEC):
    os.makedirs(d, exist_ok=True)

PAGES = {p['page']: p['lines'] for p in json.load(open('/tmp/lines.json'))}
missing = []


def full_page(pno, dest, name, scale=0.42):
    im = Image.open('/tmp/img/p-%03d.pgm' % pno)
    im.resize((int(im.width * scale), int(im.height * scale))).save(
        f'{dest}/{name}.png')


def at(pno, key, name, before=1, after=1, dest=None, scale=0.8, left=150,
       right=2430):
    """Crop the line containing `key`, plus context lines above and below."""
    dest = dest or DEC
    lines = sorted(PAGES[pno], key=lambda l: l['top'])
    idx = next((i for i, l in enumerate(lines) if key in l['text']), None)
    if idx is None:
        missing.append((pno, key))
        return
    lo = lines[max(0, idx - before)]['top'] - 18
    hi = lines[min(len(lines) - 1, idx + after)]['top'] + 88
    im = Image.open('/tmp/img/p-%03d.pgm' % pno)
    box = (left, max(0, lo), min(im.width, right), min(im.height, hi))
    c = im.crop(box)
    c.resize((int(c.width * scale), int(c.height * scale))).save(f'{dest}/{name}.png')


# ─────────────────────────────────────────── the pages re-transcribed by hand
for pno in (150, 151, 152, 153, 154, 155, 156):
    full_page(pno, FAINT, f'scan-p{pno}-retranscribed')
full_page(49, FAINT, 'scan-p049-faint-final-line')

# ─────────────────────────────────────────────────── how the layout was read
full_page(1, DEC, 'p001-title-page-scanned-sideways', 0.30)
at(8, '*Later, when recounting', 'p008-footnote-separator-is-short', 1, 4)
at(19, 'story extant', 'p019-rule-touches-first-note-line', 2, 3)
at(154, '* The Germans did', 'p154-footnote-block-single-spaced', 1, 6)
at(60, 'The army took various', 'p060-paragraph-indent-is-276px', 1, 1)
at(121, 'For your information', 'p121-telegram-indented-but-double-spaced', 3, 4)
at(52, 'Zeppelin flieg.', 'p052-verse-centred-single-spaced', 2, 5)
at(80, 'continued', 'p080-continued-is-a-typist-marker', 2, 1)
at(52, 'Walton 3rd rev', 'p052-running-header-sits-low', 0, 2)

# ────────────────────────────────────────────── umlauts, verified name by name
at(27, 'GdSppingen', 'p027-Goppingen-and-Schutte', 1, 1)
at(29, 'tte-Lanz ships', 'p029-Schutte-Lanz', 1, 1)
at(105, 'decker in "L 30"', 'p105-Bodecker', 1, 1)
at(134, 'nigin', 'p134-Goring-and-Konigin', 0, 1)
at(225, 'cker,', 'p225-Brocker', 1, 1)
at(60, 'hkorb', 'p060-Spahkorb', 1, 1)
at(183, 'Afrika zu unseren', 'p183-Gobel-Afrika-zu-unseren-Fussen', 1, 1)
at(168, 'Feind h', 'p168-Der-Feind-hoert-mit', 1, 1)

# ──────────────────────────────────────── garbled passages read off the page
at(144, 'spare sletiak', 'p144-spare-elevator-helmsman', 1, 2)
at(146, 'allowing for neces', 'p146-necessary-parations', 1, 1)
at(158, 'A spy does more', 'p158-than-steal-information', 1, 2)
at(228, 'boo oMancoal', 'p228-already-left-Mangelsdorf', 2, 0)
at(232, 'whak ba had lesrned', 'p232-benefit-of-what-he-had-learned', 2, 0)
at(235, 'leaving 29', 'p234-29-dead-and-Naples-curiously', 1, 1)
at(234, 'euarL', 'p234-Naples-ran-curiously', 1, 1)
at(125, 'pErErnTER', 'p124-freezing-temperatures', 1, 1)
at(191, 'a low aaeienae', 'p191-at-a-low-altitude', 1, 1)
at(49, 'pounds would', 'p049-required-a-ship-of-1765500-displacement', 2, 0)

# ─────────────────────── readings checked against the page and left unchanged
at(230, 'talk to Munich', 'p230-struck-through-word', 0, 0, scale=1.5, left=1450,
   right=2500)
at(253, 'into circulation', 'p253-author-omitted-the-word-put', 1, 0)
at(166, 'code books,', 'p166-full-stop-read-as-comma', 2, 0)
at(190, 'the sky was solid', 'p190-sky-was-solidly', 1, 0)
at(191, 'overcast.', 'p191-overcast-completes-the-sentence', 0, 1)
at(150, '43 degre', 'p150-degress-is-typed-that-way', 1, 1)
at(25, 'mililtary', 'p025-mililtary-is-typed-that-way', 1, 1)

if missing:
    print('COULD NOT LOCATE:', missing, file=sys.stderr)
print(f'faint-pages: {len(os.listdir(FAINT))}   decisions: {len(os.listdir(DEC))}')
