#!/usr/bin/env python3
import os, json, base64, hashlib, datetime, io, urllib.parse

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
BASE = os.path.dirname(SCRIPT_DIR)
SECTIONS = ["Madrasa", "Taharaat"]
OUT = os.path.join(SCRIPT_DIR, "Madrasa Notes.html")
TMPL = os.path.join(SCRIPT_DIR, "notes_template.html")

PDF_BLOBS = {}   # pdf_id -> base64 string
PDF_HASH = {}    # content-hash -> pdf_id (for dedup of duplicate PDFs)

def store_pdf(fpath):
    with open(fpath, 'rb') as f:
        data = f.read()
    h = hashlib.md5(data).hexdigest()[:12]
    if h in PDF_HASH:
        return PDF_HASH[h]
    pid = 'pdf_' + h
    PDF_HASH[h] = pid
    PDF_BLOBS[pid] = base64.b64encode(data).decode('ascii')
    return pid

def build_tree(base_path):
    result = {}
    for root, dirs, files in os.walk(base_path):
        dirs[:] = sorted([d for d in dirs if not d.startswith('.')])
        valid = sorted([f for f in files if not f.startswith('.') and
                        (f.endswith('.txt') or f.endswith('.pdf'))])
        if not valid:
            continue
        rel = os.path.relpath(root, base_path)
        parts = [] if rel == '.' else rel.split(os.sep)
        node = result
        for p in parts:
            if p not in node:
                node[p] = {}
            node = node[p]
        for fname in valid:
            fpath = os.path.join(root, fname)
            if fname.endswith('.pdf'):
                pid = store_pdf(fpath)
                node[fname] = '__PDF__|' + pid
            else:
                try:
                    with open(fpath, encoding='utf-8', errors='replace') as f:
                        content = f.read()
                except Exception as e:
                    content = "[Error: " + str(e) + "]"
                node[fname] = content
    return result

data = {}
for s in SECTIONS:
    p = os.path.join(BASE, s)
    if os.path.isdir(p):
        data[s] = build_tree(p)

def count_files(tree):
    n = 0
    for k, v in tree.items():
        if isinstance(v, str): n += 1
        elif isinstance(v, dict): n += count_files(v)
    return n

total = sum(count_files(t) for t in data.values())

ts = datetime.datetime.now().strftime("%B %d, %Y at %H:%M")

# ── PWA ICON ─────────────────────────────────────────────────────────
# Generate a 192x192 PNG icon in-memory and base64-encode it.  Used for
# iOS apple-touch-icon and Android/Chrome manifest icon.  Pure additive —
# PC browsers just get a nicer favicon, mobile gets a real home-screen icon.
def make_icon_b64(size=192):
    """Render the home-screen icon: 📚 emoji centered on dark blue."""
    try:
        from PIL import Image, ImageDraw, ImageFont
    except ImportError:
        return ''
    # Dark blue background (#0a2540) — iOS will auto-round the corners on the home screen.
    img = Image.new('RGBA', (size, size), (10, 37, 64, 255))
    draw = ImageDraw.Draw(img)

    emoji_path = '/System/Library/Fonts/Apple Color Emoji.ttc'
    rendered = False
    if os.path.exists(emoji_path):
        # Apple Color Emoji ships bitmap strikes at fixed sizes; Pillow requires one of them.
        # 160 is the largest standard strike and gives us crisp ~83 %-of-icon coverage.
        for try_size in (160, 96, 64, 48):
            try:
                font = ImageFont.truetype(emoji_path, try_size)
                text = '📚'
                # Render the emoji onto its own transparent canvas so we can scale/center it.
                glyph = Image.new('RGBA', (try_size + 20, try_size + 20), (0, 0, 0, 0))
                gd = ImageDraw.Draw(glyph)
                gd.text((10, 10), text, font=font, embedded_color=True)
                glyph = glyph.crop(glyph.getbbox())  # tight crop around the emoji
                # Scale to ~70 % of icon size
                target = int(size * 0.70)
                ratio = target / max(glyph.width, glyph.height)
                new_w = int(glyph.width * ratio)
                new_h = int(glyph.height * ratio)
                glyph = glyph.resize((new_w, new_h), Image.LANCZOS)
                img.alpha_composite(glyph, ((size - new_w) // 2, (size - new_h) // 2))
                rendered = True
                break
            except Exception as e:
                print('emoji render at size', try_size, 'failed:', e)
    if not rendered:
        print('No emoji renderer worked — drawing fallback white square')
        margin = size // 4
        draw.rectangle([margin, margin, size - margin, size - margin],
                       outline=(255, 255, 255, 255), width=3)

    buf = io.BytesIO()
    img.convert('RGB').save(buf, format='PNG', optimize=True)
    return base64.b64encode(buf.getvalue()).decode('ascii')

ICON_B64 = make_icon_b64(192)

manifest = {
    "name": "Madrasa Notes",
    "short_name": "Madrasa",
    "start_url": "./",
    "scope": "./",
    "display": "standalone",
    "orientation": "any",
    "background_color": "#f8f7f4",
    "theme_color": "#1a2332",
    "icons": [{
        "src": "data:image/png;base64," + ICON_B64,
        "sizes": "192x192",
        "type": "image/png",
        "purpose": "any"
    }] if ICON_B64 else []
}
MANIFEST_URL = ("data:application/manifest+json;charset=utf-8,"
                + urllib.parse.quote(json.dumps(manifest, separators=(',', ':'))))

dj = json.dumps(data, ensure_ascii=False).replace('</', '<\\/')

pdf_blocks = '\n'.join(
    '<script type="text/plain" id="' + pid + '">' + b64 + '</script>'
    for pid, b64 in PDF_BLOBS.items()
)

with open(TMPL, encoding='utf-8') as f:
    html = f.read()

html = html.replace('/*INJECT_DATA*/', dj)
html = html.replace('/*INJECT_TS*/', ts)
html = html.replace('<!--INJECT_PDFS-->', pdf_blocks)
html = html.replace('__PWA_ICON_B64__', ICON_B64)
html = html.replace('__PWA_MANIFEST_URL__', MANIFEST_URL)

with open(OUT, 'w', encoding='utf-8') as f:
    f.write(html)

pdf_kb = sum(len(b) for b in PDF_BLOBS.values()) // 1024
print("Done! " + str(total) + " notes | " + str(len(PDF_BLOBS)) + " unique PDFs (" + str(pdf_kb) + " KB base64) | " + ts + " -> " + OUT)
