%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Module:    ZzTeX Indexing Facilities, version 2
%
% Synopsis:  This file contains the facilities for generating index
%            parameter files, raw entry files, and for setting the indexes.
%
% Author:    Paul C. Anagnostopoulos
% Created:   13 June 2013
%
% Copyright 1989--2020 by Paul C. Anagnostopoulos
% under The MIT License (opensource.org/licenses/MIT)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%                       Define Index Locators
%                       ------ ----- --------


% This list contains the definition of each locator. New ones must be
% added to the end, and any program looking at the definitions must assume
% that duplicates may exist and that the last one prevails.

\def \zixloclist {}


\def \definelocator #1#2#3{%            {type}{attributes}{[format-args]}
  {\setflag \pg  = \false
   \setflag \rng = \false
   \setflag \txt = \false
   \def \page    {\setflag \pg = \true}%
   \def \nopage  {\setflag \pg = \false}%
   \def \range   {\setflag \pg = \true \setflag \rng = \true}%
   \def \norange {\setflag \rng = \false}%
   \def \text    {\setflag \txt = \true}%
   \def \notext  {\setflag \txt = \false}%
   #2%
   {\def \attr {#2}%
    \edef \znext {%
      \noexpand\append{{#1}{\expandafter\zdefof \meaning\attr\zmark}}}%
    \znext{\zixloclist}}%
   \if \andp{\pg}{\notp{\txt}}%
     \withname\gdef{\x#1}##1{\zixindex{#1}{S}{##1}{}}%
     \if \rng
       \withname\gdef{\x#1begin}##1{\zixindex{#1}{B}{##1}{}}%
       \withname\gdef{\x#1end}##1{\zixindex{#1}{E}{##1}{}}%
     \fi
   \else\if \txt
     \withname\gdef{\x#1}##1##2{\zixindex{#1}{S}{##1}{##2}}%
     \if \rng
       \withname\gdef{\x#1begin}##1##2{\zixindex{#1}{B}{##1}{##2}}%
       \withname\gdef{\x#1end}##1##2{\zixindex{#1}{E}{##1}{##2}}%
     \fi
   \else
    \error{locattr}{Invalid index locator attributes}%
   \fi\fi
   \if \notp{\emptyargp{#3}}%
    \definelocatorformat{#1}#3\relax
  \fi}}

%                       Define Locator Formats
%                       ------ ------- -------


% This list contains the definition of each locator format. It works like
% the locator list above.

\def \zixformatlist {}

\def \definelocatorformat #1#2#3#4{%  {name}{rank.order}{level-1-items}{level-2-items}
  {\def \lvli {#3}%
   \def \lvlii {#4}%
   \edef \znext {%
     \noexpand\append{{#1}{#2}%
                      {\expandafter\zdefof \meaning\lvli\zmark}%
                      {\expandafter\zdefof \meaning\lvlii\zmark}}}%
   \znext{\zixformatlist}}}

%                       Builtin Locator Types
%                       ------- ------- -----


% These locators are built-in for ease of use. Ranks 0, 1, and 5 and higher
% are reserved for the user. Ranks 10 and higher result in deferred
% locators.

\definelocator{page}{\page\range}
              {{2.1}{prefix1:, |tmplt:?P|rtmplt:?P--?Q|sep:, }{}}

\definelocator{figure}{\page\range}
              {{2.3}{prefix1:, |tmplt:?P\emph{f}|rtmplt:?P--?Q\emph{f}|sep:, }{}}

\definelocator{note}{\page}
              {{2.5}{prefix1:, |tmplt:?P\emph{n}|sep:, }{}}

\definelocator{table}{\page\range}
              {{2.7}{prefix1:, |tmplt:?P\emph{t}|rtmplt:?P--?Q\emph{t}|sep:, }{}}

\definelocator{see}{\nopage\text}
              {{3}{prefix1:. \emph{See} |tmplt:?T|sep:; }{}}

\definelocator{seealso}{\nopage\text}
              {{4}{prefix1:. \emph{See also} |tmplt:?T|sep:; }{}}

%                       Generate Indexes
%                       -------- -------


\def \zixnamelist {}
\setflag \zindexing = \false


\def \generateindex #1{%                                        {name}
  \append{#1}{\zixnamelist}%
  \global\setflag \zindexing = \true}

%                       Index File Handling
%                       ----- ---- --------

%\definewrite{\zixrootfile}
%\definewrite{\zixdivfile}
\setflag \zixdivopen = \false


\def \zindexinit {%
  \if \zindexing
    \maplist{\zixwritezzp{##1}}{\zixnamelist}%
    \immediate\openout \zixrootfile = \jobname.zzi\relax
    \immediate\write \zixrootfile 
      {\zcomment Raw index entry file for book `\jobname' root.}%
  \fi}

\def \zindexfinal {%
  \if \zindexing
    \immediate\write \zixrootfile {\zcomment [end]}%
    \immediate\closeout \zixrootfile
  \fi}

\def \zindexdivinit #1{%                                {division}
  \if \zindexing
    \immediate\openout \zixdivfile = #1.zzi\relax
    \immediate\write \zixdivfile
      {\zcomment Raw index entry file for book `\jobname', division `#1'.}%
    \global\setflag \zixdivopen = \true
  \fi}

\def \zindexdivfinal {%
  \if \zixdivopen
    \immediate\write \zixdivfile {\zcomment [end]}%
    \immediate\closeout \zixdivfile
    \global\setflag \zixdivopen = \false
  \fi}

\def \zixwritezzp #1{%                                  {name}
  \begingroup
    \processdesign{\index}{#1}%
    \immediate\openout \zwritea = #1.zzp\relax
    \immediate\write \zwritea {root \jobname;}%
    \immediate\write \zwritea {index #1;}%
    \immediate\write \zwritea {divisions \the\divisions;}%
    \maplist{\immediate\write \zwritea {define-locator ##1;}}{\zixloclist}%
    \maplist{\immediate\write \zwritea {define-format ##1;}}{\zixformatlist}%
    \immediate\write \zwritea {locators \the\locators;}%
    \immediate\write \zwritea {\the\indexparams}%
    \immediate\closeout \zwritea
  \endgroup}

%                       Write Raw Index Entry
%                       ----- --- ----- -----


% This macro is called from the indexing macros defined by
% \definelocator.

\def \zixindex #1#2#3#4{%               {locator}{page-code}{headings}{[text]}
  \zixindexb {#1}{#2}{#4}#3|||\zmark}

\catcode"B8 = \catother

\def \zixindexb #1#2#3#4|#5|#6|#7\zmark{%  {locator}{page-code}{text}
                                        %  head1|head2|head3|...\zmark
  \if \zindexing
    \zbeginhidewrite
      \def \zhdx {#4}%
      \def \zhdy {#5}%  
      \def \zhdz {#6}%  
      \def \ztxt {#3}%  
      \edef \znext {#1^^b8%
                    #2^^b8%
                    \noexpand\folio^^b8%
                    \expandafter\zdefof \meaning\zhdx\zmark^^b8%
                    \expandafter\zdefof \meaning\zhdy\zmark^^b8%
                    \expandafter\zdefof \meaning\zhdz\zmark^^b8%
                    \expandafter\zdefof \meaning\ztxt\zmark}%
      \write \if \zixdivopen \zixdivfile \else \zixrootfile \fi
           \expandafter{\znext}%
      \if \vmodep \repeatpenalty \fi      % In case glue follows write.
    \zendhidewrite
  \fi}

\catcode"B8 = \catinvalid

%                       Discussion of Index Formatting
%                       ---------- -- ----- ----------


% An index is formatted inside an index block, which is more or less
% like a text block. Within the index block are occurences of the
% \indexentry macro, one for each entry. In addition, the
% \alphabreak macro can be used to produce breaks preceding the 'A'
% entries, 'B' entries, etc.
%
% In an index block, parts 4 and 5 of the mark are used to remember
% index headings so they can be carried forward to subsequent columns
% or pages. The mark is used as follows:
%
%   Part  Subpart  Usage
%     4      1     Boolean, true if in the midst of a first-level entry.
%     4      2     The first-level entry heading.
%     5      1     Boolean, true if in the midst of a second-level entry.
%     5      2     The second-level entry heading.

%                       Index Block
%                       ----- -----


\defineblock{\index}{\endindex}{\false}{redefine}

%~block index Type
% \def \alphaformat ##1{...}            % Alphabetic break formatter.
% \bodyfont = {\size\style}             % Body font.
% \clubpenalty = penalty                % Club penalty is often different.
% \columngutter = glue                  % Column gutter.
% \divisions = {...}                    % Which divisions to include.
% \leftindent = glue                    % Left indentation.
% \locators = {...}                     % Which locators to include.
% \indexparams = {...}                  % Parameters for index generator.
% \parhang = glue                       % Paragraph hang.
% \parindent = dimen                    % Paragraph indent.
% \parrag = dimen                       % Paragraph raggedness.
% \parskip = glue                       % Paragraph skip. 
% \position = {...}                     % Position and number of columns.
% \rightindent = glue                   % Right indentation.
%~end

%\definetoks{\locators}
%\definetoks{\indexparams}

%\definecount{\previndexlevel}{0}
%\definecount{\zixcols}{0}


\def \index #1{%                                                {type}
  \blockcantbein{\index}{\index}%
  \beginblockscope{index}%
  \global\increment \indexdepth
  \clubpenalty = \breakallowed          %~default soft
  \parskip = 0pt                        %~default soft
  \processdesign{\index}{#1}%
  \global\increment \indexnumber
  \def \zixname {#1}%
  \zixparsepos
  \indexformat}

\def \indexformat {%
  \the\bodyfont
  \alterindentation{\leftindent}{\rightindent}%
  \setparrag{\parrag}%
  \setpagecolumns{\zixpageuse}{\the\zixcols}{\columngutter}%
  \everycolumn = {\zixcarryover}%
  \begingroup
    \let \zfullcolumnbreak = \fullcolumnbreak
    \let \fullcolumnbreak = \zixcolumnbreak
    \let \shortcolumnbreak = \zixcolumnbreak
    \global\previndexlevel = 0\relax}

\def \endindex {%
    \endgraf                    % End last entry.
    \vbox{}%                    % Force start of column if not already.
  \endgroup
  \zclearindexmarks
  \everycolumn = {}%
  \setpagecolumns{\usetextmeasure}{1}{0pt}%
  \global\decrement \indexdepth
  \endblockscope{index}}

\def \zixparsepos {%
  \zixcols = 1
  \def \zixpageuse {\usetextmeasure}%
  \setflag \zixrunin = \false
  \let \oneup = \relax
  \let \outline = \relax
  \def \runin {\setflag \zixrunin = \true}%
  \def \threeup {\zixcols = 3}%
  \def \twoup {\zixcols = 2}%
  \let \usetextmeasure = \relax
  \let \usetextwidth = \relax
  \def \usetypewidth {\def \zixpageuse {\usetypewidth}}%
  \the\position}

%                       Format Index Entries
%                       ------ ----- -------


\setflag \zixbreak = \false


% This macro formats the main portion of an index entry.

\def \indexentry #1#2#3{%                       level{heading}{locators}
  \ifcase #1%
  \or                                           % Level 1 heading:
    \endgraf
    \zclearindexmarks
    \zixmaybeforcebreak
    \hangindent = \parhang \hangafter = 1
    {\name{\index\zixname entryiformat}{#2\setmarkinfo{4}{\true}{#2}}{#3}}%
  \or                                           % Level 2 heading:
    \if \notp{\zixrunin}%
      \endgraf
      \if \eqlp{\previndexlevel}{1}\vpenalty{\breaknever}\fi
    \fi
    \setmarkinfo{5}{\false}{}%
    \zixmaybeforcebreak
    \hangindent = \parhang \hangafter = 1
    {\name{\index\zixname entryiiformat}{#2\setmarkinfo{5}{\true}{#2}}{#3}}%
  \or                                           % Level 3 heading:
    \if \notp{\zixrunin}\endgraf \fi
    \zixmaybeforcebreak
    \hangindent = \parhang \hangafter = 1
    {\name{\index\zixname entryiiiformat}{#2}{#3}}%
  \fi
  \global\previndexlevel = #1\relax
  \ignorespaces}

% This macro format any deferred locators.

\def \indexdeferred #1#2{%                      level{locators}
  \ifcase #1%
  \or                                           % Level 1 deferred locators:
    \if \notp{\zixrunin}\endgraf \fi
    \setmarkinfo{5}{\false}{}%
    \zixmaybeforcebreak
    \hangindent = \parhang \hangafter = 1
    {\name{\index\zixname deferrediformat}{#2}}%
  \or                                           % Level 2 deferred locators:
    \if \notp{\zixrunin}\endgraf \fi
    \zixmaybeforcebreak
    \hangindent = \parhang \hangafter = 1
    {\name{\index\zixname deferrediiformat}{#2}}%
  \fi
  \ignorespaces}

\def \alphabreak #1{%                           {text}
  \endgraf
  \zclearindexmarks
  \zixmaybeforcebreak
  \alphaformat{#1}%
  \endgraf}

\def \zclearindexmarks {%
  \setmarkinfo{4}{\false}{}%
  \setmarkinfo{5}{\false}{}}

\def \zixcolumnbreak {%
  \setflag \zixbreak = \true}

\def \zixmaybeforcebreak {%
  \if \zixbreak
    \zfullcolumnbreak
    \setflag \zixbreak = \false
  \fi}


% This macro is invoked at the beginning of each column to perform
% any first- and second-level entry carryovers. It only includes
% carryovers on the first column of versos.

\def \zixcarryover {%
  \if \andp{\evenpagep}{\firstcolumnp}%
    {\setflag \znext = {\markinfo{\bottommark}{4}{1}}%
     \if \znext
       \hangindent = \parhang \hangafter = 1
       \name{\index\zixname carryoveriformat}{\markinfo{\bottommark}{4}{2}}%
       \setflag \znext = {\markinfo{\bottommark}{5}{1}}%
       \if \znext
         \hangindent = \parhang \hangafter = 1
         \name{\index\zixname carryoveriiformat}{\markinfo{\bottommark}{5}{2}}%
       \fi
       \endgraf
       \tdimena = \zoutputcoldepth              % Compensate for depth of
       \advance \tdimena by -\prevdepth         % previous column v. depth
       \kern \tdimena                           % of carryover line(s).
     \fi}%
  \fi}

%                       Built-in Entry Formats
%                       -------- ----- -------


% These entry formats and carryovers are for a typical outliine style index.

\def \indexoutlineentryiformat #1#2{%
  \noindent #1#2\par}

\def \indexoutlineentryiiformat #1#2{%
  \indent #1#2\par}

\def \indexoutlineentryiiiformat #1#2{%
  \indent\indent #1#2\par}

\def \indexoutlinedeferrediformat #1{%
  \indent #1\par}

\def \indexoutlinedeferrediiformat #1{%
  \indent\indent #1\par}

\def \indexoutlinecarryoveriformat #1{%
  \noindent #1 \tie{\emph{(continued)}}\par}

\def \indexoutlinecarryoveriiformat #1{%
  \indent #1 \tie{\emph{(continued)}}\par}

% These entry formats and carryovers are for a typical run-in style index.

\setflag \zixplain = \false

\def \indexruninentryiformat #1#2{%
  \noindent #1#2%
  \global\setflag \zixplain = {\emptyargp{#2}}%
  \if \zixplain :\space \fi}

\def \indexruninentryiiformat #1#2{%
  \if \notp{\zixplain};\space \fi
  \global\setflag \zixplain = \false
  #1#2}

\def \indexruninentryiiiformat #1#2{%
  \error{runiniii}{It makes no sense to have level-3 entries in a run-in index}}

\def \indexrunindeferrediformat #1{%
  . #1}

\def \indexrunincarryoveriformat #1{%
  \noindent #1 \tie{\emph{(continued)}}\par}

\def \indexrunincarryoveriiformat #1{%
  \relax}