# -*- coding: utf-8 -*-
"""Перебрендування: bibletime → «Біблійна Година».

Крок конвеєра після набору тексту й головоломок. Робить те саме на всіх 180
буклетах, тому живе в одному місці, а не у файлах перекладу:

1. напис `bible` + `time` у шапці → «Біблійна Година»;
2. `www.besweb.com` → `www.bibletime.org.ua`;
3. логотип `bes` → наша емблема (біла на кольоровій плашці, у колір рівня —
   на білому тлі);
4. підвал останньої сторінки — свій копірайт, сайт і телефон;
5. рядок `Registered Charity UK CIO …` і дату рівня прибрати.

Два правила, без яких виходить брудно:

* **спершу всі закриття, потім усе малювання.** Якщо намалювати новий напис,
  а вже потім закривати роздільник поруч, закриття зріже свіжий текст;
* **стару графіку закриваємо, а не видаляємо** (§2.1), і закриваємо кольором
  тла. На обкладинці логотип лежить на кольоровій плашці — білий прямокутник
  лишив би там діру.
"""
import os
import re

import fitz

HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)

NAME_1, NAME_2 = 'Біблійна', ' Година'
SITE = 'www.bibletime.org.ua'
PHONE = '+380964189292'
COPYRIGHT = f'©  Біблійна Година. Відвідай {SITE} або телефонуй {PHONE}.'

ARIAL = '/System/Library/Fonts/Supplemental/Arial.ttf'
ARIAL_B = '/System/Library/Fonts/Supplemental/Arial Bold.ttf'
_FR = fitz.Font(fontfile=ARIAL)
_FB = fitz.Font(fontfile=ARIAL_B)

DROP = re.compile(r'Registered Charity|^\s*\d{2}/\d{4}\s*$')
BES = re.compile(r'Bible Educational Services|6632 2462|besweb\.com')


def _spans(page):
    for b in page.get_text('dict')['blocks']:
        if b['type']:
            continue
        for l in b['lines']:
            for s in l['spans']:
                yield s


def _norm(t):
    """Витяг тексту віддає пробіли як нерозривні (\\xa0) — без нормалізації
    жоден пошук за фразою не спрацьовує."""
    return t.replace('\xa0', ' ').replace('\u2009', ' ').strip()


def _key(span):
    """Стабільний ідентифікатор спана: `get_text` щоразу віддає нові обʼєкти,
    тож `id()` для порівняння не годиться."""
    return (round(span['bbox'][0], 1), round(span['bbox'][1], 1), _norm(span['text']))


def _rgb(colour):
    return ((colour >> 16 & 255) / 255, (colour >> 8 & 255) / 255, (colour & 255) / 255)


def _bg(page, x, y):
    """Колір тла в точці — щоб закривати старе тим самим кольором."""
    pm = page.get_pixmap(clip=fitz.Rect(x, y, x + 1.5, y + 1.5))
    return tuple(c / 255 for c in pm.samples[:3])


class Job:
    """Накопичує, що закрити й що намалювати, щоб зробити це у правильному порядку."""

    def __init__(self, page):
        self.page = page
        self.covers = []      # (rect, fill)
        self.draws = []       # callable(page)

    def cover(self, rect, fill=None, pad=0.8):
        r = fitz.Rect(rect) + (-pad, -pad, pad, pad)
        if fill is None:
            fill = _bg(self.page, r.x0 - 2.5, r.y0 + r.height / 2)
        self.covers.append((r, fill))

    def draw(self, fn):
        self.draws.append(fn)

    def run(self):
        if not (self.covers or self.draws):
            return
        for r, _ in self.covers:
            self.page.add_redact_annot(r)
        if self.covers:
            self.page.apply_redactions(images=fitz.PDF_REDACT_IMAGE_NONE,
                                       graphics=fitz.PDF_REDACT_LINE_ART_NONE,
                                       text=fitz.PDF_REDACT_TEXT_REMOVE)
        for r, fill in self.covers:
            self.page.draw_rect(r, color=None, fill=fill)
        self.page.insert_font(fontname='brR', fontfile=ARIAL)
        self.page.insert_font(fontname='brB', fontfile=ARIAL_B)
        for fn in self.draws:
            fn(self.page)


def _header(job, page):
    """Напис у шапці: `bible`+`time` → «Біблійна Година».

    Нічого іншого не рухаємо. Спроба зсувати роздільник і номер серії, щоб
    дати напису повний розмір, зіпсувала обкладинку: підзаголовок опинявся
    під прямокутником закриття, а «A1» їхало за край. Напис просто
    вписується у вільне місце — на обкладинці це майже повний кегль, на
    внутрішніх сторінках менший.
    """
    a = b = None
    for s_ in _spans(page):
        if _norm(s_['text']) == 'bible' and s_['size'] > 20:
            a = s_
        elif _norm(s_['text']) == 'time' and s_['size'] > 20 and a is not None:
            b = s_
            break
    if not (a and b):
        return False

    size, x0, base = a['size'], a['bbox'][0], a['origin'][1]
    old = fitz.Rect(a['bbox']) | fitz.Rect(b['bbox'])

    # найближчий елемент праворуч на тій самій висоті
    limit = page.rect.width
    for dr in page.get_drawings():
        r = fitz.Rect(dr['rect'])
        if r.height > 15 and r.x0 > old.x1 and r.y0 < base < r.y1 + size:
            limit = min(limit, r.x0)
    for s_ in _spans(page):
        if s_ is a or s_ is b:
            continue
        if s_['bbox'][0] > old.x1 and s_['bbox'][3] > old.y0 and s_['bbox'][1] < old.y1:
            limit = min(limit, s_['bbox'][0])

    want = _FR.text_length(NAME_1 + NAME_2, fontsize=size)
    room = limit - x0 - 8
    if want > room:
        size = size * room / want
    w1 = _FR.text_length(NAME_1, fontsize=size)
    c1, c2 = _rgb(a['color']), _rgb(b['color'])

    # знизу ріжемо по базовій лінії: під написом стоїть підзаголовок
    # «Рівень N — біблійні історії», і ширше закриття його зʼїдає
    job.cover((old.x0, old.y0, old.x1, base + size * 0.08), pad=0.8)

    def _draw_name(pg):
        pg.insert_text((x0, base), NAME_1, fontname='brR', fontsize=size, color=c1)
        pg.insert_text((x0 + w1, base), NAME_2, fontname='brR', fontsize=size, color=c2)
    job.draw(_draw_name)
    return True


def _site_and_footer(job, page):
    """URL на кожній сторінці; на останній — свій копірайт замість чужого.

    Рядок копірайту складається з кількох спанів, і серед них є
    `www.besweb.com`. Якщо обробляти спани поодинці, URL перемалюється двічі:
    один раз у складі нового рядка, другий — на своєму старому місці. Тому
    рядок із © обробляємо **цілим**, а його спани пропускаємо.
    """
    lines = []
    for b in page.get_text('dict')['blocks']:
        if b['type']:
            continue
        for l in b['lines']:
            lines.append(l)

    done = set()
    for l in lines:
        joined = _norm(''.join(sp['text'] for sp in l['spans']))
        if joined.startswith('©'):
            first = l['spans'][0]
            job.cover((first['bbox'][0], l['bbox'][1],
                       page.rect.width - 20, l['bbox'][3]))
            job.draw(lambda pg, s=first: pg.insert_text(
                (s['bbox'][0], s['origin'][1]), COPYRIGHT, fontname='brR',
                fontsize=s['size'], color=_rgb(s['color'])))
            done.update(_key(sp) for sp in l['spans'])

    for s in _spans(page):
        if _key(s) in done:
            continue
        t = _norm(s['text'])
        if DROP.search(t) or 'educational services' in t.lower():
            job.cover(s['bbox'])                       # просто прибрати
        elif t == 'www.besweb.com':
            job.cover(s['bbox'])
            job.draw(lambda pg, s=s: pg.insert_text(
                (s['bbox'][0], s['origin'][1]), SITE, fontname='brB',
                fontsize=s['size'] * 0.72, color=_rgb(s['color'])))
        elif BES.search(t):
            job.cover(s['bbox'])


def _logo(job, page, dark, white):
    # Тільки дрібні фігури: смуга кольорової плашки теж потрапляє в кут, але
    # вона широка й заходить за край сторінки — з нею габарит логотипа
    # роздувався вчетверо, і емблема вилазила за плашку.
    cands = [fitz.Rect(dr['rect']) for dr in page.get_drawings()
             if fitz.Rect(dr['rect']).x0 > 380 and fitz.Rect(dr['rect']).y0 > 18
             and fitz.Rect(dr['rect']).y1 < 110
             and 0.4 < fitz.Rect(dr['rect']).width < 60
             and 0.4 < fitz.Rect(dr['rect']).height < 60]
    if not cands:
        return False
    box = cands[0]
    for r in cands[1:]:
        box |= r
    if box.width < 20 or box.height < 15:
        return False
    fill = _bg(page, box.x0 - 5, box.y0 + box.height / 2)
    light = sum(fill) / 3 > 0.85
    job.cover(box, fill=fill, pad=1.2)
    side = min(box.height, box.width) * 1.15
    cx, cy = (box.x0 + box.x1) / 2, (box.y0 + box.y1) / 2
    place = fitz.Rect(cx - side / 2, cy - side / 2, cx + side / 2, cy + side / 2)
    job.draw(lambda pg: pg.insert_image(place, filename=(dark if light else white),
                                        keep_proportion=True))
    return True


def rebrand(doc, lesson):
    lv = lesson.level
    dark = os.path.join(ROOT, 'data', 'logo', f'logo_L{lv.n}.png')
    white = os.path.join(ROOT, 'data', 'logo', 'logo_white.png')
    stats = {'напис': 0, 'логотип': 0}
    for page in doc:
        job = Job(page)
        if _header(job, page):
            stats['напис'] += 1
        _site_and_footer(job, page)
        if _logo(job, page, dark, white):
            stats['логотип'] += 1
        job.run()
    return stats
