#! /usr/bin/env python

##############################################
# Extract each vscodeicons symbol to a       #
# separate PDF file.                         #
#                                            #
# In fact, this script creates only a Ninja  #
# build file to perform the operation.       #
#                                            #
# Author: Scott Pakin <scott-clsl@pakin.org> #
##############################################

import glob
import os
import re
import subprocess
import sys
import textwrap
from pathlib import Path


def kpsewhich(fname, must_exist=True):
    'Find a filename in the TeX tree.'
    proc = subprocess.run(['kpsewhich', fname], capture_output=True,
                          check=must_exist, encoding='utf-8')
    if proc.returncode != 0:
        return None
    return proc.stdout.strip()


def header_string():
    'Return a "generated file" header string.'
    full_name = os.path.abspath(sys.argv[0])
    gen_line = 'This is a generated file.  DO NOT EDIT.'
    edit_line = f'Edit {full_name} instead.'
    max_chars = max(len(gen_line), len(edit_line))
    hash_line = '#' * (max_chars + 4)
    return '\n'.join([
        hash_line,
        '# %-*.*s #' % (max_chars, max_chars, gen_line),
        '# %-*.*s #' % (max_chars, max_chars, edit_line),
        hash_line,
        '',
    ])


def count_pdf_pages(fname):
    'Use pdfinfo to query the number of pages in a PDF file.'
    proc = subprocess.run(['pdfinfo', fname], capture_output=True,
                          check=True, encoding='utf-8')
    for ln in proc.stdout.split('\n'):
        fields = ln.split()
        if len(fields) == 2 and fields[0] == 'Pages:':
            return int(fields[1])
    raise RuntimeError(f'failed to count pages of {fname}')


# Generate a Ninja file that extracts each page of
# vscodeicons-default-all.pdf, each page of vscodeicons-filetype-all.pdf,
# and each page of vscodeicons-foldertype-all.pdf into a separate file.
with open('vscodeicons.ninja', 'w') as w:
    # Write the header string then stop if any of the source PDF files is
    # unavailable.
    w.write(header_string())
    cat_to_fname = {
        'default': kpsewhich('vscodeicons-default-all.pdf', must_exist=False),
        'filetype': kpsewhich('vscodeicons-filetype-all.pdf', must_exist=False),
        'foldertype': kpsewhich('vscodeicons-foldertype-all.pdf', must_exist=False),
    }
    if any(cat_to_fname.values()) is None:
        w.write('''
rule touch
  command = touch $out

build fakevscodeicons.sty : touch

build VSCODEICONS : phony fakevscodeicons.sty
''')
        sys.exit(0)

    # Define a rule that converts vscodeicons.sty to fakevscodeicons.sty.
    source_sty = kpsewhich('vscodeicons.sty')
    full_name = os.path.abspath(sys.argv[0])
    w.write(rf'''
rule fake-vscodeicons-sty
  command = $
    echo '%% This is a generated file.  DO NOT EDIT.' > $out ; $
    echo '%% Edit {full_name} instead.' >> $out ; $
    echo '' >> $out ; $''')
    w.write(r'''
    sed -e 's!\\includegraphics\[page=#1, ##1\]{vscodeicons-\([a-z]\+\)-all.pdf}!\\includegraphics[##1]{vscodeicons/\1-#1}!' $in >> $out
  description = Generate $out from vscodeicons.sty

''')

    # Invoke the preceding rule.
    w.write(f'build fakevscodeicons.sty : fake-vscodeicons-sty {source_sty}\n')

    # Define a rule that copies vscodeicons-*-all.pdf locally, cleaning the
    # PDF as it does so.  At the time of this writing (1-Jan-2026), some of
    # the vscodeicons PDF files contain incorrect object counts.  qpdf can
    # fix those.
    w.write('''
rule clean-pdf
  command = qpdf $in $out ; true
  description = Fixing $out

''')

    # Invoke the preceding rule.
    for cat, fname in cat_to_fname.items():
        w.write(f'build vscodeicons/vscodeicons-{cat}-all.pdf : clean-pdf {fname}\n')

    # Define a rule that extracts a single page of vscodeicons-*-all.pdf
    # into a separate file.
    w.write('''
rule extract-pdf-page
  command = $
    pdfseparate -f $page -l $page $in "vscodeicons/${category}-%d.pdf"
  description = Extracting $category, page $page from vscodeicons
''')

    # Invoke the preceding rule one for each page of vscodeicons-default.pdf.
    all_pdfs = []
    for cat, fname in cat_to_fname.items():
        w.write('\n')
        num_pages = count_pdf_pages(fname)
        w.write('# Extract %d %s icons\n' % (num_pages, cat))
        local_fname = Path('vscodeicons') / (Path(fname).name)
        for i in range(1, num_pages + 1):
            pdf = f'vscodeicons/{cat}-{i}.pdf'
            all_pdfs.append(pdf)
            w.write(f'build {pdf} : extract-pdf-page {local_fname}\n')
            w.write(f'  category = {cat}\n')
            w.write(f'  page = {i}\n')
    w.write('\n')

    # Define a phony rule that depends on fakevscodeicons.sty and all of the
    # output files.
    w.write('build VSCODEICONS : phony $\n')
    phony = all_pdfs + ['fakevscodeicons.sty']
    lines = textwrap.wrap(' '.join(phony),
                          break_long_words=False, break_on_hyphens=False,
                          initial_indent='  ', subsequent_indent='  ')
    w.write('%s\n' % ' $\n'.join(lines))
