"""
A renderer which renders each slide into its own PNG file.
"""

from pathlib import Path

from typing import Iterator

from xml.etree import ElementTree as ET
from string import Formatter
from itertools import count

from slidie.inkscape import Inkscape, open_etree_in_inkscape, set_visible_step
from slidie.svg_utils import (
    annotate_build_steps,
    find_build_elements,
    get_build_step_range,
)
from slidie.speaker_notes import extract_speaker_notes
from slidie.magic import extract_magic


def iter_output_filenames(template: Path) -> Iterator[Path]:
    """
    An iterator which generates filenames given a template.
    """
    # If no substitution string is present, we'll add one
    num_substitutions = sum(
        1
        for literal_text, field_name, format_spec, conversion in Formatter().parse(
            template.name
        )
        if field_name is not None
    )
    if num_substitutions == 0:
        template = template.with_name("{:03d}_" + template.name)

    # Generate numbered filenames
    for i in count(1):
        yield template.with_name(template.name.format(i))


def render_slide(
    svg: ET.Element,
    inkscape: Inkscape,
    filenames: Iterator[Path],
    dpi: float = 96.0,
    background_opacity: float | None = 1.0,
) -> Iterator[Path]:
    """
    Render a single slide into a series of PNGs, one per step. Writes the
    slides to the filenames generated by the 'filenames' iterator argument.

    Generates the list of PNG files written.
    """
    annotate_build_steps(svg)
    build_elements = find_build_elements(svg)

    # Neither speaker notes nor magic are processed for PNG output
    extract_speaker_notes(svg)
    extract_magic(svg)

    with open_etree_in_inkscape(inkscape, svg):
        for step_number, filename in zip(get_build_step_range(svg), filenames):
            set_visible_step(inkscape, build_elements, step_number)
            inkscape.export(filename, dpi=dpi, background_opacity=background_opacity)
            yield filename


def render_png(
    slide_filenames: list[Path],
    output: Path,
    dpi: float = 96.0,
    background_opacity: float | None = 1.0,
) -> list[Path]:
    """
    Render a slidie show into a series of PNGs. Returns the filenames of the
    generated slides.
    """
    filename_generator = iter_output_filenames(output)

    output_filenames: list[Path] = []
    with Inkscape() as inkscape:
        for slide_index, svg_filename in enumerate(slide_filenames):
            svg = ET.parse(svg_filename).getroot()
            try:
                output_filenames.extend(
                    render_slide(
                        svg, inkscape, filename_generator, dpi, background_opacity
                    )
                )
            except Exception as exc:
                exc.add_note(f"While processing {svg_filename}")
                raise

    return output_filenames
