"""
GLAMWalk -- Volume 3: Data at Scale
13_nls_chapbook_embedded_image_extraction.py

The real worked example for extracting illustrations that are EMBEDDED in the dataset (not linked by URL): defensive image-byte decoding that fails visibly rather than guessing, and extracting real page files plus per-illustration pixel boxes.

Source: Volume Three, Section 10.4 -- A Real Worked Example
Targets Python 3.11.4 -- see Volume One, Chapter 1.5/1.6.

NOTE: This is a teaching example. Some names (e.g. a placeholder
value or object) stand in for something you'd supply yourself when
adapting the pattern to a real script.
"""

import pyarrow.parquet as pq
from pathlib import Path

def read_chapbook_rows(parquet_path):
    table = pq.read_table(parquet_path)
    return table.to_pylist()

def extract_image_bytes(image_field):
    """
    Raw parquet storage of a HuggingFace Image column is typically a
    struct shaped {bytes: <raw JPEG>, path: <str or None>} -- but don't
    assume that blindly. Handle it defensively, and say plainly when a
    row doesn't match rather than silently producing a corrupt file.
    """
    if isinstance(image_field, (bytes, bytearray)):
        return bytes(image_field)
    if isinstance(image_field, dict) and isinstance(image_field.get("bytes"), (bytes, bytearray)):
        return bytes(image_field["bytes"])
    return None   # unrecognized shape -- don't guess, skip this row


def extract_illustrations(row, crop_output_dir):
    """One page (one row) can contain several detected illustrations —
    the objects field is a list. Save the real page image once, and
    return one record per illustration pointing at it plus its own box."""
    bboxes = (row.get("objects") or {}).get("bbox") or []
    if not bboxes:
        return []

    img_bytes = extract_image_bytes(row.get("image"))
    if not img_bytes:
        return []

    image_id = row.get("image_id", "unknown")
    out_path = Path(crop_output_dir) / f"page_{image_id}.jpg"
    out_path.parent.mkdir(parents=True, exist_ok=True)
    out_path.write_bytes(img_bytes)   # a REAL file, now on disk

    return [
        {"illustration_id": f"{image_id}_{i}", "page_file": str(out_path), "bbox_px": bbox}
        for i, bbox in enumerate(bboxes)
    ]
