#! /usr/bin/env python

################################################
# Convert each figchild picture to a PDF file. #
#                                              #
# In fact, this script creates only a Ninja    #
# build file to perform the conversion and a   #
# fakefigchild.sty file that uses the          #
# generated PDF files.                         #
#                                              #
# Author: Scott Pakin <scott-clsl@pakin.org>   #
################################################

import os
import re
import subprocess
import sys
import textwrap


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


# If figchild.sty doesn't exist, write a dummy figchild.ninja and exit.
sty = kpsewhich('figchild.sty')
if sty is None:
    with open('figchild.ninja', 'w') as w:
        w.write('# This is a generated file.  DO NOT EDIT.\n')
        w.write('# Edit %s instead.\n' % os.path.abspath(sys.argv[0]))
        w.write('''
rule touch
  command = touch $out

build fakefigchild.sty : touch

build FIGCHILD : phony fakefigchild.sty
''')
    sys.exit(0)

# Extract all commands defined by figchild.sty and the package description.
desc = None
commands = []
newcmd_re = re.compile(r'^\s*\\newcommand{\\(fc\w+)\}')
desc_re = re.compile(r'\\ProvidesPackage\{figchild\}\[(.*?)\]')
with open(sty) as r:
    for ln in r:
        # Extract the package description.
        if desc is None:
            match = desc_re.search(ln)
            if match is not None:
                desc = match[1]

        # Extract a command.
        match = newcmd_re.search(ln)
        if match is None:
            continue
        if match[1] != 'fcAlligatorB':   # Undocumented and broken
            commands.append(match[1])
commands.sort(key=lambda s: s.lower())

# Generate a Ninja build file for managing PDF creation.
with open('figchild.ninja', 'w') as w:
    # Write some boilerplate code.
    w.write('# This is a generated file.  DO NOT EDIT.\n')
    w.write('# Edit %s instead.\n' % os.path.abspath(sys.argv[0]))
    w.write(r'''
rule write-file
  command = /bin/echo -e '$body' > $out
  description = Creating $out

build figchild/preloaded.tex : write-file
  body = $
    \\documentclass{minimal}\n$
    \\usepackage[a2paper,landscape]{geometry}\n$
    \\usepackage{figchild}\n$
    \\usepackage{graphicx}\n$
    \\begin{document}\n$
    \\end{document}

rule generate_mylatex
  command = $
    cd figchild ; $
    pdflatex --ini '&pdflatex' mylatex.ltx preloaded.tex
  description = Creating mylatex.fmt for faster builds

''')
    w.write('build figchild/mylatex.fmt figchild/mylatex.log :'
            ' generate_mylatex figchild/preloaded.tex | %s %s\n' %
            (kpsewhich('mylatex.ltx'), kpsewhich('figchild.sty')))
    w.write(r'''
rule write_symbol_tex
  command = bash -c '$
sym=$$(basename $out .tex) ; $
echo -e "$
\\\\documentclass{minimal}\\n$
\\\\begin{document}\\n$
\\\\resizebox{!}{48pt}{\\\\$$sym[line width=3]}\\n$
\\\\end{document}" > $out'
  description = Writing $out

rule write_wide_short_symbol_tex
  command = bash -c '$
sym=$$(basename $out .tex) ; $
echo -e "$
\\\\documentclass{minimal}\\n$
\\\\begin{document}\\n$
\\\\resizebox{!}{12pt}{\\\\$$sym[line width=3]}\\n$
\\\\end{document}" > $out'
  description = Writing $out

''')

    # For each symbol defined by figchild.sty, create a LaTeX file.
    for sym in commands:
        if sym == 'fcPencil':
            # Special case for \fcPencil, which is abnormally wide and short
            w.write(f'build figchild/{sym}.tex : write_wide_short_symbol_tex\n')
        else:
            # Common case
            w.write(f'build figchild/{sym}.tex : write_symbol_tex\n')

    # Compile each LaTeX file to a PDF file.
    w.write(r'''
rule tex_to_pdf
  command = $
    texname="$$(basename $in)" ; $
    jname="$$(basename $in .tex)-uncrop" ; $
    cd figchild ; $
    pdflatex -jobname "$$jname" '&mylatex' "$$texname"
  description = pdflatex $in

''')
    for sym in commands:
        w.write(f'build figchild/{sym}-uncrop.pdf figchild/{sym}-uncrop.log figchild/{sym}-uncrop.aux : tex_to_pdf figchild/{sym}.tex | figchild/mylatex.fmt\n')
    w.write('\n')

    # Crop each PDF file.
    w.write('rule crop-pdf\n')
    w.write('  command = pdfcrop $in $out\n')
    w.write('  description = Cropping $in to produce $out\n\n')
    for sym in commands:
        w.write(f'build figchild/{sym}.pdf : crop-pdf figchild/{sym}-uncrop.pdf\n')

    # Create a fakefigchild.sty file.
    w.write(r'''
build fakefigchild.sty : write-file
  body = $
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n$
    % This is a generated file.  DO NOT EDIT. %\n$
    % Edit makefakefigchild instead.          %\n$
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n$
    \n$''')
    w.write(r'''
    \\NeedsTeXFormat{LaTeX2e}\n$
    \\ProvidesPackage{fakefigchild}[%s]\n$
    \\RequirePackage{graphicx}\n$
    \n$
''' % desc)
    for cmd in commands:
        ht = 20
        if cmd == 'fcPencil':
            # Special case for \fcPencil, which is abnormally wide and short.
            ht = 5
        w.write('    \\\\DeclareRobustCommand{\\\\%s}{%%\\n$\n' % cmd)
        w.write('    $ $ \\\\includegraphics[height=%dpt,keepaspectratio]'
                '{figchild/%s}%%\\n$\n' % (ht, cmd))
        w.write('    }\\n$\n')
    w.write('    \\n$\n')
    w.write('    \\\\endinput\n')
    w.write('\n')

    # Create a phony symbol that depends on fakefigchild.sty and all
    # generated PDF files.
    w.write('build FIGCHILD : phony $\n')
    phony = [f'figchild/{sym}.pdf' for sym in commands] + ['fakefigchild.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))
