#
# sphinx.mk - Build HTML, man pages, and intro_zsh artifacts from Doc/source rST
#
# Not wired into Makefile.in. Run from Doc/:
#
#   make -f sphinx.mk venv           # create Doc/.venv and install deps
#   make -f sphinx.mk html           # → build/html/       (basic, no sidebar; distro)
#   make -f sphinx.mk html-web       # → build/html-web/   (-t web; Furo)
#   make -f sphinx.mk man            # → build/man/        (-t man)
#   make -f sphinx.mk texinfo        # → build/texinfo/zsh.texi
#   make -f sphinx.mk info           # → build/texinfo/zsh.info (makeinfo)
#   make -f sphinx.mk intro_zsh.html # → build/intro_zsh.html  (singlehtml)
#   make -f sphinx.mk pdf            # → build/intro_zsh.pdf (LaTeX → A4)
#   make -f sphinx.mk intro_zsh.a4.pdf intro_zsh.us.pdf
#   make -f sphinx.mk fallback-pdf   # → rst2pdf (known presentation gaps)
#   make -f sphinx.mk faq.html       # → build/faq-html/ (multipage FAQ)
#   make -f sphinx.mk faq.txt        # → build/faq.txt (concatenated text)
#   make -f sphinx.mk clean
#
# Sources are the yodl2rst.mk output under source/. Hand-written Sphinx
# entry points live in sphinx-config/ and are staged into source/ before
# each build (so yodl2rst.mk clean does not permanently delete them).
#

PYTHON ?= python3

VENV      ?= .venv
VENV_PY   := $(VENV)/bin/python
VENV_PIP  := $(VENV)/bin/pip
SPHINXBUILD := $(VENV)/bin/sphinx-build

SPHINXOPTS   ?=
SPHINXCONF   := sphinx-config
SOURCEDIR    := source
BUILDDIR     := build
REQUIREMENTS := $(SPHINXCONF)/requirements.txt
MAKEINFO     ?= makeinfo

# Staged into $(SOURCEDIR) before html/man/info builds.
SPHINX_PAGES := \
	index.rst \
	manual.rst \
	Zsh/modules.rst \
	Zsh/misc.rst \
	Zsh/zsh.rst \
	Zsh/zshall.rst

# Canonical module fragment order (matches Makefile.in MODDOCSRC).
# generate_module_docs.py trusts this list; it does not discover modules.
MOD_DOCS := \
	Zsh/mod_attr.rst Zsh/mod_cap.rst Zsh/mod_clone.rst \
	Zsh/mod_compctl.rst Zsh/mod_complete.rst Zsh/mod_complist.rst \
	Zsh/mod_computil.rst Zsh/mod_curses.rst \
	Zsh/mod_datetime.rst Zsh/mod_db_gdbm.rst Zsh/mod_deltochar.rst \
	Zsh/mod_example.rst Zsh/mod_files.rst \
	Zsh/mod_hlgroup.rst Zsh/mod_langinfo.rst \
	Zsh/mod_ksh93.rst Zsh/mod_mapfile.rst Zsh/mod_mathfunc.rst \
	Zsh/mod_nearcolor.rst Zsh/mod_newuser.rst \
	Zsh/mod_parameter.rst Zsh/mod_pcre.rst Zsh/mod_private.rst \
	Zsh/mod_regex.rst Zsh/mod_random.rst Zsh/mod_sched.rst Zsh/mod_socket.rst \
	Zsh/mod_stat.rst Zsh/mod_system.rst Zsh/mod_tcp.rst \
	Zsh/mod_termcap.rst Zsh/mod_terminfo.rst \
	Zsh/mod_watch.rst \
	Zsh/mod_zftp.rst Zsh/mod_zle.rst Zsh/mod_zleparameter.rst \
	Zsh/mod_zprof.rst Zsh/mod_zpty.rst Zsh/mod_zselect.rst \
	Zsh/mod_zutil.rst

MOD_FRAG_PATHS := $(addprefix $(SOURCEDIR)/,$(MOD_DOCS))
MODULE_GEN := $(SPHINXCONF)/generate_module_docs.py
MOD_INCLUDES := \
	$(SOURCEDIR)/modules_summary.rst.inc \
	$(SOURCEDIR)/modules_body.rst.inc

# zshmisc.1 chapter order (matches Doc/zshmisc.yo includefile list).
MISC_DOCS := \
	Zsh/grammar.rst Zsh/redirect.rst Zsh/exec.rst Zsh/func.rst \
	Zsh/jobs.rst Zsh/arith.rst Zsh/cond.rst Zsh/prompt.rst

MISC_FRAG_PATHS := $(addprefix $(SOURCEDIR)/,$(MISC_DOCS))
BUNDLE_GEN := $(SPHINXCONF)/generate_include_bundle.py
MISC_INCLUDE := $(SOURCEDIR)/misc_body.rst.inc

INTRO_ZSH_ROOT := intro_zsh/index
# Keep the main manual out of dedicated intro_zsh artifact builds.
# Quoted when passed to -D so the shell does not glob the trailing *.
INTRO_ZSH_EXCLUDES := index.rst,Zsh/*

FAQ_ROOT := faq/index
# Keep the main manual and intro_zsh out of dedicated FAQ artifact builds.
FAQ_EXCLUDES := index.rst,manual.rst,Zsh/*,intro_zsh/*
FAQ_TEXT_PARTS := index install differences howto completion multibyte future meta

.PHONY: help venv html html-web man texinfo info prepare clean clean-venv all \
	pdf fallback-pdf check-latexpdf \
	intro_zsh.html intro_zsh.pdf intro_zsh.a4.pdf intro_zsh.us.pdf \
	intro_zsh.fallback.a4.pdf intro_zsh.fallback.us.pdf \
	faq.html faq.txt

all: html

help:
	@echo "Sphinx builds from Doc/source (standalone; not part of Makefile.in)"
	@echo ""
	@echo "  make -f sphinx.mk venv              create $(VENV) and install deps"
	@echo "  make -f sphinx.mk html              distro HTML → $(BUILDDIR)/html/"
	@echo "                                      (basic theme, no sidebar/search/JS)"
	@echo "  make -f sphinx.mk html-web          website HTML → $(BUILDDIR)/html-web/"
	@echo "                                      (-t web, Furo light/dark)"
	@echo "  make -f sphinx.mk man               man pages → $(BUILDDIR)/man/  (-t man)"
	@echo "  make -f sphinx.mk texinfo           Texinfo → $(BUILDDIR)/texinfo/zsh.texi"
	@echo "  make -f sphinx.mk info              Info → $(BUILDDIR)/texinfo/zsh.info"
	@echo "  make -f sphinx.mk intro_zsh.html    → $(BUILDDIR)/intro_zsh.html"
	@echo "  make -f sphinx.mk pdf               → $(BUILDDIR)/intro_zsh.pdf (LaTeX A4)"
	@echo "  make -f sphinx.mk intro_zsh.a4.pdf  → LaTeX PDF (A4)"
	@echo "  make -f sphinx.mk intro_zsh.us.pdf  → LaTeX PDF (US Letter)"
	@echo "  make -f sphinx.mk fallback-pdf      → rst2pdf A4 (presentation gaps vs HTML)"
	@echo "  make -f sphinx.mk faq.html          → $(BUILDDIR)/faq-html/ (multipage FAQ)"
	@echo "  make -f sphinx.mk faq.txt           → $(BUILDDIR)/faq.txt (Usenet-style)"
	@echo "  make -f sphinx.mk clean             remove $(BUILDDIR)/ and staged pages"
	@echo "  make -f sphinx.mk clean-venv        also remove $(VENV)/"
	@echo ""
	@echo "Primary PDF uses Sphinx latexpdf (pdflatex/latexmk). fallback-pdf uses"
	@echo "vanilla rst2pdf when a suitable TeX install is unavailable."
	@echo ""
	@echo "Chapter/module rST under $(SOURCEDIR)/Zsh/ must already exist;"
	@echo "convert with: make -f yodl2rst.mk chapters modules"
	@echo "FAQ rST under $(SOURCEDIR)/faq/ : make -f yodl2rst.mk faq"
	@echo ""
	@echo "Uses config in $(SPHINXCONF)/conf.py and the interpreter in $(VENV)/."

# ----- virtualenv -----------------------------------------------------------

venv: $(SPHINXBUILD)

$(SPHINXBUILD): $(REQUIREMENTS)
	$(PYTHON) -m venv $(VENV)
	$(VENV_PIP) install -U pip setuptools wheel
	$(VENV_PIP) install -r $(REQUIREMENTS)
	@# Ensure the stamp matches the real binary even if pip skipped reinstall.
	@test -x $(SPHINXBUILD)
	@$(SPHINXBUILD) --version

# ----- stage hand-written pages / assemble modules & misc -------------------

prepare: $(addprefix $(SOURCEDIR)/,$(SPHINX_PAGES)) $(MOD_INCLUDES) $(MISC_INCLUDE)

$(SOURCEDIR)/index.rst: $(SPHINXCONF)/index.rst | $(SOURCEDIR)
	cp $< $@

$(SOURCEDIR)/manual.rst: $(SPHINXCONF)/manual.rst | $(SOURCEDIR)
	cp $< $@

$(SOURCEDIR)/Zsh/modules.rst: $(SPHINXCONF)/Zsh/modules.rst | $(SOURCEDIR)/Zsh
	cp $< $@

$(SOURCEDIR)/Zsh/misc.rst: $(SPHINXCONF)/Zsh/misc.rst | $(SOURCEDIR)/Zsh
	cp $< $@

$(SOURCEDIR)/Zsh/zsh.rst: $(SPHINXCONF)/Zsh/zsh.rst | $(SOURCEDIR)/Zsh
	cp $< $@

$(SOURCEDIR)/Zsh/zshall.rst: $(SPHINXCONF)/Zsh/zshall.rst | $(SOURCEDIR)/Zsh
	cp $< $@

# Fail if any fragment is missing (yodl2rst conversion is a separate, manual step).
$(MOD_INCLUDES): $(MODULE_GEN) $(MOD_FRAG_PATHS) | $(SOURCEDIR)
	@missing=; \
	for f in $(MOD_FRAG_PATHS); do \
	  if [ ! -f "$$f" ]; then missing="$$missing $$f"; fi; \
	done; \
	if [ -n "$$missing" ]; then \
	  echo "error: missing module fragment(s):$$missing"; \
	  echo "Convert with: make -f yodl2rst.mk modules"; \
	  exit 1; \
	fi
	$(PYTHON) $(MODULE_GEN) $(SOURCEDIR) $(SOURCEDIR) $(MOD_FRAG_PATHS)

$(MISC_INCLUDE): $(BUNDLE_GEN) $(MISC_FRAG_PATHS) | $(SOURCEDIR)
	@missing=; \
	for f in $(MISC_FRAG_PATHS); do \
	  if [ ! -f "$$f" ]; then missing="$$missing $$f"; fi; \
	done; \
	if [ -n "$$missing" ]; then \
	  echo "error: missing zshmisc fragment(s):$$missing"; \
	  echo "Convert with: make -f yodl2rst.mk grammar redirect exec func jobs arith cond prompt"; \
	  exit 1; \
	fi
	$(PYTHON) $(BUNDLE_GEN) $(SOURCEDIR) $(SOURCEDIR) misc_body \
		--demote-top-title $(MISC_FRAG_PATHS)

$(SOURCEDIR) $(SOURCEDIR)/Zsh:
	mkdir -p $@

# ----- builders -------------------------------------------------------------

# Distribution / terminal-browser HTML (default tag set — not ``web``).
html: venv prepare
	$(SPHINXBUILD) -b html -c $(SPHINXCONF) \
		$(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/html

# Website HTML (Furo). Same sources and zsh_roles; selected via ``-t web``.
html-web: venv prepare
	$(SPHINXBUILD) -t web -b html -c $(SPHINXCONF) \
		$(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/html-web

# -t man enables zrst ``.. only:: man`` sections (man-only headings, etc.).
# zshall.1 is overview plus troff .so includes (Sphinx drops raw:: man).
ZSHALL_SO_PAGES := \
	zshroadmap zshmisc zshexpn zshparam zshoptions zshbuiltins zshzle \
	zshcompwid zshcompsys zshcompctl zshmodules zshcalsys zshtcpsys \
	zshzftpsys zshcontrib

man: venv prepare
	$(SPHINXBUILD) -t man -b man -c $(SPHINXCONF) \
		$(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/man
	$(PYTHON) $(SPHINXCONF)/insert_zshall_so.py \
		$(BUILDDIR)/man/zshall.1 $(ZSHALL_SO_PAGES)

# Full manual Texinfo (no -t man so .. only:: man is dropped).
texinfo: venv prepare
	$(SPHINXBUILD) -b texinfo -c $(SPHINXCONF) \
		$(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/texinfo

# makeinfo → zsh.info (requires GNU texinfo / makeinfo on PATH).
# Pass --no-split so Sphinx's generated Makefile does not lose it when we
# override MAKEINFO with a custom binary path.
info: texinfo
	@command -v $(MAKEINFO) >/dev/null 2>&1 || { \
	  echo "error: $(MAKEINFO) not found; install GNU texinfo"; \
	  exit 1; \
	}
	$(MAKE) -C $(BUILDDIR)/texinfo info MAKEINFO='$(MAKEINFO) --no-split'

# ----- An Introduction to the Z Shell (intro_zsh) ---------------------------

intro_zsh.html: $(BUILDDIR)/intro_zsh.html
pdf: intro_zsh.pdf
intro_zsh.pdf: $(BUILDDIR)/intro_zsh.pdf
intro_zsh.a4.pdf: $(BUILDDIR)/intro_zsh.a4.pdf
intro_zsh.us.pdf: $(BUILDDIR)/intro_zsh.us.pdf
fallback-pdf: intro_zsh.fallback.a4.pdf
intro_zsh.fallback.a4.pdf: $(BUILDDIR)/intro_zsh.fallback.a4.pdf
intro_zsh.fallback.us.pdf: $(BUILDDIR)/intro_zsh.fallback.us.pdf

# Shared -D flags for intro_zsh-only builds (quote excludes for the shell).
INTRO_ZSH_SPHINX_D = \
	-D root_doc=$(INTRO_ZSH_ROOT) \
	-D exclude_patterns='$(INTRO_ZSH_EXCLUDES)'

# Minimal style probe for Sphinx latexpdf (thin distro TeX often fails here).
INTRO_ZSH_LATEX_STYLES := cmap.sty tabulary.sty framed.sty fncychap.sty \
	titlesec.sty wrapfig.sty upquote.sty capt-of.sty needspace.sty makecell.sty

# Single-file HTML for convenient review / distribution.
$(BUILDDIR)/intro_zsh.html: venv prepare
	$(SPHINXBUILD) -b singlehtml -c $(SPHINXCONF) \
		$(INTRO_ZSH_SPHINX_D) \
		$(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/intro_zsh-html
	cp -f $(BUILDDIR)/intro_zsh-html/intro_zsh/index.html $@

# Abort early with a pointer to fallback-pdf when TeX is missing or too thin.
check-latexpdf:
	@if ! command -v pdflatex >/dev/null 2>&1; then \
	  echo "LaTeX PDF toolchain not suitable (pdflatex not found)."; \
	  echo "Will use rst2pdf via: make -f sphinx.mk fallback-pdf"; \
	  echo "(rst2pdf has known presentation bugs versus HTML.)"; \
	  exit 1; \
	fi
	@missing=; \
	for sty in $(INTRO_ZSH_LATEX_STYLES); do \
	  if ! kpsewhich "$$sty" >/dev/null 2>&1; then \
	    missing="$$missing $$sty"; \
	  fi; \
	done; \
	if [ -n "$$missing" ]; then \
	  echo "LaTeX PDF toolchain not suitable (missing TeX style(s):$$missing)."; \
	  echo "Will use rst2pdf via: make -f sphinx.mk fallback-pdf"; \
	  echo "(rst2pdf has known presentation bugs versus HTML.)"; \
	  exit 1; \
	fi

# Run Sphinx's latex Makefile (latexmk) or a multi-pass pdflatex fallback.
# $(1) = latex output directory
define run-latexpdf
	if command -v latexmk >/dev/null 2>&1; then \
	  $(MAKE) -C $(1); \
	else \
	  echo "latexmk not found; using multi-pass pdflatex"; \
	  cd $(1) && \
	    pdflatex -interaction=nonstopmode intro_zsh.tex && \
	    (makeindex -s python.ist intro_zsh.idx || true) && \
	    pdflatex -interaction=nonstopmode intro_zsh.tex && \
	    pdflatex -interaction=nonstopmode intro_zsh.tex; \
	fi
endef

# Primary PDF via Sphinx latex (requires a suitable TeX install).
# Use -b latex (not -M) so -c sphinx-config is honored.
$(BUILDDIR)/intro_zsh.a4.pdf: venv prepare check-latexpdf
	$(SPHINXBUILD) -b latex -c $(SPHINXCONF) \
		-t intro_zsh_a4 \
		$(INTRO_ZSH_SPHINX_D) \
		$(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/intro_zsh-latex-a4
	$(call run-latexpdf,$(BUILDDIR)/intro_zsh-latex-a4)
	cp -f $(BUILDDIR)/intro_zsh-latex-a4/intro_zsh.pdf $@

$(BUILDDIR)/intro_zsh.us.pdf: venv prepare check-latexpdf
	$(SPHINXBUILD) -b latex -c $(SPHINXCONF) \
		-t intro_zsh_us \
		$(INTRO_ZSH_SPHINX_D) \
		$(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/intro_zsh-latex-us
	$(call run-latexpdf,$(BUILDDIR)/intro_zsh-latex-us)
	cp -f $(BUILDDIR)/intro_zsh-latex-us/intro_zsh.pdf $@

# Default PDF name → A4 (LaTeX).
$(BUILDDIR)/intro_zsh.pdf: $(BUILDDIR)/intro_zsh.a4.pdf
	ln -sfn intro_zsh.a4.pdf $@

# Fallback PDF via vanilla rst2pdf (presentation gaps vs HTML/LaTeX).
$(BUILDDIR)/intro_zsh.fallback.a4.pdf: venv prepare
	$(SPHINXBUILD) -t intro_zsh_a4 -b pdf -c $(SPHINXCONF) \
		$(INTRO_ZSH_SPHINX_D) \
		$(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/intro_zsh-fallback-a4
	cp -f $(BUILDDIR)/intro_zsh-fallback-a4/intro_zsh.pdf $@

$(BUILDDIR)/intro_zsh.fallback.us.pdf: venv prepare
	$(SPHINXBUILD) -t intro_zsh_us -b pdf -c $(SPHINXCONF) \
		$(INTRO_ZSH_SPHINX_D) \
		$(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/intro_zsh-fallback-us
	cp -f $(BUILDDIR)/intro_zsh-fallback-us/intro_zsh.pdf $@

# ----- Z-Shell FAQ (Etc/FAQ.yo → source/faq/) --------------------------------

faq.html: $(BUILDDIR)/faq-html/faq/index.html
faq.txt: $(BUILDDIR)/faq.txt

FAQ_SPHINX_D = \
	-D root_doc=$(FAQ_ROOT) \
	-D exclude_patterns='$(FAQ_EXCLUDES)'

# Multipage HTML isolated from the main manual / intro_zsh trees.
# root_doc=faq/index → pages live under build/faq-html/faq/.
$(BUILDDIR)/faq-html/faq/index.html: venv
	@if [ ! -f $(SOURCEDIR)/faq/index.rst ]; then \
	  echo "error: missing $(SOURCEDIR)/faq/index.rst"; \
	  echo "Convert with: make -f yodl2rst.mk faq"; \
	  exit 1; \
	fi
	$(SPHINXBUILD) -b html -c $(SPHINXCONF) \
		$(FAQ_SPHINX_D) \
		$(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/faq-html

# Per-page text builder, then concatenate in toctree order (no singletext).
$(BUILDDIR)/faq.txt: venv
	@if [ ! -f $(SOURCEDIR)/faq/index.rst ]; then \
	  echo "error: missing $(SOURCEDIR)/faq/index.rst"; \
	  echo "Convert with: make -f yodl2rst.mk faq"; \
	  exit 1; \
	fi
	$(SPHINXBUILD) -b text -c $(SPHINXCONF) \
		$(FAQ_SPHINX_D) \
		$(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/faq-text
	@rm -f $@
	@for part in $(FAQ_TEXT_PARTS); do \
	  f=$(BUILDDIR)/faq-text/faq/$$part.txt; \
	  if [ ! -f "$$f" ]; then \
	    echo "error: missing $$f after text build"; \
	    exit 1; \
	  fi; \
	  cat "$$f" >> $@; \
	  printf '\n' >> $@; \
	done

# ----- clean ----------------------------------------------------------------

clean:
	rm -rf $(BUILDDIR)
	rm -f $(addprefix $(SOURCEDIR)/,$(SPHINX_PAGES))
	rm -f $(MOD_INCLUDES) $(MISC_INCLUDE)

clean-venv: clean
	rm -rf $(VENV)
