#!/usr/bin/env python3
"""Compile every example, reject invalid element/fluid geometry, and isolate the bundle.
Run from any directory. Logs and PDFs are kept in tmp/fluid-verification.
Visual inspection of the rendered galleries remains a separate step.
"""
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import os
import shutil
import subprocess

ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / 'tmp/fluid-verification'
OUT.mkdir(parents=True, exist_ok=True)

def compile_tex(source, out=OUT, cwd=ROOT, env=None):
    p = subprocess.run(['pdflatex', '-interaction=nonstopmode', '-halt-on-error',
                        '-output-directory='+str(out), str(source)],
                       cwd=cwd, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    (out / (Path(source).stem+'.stdout')).write_bytes(p.stdout)
    return p.returncode, p.stdout.decode(errors='replace')

def example(source):
    code, _ = compile_tex(source)
    return source.name, code

with ThreadPoolExecutor(max_workers=4) as pool:
    results = list(pool.map(example, sorted((ROOT/'examples').glob('*.tex'))))
failures = [name for name, code in results if code]
print(f'Examples: {len(results)-len(failures)}/{len(results)} passed', flush=True)

invalid = [
 ('zero-size', 'fluid tank={fluid height=0}', 'greater than zero'),
 ('angle-unit', 'liquid ring={fluid angle=2cm}', 'must be unitless'),
 ('level-unit', 'fluid tank={fluid level=2pt}', 'must be unitless'),
 ('negative-size', 'fluid tank={fluid width=-1}', 'must not be negative'),
 ('fraction', 'fluid tank={fluid left level=1.2}', 'above one'),
 ('tube-width', 'u tube={fluid tube width=1cm}', 'too large'),
 ('bend-level', 'u tube={fluid left level=.1}', 'intersect the bend'),
 ('press', 'hydraulic press={fluid tube width=2cm}', 'hydraulic press geometry'),
 ('rotating-section', 'rotating tube={fluid level=.99}', 'leaves the tube'),
 ('parabola', 'rotating fluid={fluid level=.9,fluid bend=.3}', 'surface leaves'),
 ('meniscus', 'meniscus={fluid bend=.8}', 'Meniscus leaves'),
 ('capillary', 'capillary={fluid left level=.2}', 'capillary rise'),
 ('ring', 'liquid ring={fluid sweep=361}', 'ring geometry'),
 ('sphere-slice-boundary', 'sphere slice diagram={element position=.95,element axial thickness=5mm}', 'extends beyond the sphere body'),
 ('cylinder-slice-boundary', 'cylinder slice diagram={element position=.99,element axial thickness=2mm}', 'extends beyond the cylinder body'),
 ('cone-slice-boundary', 'cone slice diagram={element position=.99,element axial thickness=2mm}', 'extends beyond the base'),
]
for name, pic, message in invalid:
    source = OUT/(name+'.tex')
    source.write_text('\\documentclass{article}\n\\usepackage{tikz}\n'
                      '\\usetikzlibrary{tikzphysics.elements,tikzphysics.fluids}\n\\begin{document}\n'
                      '\\begin{tikzpicture}\\pic {'+pic+'};\\end{tikzpicture}\n\\end{document}\n')
    code, log = compile_tex(source)
    if not code or message not in log:
        failures.append(name)
        print('Unexpected invalid-geometry result:', name, flush=True)
print(f'Invalid element/fluid geometry: {len(invalid)} cases checked', flush=True)

# Absolute input path to the bundle plus a clean working directory and TEXINPUTS
# prevents sibling runtime libraries from silently satisfying missing content.
bundle = OUT/'isolated-bundle'
bundle.mkdir(exist_ok=True)
shutil.copy2(ROOT/'output/overleaf/tikzphysics.sty', bundle/'tikzphysics.sty')
for name in ['fluid-mechanics-reference-scenes','fluid-mechanics-nodes','fluid-patterns','elements-fonts','elements-solid-geometries','debug-feature-tour']:
    source = bundle/(name+'.tex')
    shutil.copy2(ROOT/'examples'/(name+'.tex'), source)
    code, log = compile_tex(source, bundle, bundle, {**os.environ, 'TEXINPUTS':str(bundle)+os.pathsep})
    if code or 'tikzlibrarytikzphysics.' in log:
        failures.append('bundle '+name)
print('Failures:', ', '.join(failures) or 'none')
raise SystemExit(bool(failures))
