# -*- mode: Perl -*-
# /=====================================================================\ #
# |  natbib                                                             | #
# | Implementation for LaTeXML                                          | #
# |=====================================================================| #
# | Part of LaTeXML:                                                    | #
# |  Public domain software, produced as part of work done by the       | #
# |  United States Government & not subject to copyright in the US.     | #
# |---------------------------------------------------------------------| #
# | Bruce Miller <bruce.miller@nist.gov>                        #_#     | #
# | http://dlmf.nist.gov/LaTeXML/                              (o o)    | #
# \=========================================================ooo==U==ooo=/ #
package LaTeXML::Package::Pool;
use strict;
use warnings;
use LaTeXML::Package;

#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# natbib
#   following natbib.pdf document
# The basic support function for citation styles is in LaTeX.pool
# Other formatting support is in Post::MakeBibliography
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

#======================================================================
# 5. Package Options

# The kind of citation
DeclareOption('numbers', sub {
    setCitationStyle(numbers => 1);
    ExecuteOptions('square', 'comma', 'nobibstyle'); });
DeclareOption('super', sub {
    setCitationStyle(super => 1, open => Tokens(), close => Tokens());
    ExecuteOptions('nobibstyle'); });
DeclareOption('authoryear', sub {
    setCitationStyle(authoryear => 1);
    ExecuteOptions('round', 'semicolon', 'bibstyle'); });
# # The kind of braces around citations
DeclareOption('round', sub {
    setCitationStyle(round => 1);
    ExecuteOptions('nobibstyle'); });
DeclareOption('curly', sub {
    setCitationStyle(curly => 1);
    ExecuteOptions('nobibstyle'); });
DeclareOption('square', sub {
    setCitationStyle(square => 1);
    ExecuteOptions('nobibstyle'); });
DeclareOption('angle', sub {
    setCitationStyle(angle => 1);
    ExecuteOptions('nobibstyle'); });

# The kind of separator between multiple citations
DeclareOption('comma', sub {
    setCitationStyle(comma => 1);
    ExecuteOptions('nobibstyle'); });
DeclareOption('semicolon', sub {
    setCitationStyle(semicolon => 1);
    ExecuteOptions('nobibstyle'); });
DeclareOption('colon', sub {
    ExecuteOptions('semicolon'); });    # SIC!

# This disables any future \bibstyle found in the aux file (or from \bibliographystyle)
DeclareOption('nobibstyle', sub {
    Let('\bibstyle', '\@gobble'); });
DeclareOption('bibstyle', sub {
    Let('\bibstyle', '\@citestyle'); });

# sorting options
DeclareOption('sort',          sub { });
DeclareOption('sort&compress', sub { });
DeclareOption('compress',      sub { });

DeclareOption('longnamesfirst', sub { });

DeclareOption('openbib', sub { });
DeclareOption('sectionbib', sub {
    AssignMapping('BACKMATTER_ELEMENT', 'ltx:bibliography' => 'ltx:section'); });
DeclareOption('nonamebreak', sub { });

# They _say_ round & semicolon but ...
#ExecuteOptions('round','semicolon','authoryear');
#ExecuteOptions('square','comma','authoryear');
setCitationStyle(round => 1, semicolon => 1);

# NOTE \bibliographystyle puts \bibstyle in the *.aux, read at the beginning!
# Normally, \bibliography appears at the end next to the bibliography.... it's TOO LATE!
# [FUTURE: Maybe this can be encoded as attributes on ltx:bibliography ???
#  But then we need to put in stub elements to be filled in by CrossRef! Ugh]
# In any case, \bibstyle may get redefined by options, so define it now!
DefMacro('\bibstyle{}', sub {
    my $style = T_CS('\bibstyle@' . ToString($_[1]));
    (LookupDefinition($style) ? ($style) : (T_CS('\relax'))); });
#AtBeginDocument{\global\let\bibstyle=\@gobble}
Let('\@citestyle', '\bibstyle');

# # They _say_ round & semicolon but ...
ExecuteOptions('round', 'semicolon', 'authoryear');
# ExecuteOptions('square','comma','authoryear');
AssignValue(CITE_STYLE => 'authoryear');    # the default?

ProcessOptions();
#======================================================================
# 2.3 Basic Citation Commands

# Leverage the definitions in LaTeX.pool.

# Note that LaTeX's \cite command is treated almost equivalent to \citet in authoryear mode,
# but like \citep in numbers mode.
DefMacro('\cite OptionalMatch:* [][] Semiverbatim', sub {
    my ($gullet, $star, $pre, $post, $keys) = @_;
    my ($style, $open, $close, $ns, $ay)
      = map { LookupValue($_) } qw(CITE_STYLE CITE_OPEN CITE_CLOSE CITE_NOTE_SEPARATOR CITE_AY_SEPARATOR);
    if (!$post) { ($pre, $post) = (undef, $pre); }
    $pre  = undef if IsEmpty($pre);
    $post = undef if IsEmpty($post);
    my $author = ($star ? "FullAuthors" : "Authors");
    if ($style eq 'numbers') {
      Invocation(T_CS('\@@cite'),
        Tokens(Explode('cite')),
        Tokens($open, ($pre ? ($pre, T_SPACE) : ()),
          Invocation(T_CS('\@@bibref'), Tokens(Explode("Number")), $keys, undef, undef),
          ($post ? ($ns, T_SPACE, $post) : ()),
          $close)); }
    elsif ($style eq 'super') {
      Invocation(T_CS('\@@cite'),
        Tokens(Explode('cite')),
        Tokens(($pre ? ($pre, T_SPACE) : ()), Invocation(T_CS('\textsuperscript'),
            Invocation(T_CS('\@@bibref'), Tokens(Explode("Number")), $keys,
              undef, undef)),
          ($post ? (T_SPACE, $post) : ()))); }
    else {
      # Appears to be textual, unless pre or post phrase, then parenthetical (!)
      if ($pre || $post) {
        Invocation(T_CS('\@@cite'),
          Tokens(Explode('cite')),
          Tokens($open->unlist, ($pre ? ($pre, T_SPACE) : ()),
            Invocation(T_CS('\@@bibref'),
              Tokens(Explode($author . "Phrase1Year")),
              $keys,
              Invocation(T_CS('\@@citephrase'),
                Tokens($ay->unlist, T_SPACE)), undef),
            ($post ? ($ns, T_SPACE, $post) : ()), $close)); }
      else {
        Invocation(T_CS('\@@cite'),
          Tokens(Explode('cite')),
          Invocation(T_CS('\@@bibref'),
            Tokens(Explode($author . " Phrase1YearPhrase2")),
            $keys,
            Invocation(T_CS('\@@citephrase'), $open),
            Invocation(T_CS('\@@citephrase'), $close))); } }
}, locked => 1);

DefMacro('\citet OptionalMatch:* [][] Semiverbatim', sub {
    my ($gullet, $star, $pre, $post, $keys) = @_;
    my ($style, $open, $close, $ns)
      = map { LookupValue($_) } qw(CITE_STYLE CITE_OPEN CITE_CLOSE CITE_NOTE_SEPARATOR);
    if (!$post) { ($pre, $post) = (undef, $pre); }
    $pre  = undef if IsEmpty($pre);
    $post = undef if IsEmpty($post);
    my $author = ($star ? "FullAuthors" : "Authors");
    if ($style eq 'numbers') {
      Invocation(T_CS('\@@cite'),
        Tokens(Explode('citet')),
        Tokens(    #($pre ? ($pre, T_SPACE) : ()),
          Invocation(T_CS('\@@bibref'),
            Tokens(Explode("$author Phrase1NumberPhrase2")),
            $keys,
            Invocation(T_CS('\@@citephrase'),
              Tokens($open, ($pre ? ($pre, T_SPACE) : ()))),
            Invocation(T_CS('\@@citephrase'),
              Tokens(($post ? ($ns->unlist, T_SPACE, $post->unlist) : ()), $close->unlist))
          )))->unlist; }
    elsif ($style eq 'super') {
      Invocation(T_CS('\@@cite'),
        Tokens(Explode('citet')),
        Tokens(($pre ? ($pre, T_SPACE) : ()),
          Invocation(T_CS('\@@bibref'),
            Tokens(Explode("$author Phrase1SuperPhrase2")),
            $keys, undef, undef)->unlist,
          ($post ? ($ns, T_SPACE, $post->unlist) : ()))); }
    else {
      Invocation(T_CS('\@@cite'),
        Tokens(Explode('citet')),
        Invocation(T_CS('\@@bibref'),
          Tokens(Explode("$author Phrase1YearPhrase2")),
          $keys,
          Invocation(T_CS('\@@citephrase'),
            Tokens($open, ($pre ? ($pre, T_SPACE) : ()))),
          Invocation(T_CS('\@@citephrase'),
            Tokens(($post ? ($ns, T_SPACE, $post) : ()), $close)))); }
}, locked => 1);

DefMacro('\citep OptionalMatch:* [][] Semiverbatim', sub {
    my ($gullet, $star, $pre, $post, $keys) = @_;
    my ($style, $open, $close, $ns, $ay)
      = map { LookupValue($_) } qw(CITE_STYLE CITE_OPEN CITE_CLOSE
      CITE_NOTE_SEPARATOR CITE_AY_SEPARATOR);
    if (!$post) { ($pre, $post) = (undef, $pre); }
    $pre  = undef if IsEmpty($pre);
    $post = undef if IsEmpty($post);
    my $author = ($star ? "FullAuthors" : "Authors");

    if ($style eq 'numbers') {
      Invocation(T_CS('\@@cite'),
        Tokens(Explode('citep')),
        Tokens($open, ($pre ? ($pre, T_SPACE) : ()),
          Invocation(T_CS('\@@bibref'), Tokens(Explode("Number")), $keys, undef, undef),
          ($post ? ($ns, T_SPACE, $post) : ()), $close)); }
    elsif ($style eq 'super') {
      Invocation(T_CS('\@@cite'),
        Tokens(Explode('citep')),
        Tokens(($pre ? ($pre, T_SPACE) : ()),
          Invocation(T_CS('\@@bibref'), Tokens(Explode("Super")), $keys, undef, undef),
          ($post ? (T_SPACE, $post) : ()))); }
    else {
      Invocation(T_CS('\@@cite'),
        Tokens(Explode('citep')),
        Tokens($open->unlist, ($pre ? ($pre, T_SPACE) : ()),
          Invocation(T_CS('\@@bibref'),
            Tokens(Explode("${author}Phrase1Year")),
            $keys,
            Invocation(T_CS('\@@citephrase'), Tokens($ay->unlist, T_SPACE)),
            undef),
          ($post ? ($ns, T_SPACE, $post) : ()), $close)); }
}, locked => 1);

#======================================================================
# 2.4 Extended Citation Commands
DefMacro('\@@cite@noparens', sub {
    AssignValue(CITE_OPEN  => Tokens());
    AssignValue(CITE_CLOSE => Tokens()); });

# The next two are the same as \citet, \citep, but redefine open & close to empty.
DefMacro('\citealt OptionalMatch:* [][] Semiverbatim', sub {
    my ($gullet, $star, $pre, $post, $keys) = @_;
    (T_CS('\bgroup'), T_CS('\@@cite@noparens'),
      Invocation(T_CS('\citet'), $star, $pre, $post, $keys)->unlist,
      T_CS('\egroup')); });

DefMacro('\citealp OptionalMatch:* [][] Semiverbatim', sub {
    my ($gullet, $star, $pre, $post, $keys) = @_;
    (T_CS('\bgroup'), T_CS('\@@cite@noparens'),
      Invocation(T_CS('\citep'), $star, $pre, $post, $keys)->unlist,
      T_CS('\egroup')); });

DefMacro('\citenum Semiverbatim', sub {    # No optional ?
    my ($gullet, $keys) = @_;
    Invocation(T_CS('\@@cite'),
      Tokens(Explode('citenum')),
      Tokens(Invocation(T_CS('\@@bibref'), Tokens(Explode("Number")), $keys, undef, undef))); });

# Sorta right, but would like to avoid the nested <ltx:cite>!
# maybe can neutralize \@@cite?
DefMacro('\citetext', '\@@cite');

DefMacro('\citeauthor OptionalMatch:* [][] Semiverbatim', sub {
    my ($gullet, $star, $pre, $post, $keys) = @_;
    my $author = ($star ? "FullAuthors" : "Authors");
    if (!$post) { ($pre, $post) = (undef, $pre); }
    $pre  = undef if IsEmpty($pre);
    $post = undef if IsEmpty($post);
    my $ns = LookupValue('CITE_NOTE_SEPARATOR');
    Invocation(T_CS('\@@cite'),
      Tokens(Explode('citeauthor')),
      Tokens(    # ($pre ? ($pre, T_SPACE) : ()),
        Invocation(T_CS('\@@bibref'), Tokens(Explode($author)), $keys, undef, undef),
        ($post ? ($ns, T_SPACE, $post) : ()))); });

DefMacro('\citefullauthor [][] Semiverbatim', sub {
    my ($gullet, $pre, $post, $keys) = @_;
    if (!$post) { ($pre, $post) = (undef, $pre); }
    $pre  = undef if IsEmpty($pre);
    $post = undef if IsEmpty($post);
    my $ns = LookupValue('CITE_NOTE_SEPARATOR');
    Invocation(T_CS('\@@cite'),
      Tokens(Explode('citefullauthor')),
      Tokens(    # ($pre ? ($pre, T_SPACE) : ()),
        Invocation(T_CS('\@@bibref'), Tokens(Explode("FullAuthors")), $keys, undef, undef),
        ($post ? ($ns, T_SPACE, $post) : ()))); });

DefMacro('\citeyear [][] Semiverbatim', sub {
    my ($gullet, $pre, $post, $keys) = @_;
    if (!$post) { ($pre, $post) = (undef, $pre); }
    $pre  = undef if IsEmpty($pre);
    $post = undef if IsEmpty($post);
    my $ns = LookupValue('CITE_NOTE_SEPARATOR');
    Invocation(T_CS('\@@cite'),
      Tokens(Explode('citeyear')),
      Tokens(    # ($pre ? ($pre, T_SPACE) : ()),
        Invocation(T_CS('\@@bibref'), Tokens(Explode("Year")), $keys, undef, undef),
        ($post ? ($ns, T_SPACE, $post) : ()))); });

DefMacro('\citeyearpar [][] Semiverbatim', sub {
    my ($gullet, $pre, $post, $keys) = @_;
    my ($open, $close) = map { LookupValue($_) } qw(CITE_OPEN CITE_CLOSE);
    if (!$post) { ($pre, $post) = (undef, $pre); }
    $pre  = undef if IsEmpty($pre);
    $post = undef if IsEmpty($post);
    my $ns = LookupValue('CITE_NOTE_SEPARATOR');
    Invocation(T_CS('\@@cite'),
      Tokens(Explode('citeyearpar')),
      Tokens($open,
        ($pre ? ($pre, T_SPACE) : ()),
        Invocation(T_CS('\@@bibref'), Tokens(Explode("Year")), $keys, undef, undef),
        ($post ? ($ns, T_SPACE, $post) : ()),
        $close)); });

#======================================================================
# 2.5 Forcing Upper Cased Name
# These are SUPPOSED to capitalize the first letter, .. but
DefMacro('\Citet',      '\citet');
DefMacro('\Citep',      '\citep');
DefMacro('\Citealt',    '\citealt');
DefMacro('\Citealp',    '\citealp');
DefMacro('\Citeauthor', '\citeauthor');

#======================================================================
# 2.6 Citation Aliasing
# Citation aliasing is achieved with
#   \defcitealias{key}{text}
#   \citetalias{key}  ==>> text
#   \citepalias{key}  ==>> (text)

# should end the defined key with \@extra@b@citeb ???

DefPrimitive('\defcitealias Semiverbatim {}', sub {
    my ($gullet, $key, $text) = @_;
    DefMacroI(T_CS('\al@' . ToString($key)), undef, $text); });

# These use the above defined text to fill in the bibref
# (which will still be a link to the bibitem!)
DefMacro('\citetalias [][] Semiverbatim', sub {
    my ($gullet, $pre, $post, $key) = @_;
    my ($open, $close) = map { LookupValue($_) } qw(CITE_OPEN CITE_CLOSE);
    if (!$post) { ($pre, $post) = (undef, $pre); }
    $pre  = undef if IsEmpty($pre);
    $post = undef if IsEmpty($post);
    Invocation(T_CS('\@@cite'),
      Tokens(Explode('citealias')),
      Tokens(($pre ? ($pre, T_SPACE) : ()),
        Invocation(T_CS('\@@bibref'),
          Tokens(Explode("Phrase1")),
          $key,
          Invocation(T_CS('\@@citephrase'), T_CS('\al@' . ToString($key)))),
        ($post ? (T_SPACE, $post) : ()))); });

DefMacro('\citepalias [][] Semiverbatim', sub {
    my ($gullet, $pre, $post, $key) = @_;
    my ($open, $close, $ns) = map { LookupValue($_) } qw(CITE_OPEN CITE_CLOSE CITE_NOTE_SEPARATOR);
    Invocation(T_CS('\@@cite'),
      Tokens(Explode('citepalias')),
      Tokens($open,
        ($pre ? ($pre, T_SPACE) : ()),
        Invocation(T_CS('\@@bibref'),
          Tokens(Explode("Phrase1")),
          $key,
          Invocation(T_CS('\@@citephrase'), T_CS('\al@' . ToString($key)))),
        ($post ? ($ns, T_SPACE, $post) : ()),
        $close)); });

#======================================================================
# 2.9 Selecting Citation Punctuation
DefKeyVal('natbib', 'authoryear', '', 'true');
DefKeyVal('natbib', 'numbers',    '', 'true');
DefKeyVal('natbib', 'super',      '', 'true');
DefKeyVal('natbib', 'round',      '', 'true');
DefKeyVal('natbib', 'square',     '', 'true');
DefKeyVal('natbib', 'open',       '');
DefKeyVal('natbib', 'close',      '');
DefKeyVal('natbib', 'semicolon',  '');
DefKeyVal('natbib', 'comma',      '');
DefKeyVal('natbib', 'citesep',    '');
DefKeyVal('natbib', 'aysep',      '');
DefKeyVal('natbib', 'yysep',      '');
DefKeyVal('natbib', 'notesep',    '');

AssignValue(CITE_AY_SEPARATOR => T_OTHER(','));

sub setCitationStyle {
  my (@pairs) = @_;
  while (@pairs) {
    my ($key, $value) = (shift(@pairs), shift(@pairs));
    $key = ToString(Digest($key)) if ref $key;
    if    ($key eq 'authoryear') { AssignValue(CITE_STYLE => 'authoryear'); }
    elsif ($key eq 'numbers')    { AssignValue(CITE_STYLE => 'numbers'); }
    elsif ($key eq 'super')      { AssignValue(CITE_STYLE => 'super'); }
    elsif ($key eq 'round')      { AssignValue(CITE_OPEN  => T_OTHER('('));
      AssignValue(CITE_CLOSE => T_OTHER(')')); }
    elsif ($key eq 'square') { AssignValue(CITE_OPEN => T_OTHER('['));
      AssignValue(CITE_CLOSE => T_OTHER(']')); }
    elsif ($key eq 'curly') { AssignValue(CITE_OPEN => T_OTHER('{'));
      AssignValue(CITE_CLOSE => T_OTHER('}')); }
    elsif ($key eq 'angle') { AssignValue(CITE_OPEN => T_OTHER('<'));
      AssignValue(CITE_CLOSE => T_OTHER('>')); }
    elsif ($key eq 'open')      { AssignValue(CITE_OPEN           => $value); }
    elsif ($key eq 'close')     { AssignValue(CITE_CLOSE          => $value); }
    elsif ($key eq 'semicolon') { AssignValue(CITE_SEPARATOR      => T_OTHER(';')); }
    elsif ($key eq 'comma')     { AssignValue(CITE_SEPARATOR      => T_OTHER(',')); }
    elsif ($key eq 'aysep')     { AssignValue(CITE_AY_SEPARATOR   => $value); }
    elsif ($key eq 'yysep')     { AssignValue(CITE_YY_SEPARATOR   => $value); }
    elsif ($key eq 'notesep')   { AssignValue(CITE_NOTE_SEPARATOR => $value); }
    else {
      Warn('unexpected', $key, undef, "Unexpected Citation Style keyword '$key'"); } }
  return; }

DefPrimitive('\setcitestyle RequiredKeyVals:natbib', sub {
    setCitationStyle($_[1]->getPairs); });

DefPrimitive('\bibpunct[]{}{}{}{}{}{}', sub {
    my ($stomach, $notesep, $open, $close, $sep, $style, $aysep, $yysep) = @_;
    $style = ToString(Digest($style));
    AssignValue(CITE_OPEN      => $open);
    AssignValue(CITE_CLOSE     => $close);
    AssignValue(CITE_SEPARATOR => $sep);
    AssignValue(CITE_STYLE => ($style eq 'n' ? 'numbers' : ($style eq 's' ? 'super' : 'authoryear')));
    AssignValue(CITE_AY_SEPARATOR   => $aysep);
    AssignValue(CITE_YY_SEPARATOR   => $yysep);
    AssignValue(CITE_NOTE_SEPARATOR => $notesep) if $notesep;
    return; });

DefMacro('\citestyle{}', '\@citestyle{#1}\let\bibstyle\@gobble');

DefMacro('\bibstyle@chicago',    '\bibpunct{(}{)}{;}{a}{,}{,}');
DefMacro('\bibstyle@named',      '\bibpunct{[}{]}{;}{a}{,}{,}');
DefMacro('\bibstyle@agu',        '\bibpunct{[}{]}{;}{a}{,}{,~}');    #Amer. Geophys. Union
DefMacro('\bibstyle@copernicus', '\bibpunct{(}{)}{;}{a}{,}{,}');     #Copernicus Publications
Let('\bibstyle@egu', '\bibstyle@copernicus');
Let('\bibstyle@egs', '\bibstyle@copernicus');
DefMacro('\bibstyle@agsm',     '\bibpunct{(}{)}{,}{a}{}{,}\gdef\harvardand{\&}');
DefMacro('\bibstyle@kluwer',   '\bibpunct{(}{)}{,}{a}{}{,}\gdef\harvardand{\&}');
DefMacro('\bibstyle@dcu',      '\bibpunct{(}{)}{;}{a}{;}{,}\gdef\harvardand{and}');
DefMacro('\bibstyle@aa',       '\bibpunct{(}{)}{;}{a}{}{,}');        # Astronomy & Astrophysics
DefMacro('\bibstyle@pass',     '\bibpunct{(}{)}{;}{a}{,}{,}');       #Planet. & Space Sci
DefMacro('\bibstyle@anngeo',   '\bibpunct{(}{)}{;}{a}{,}{,}');       #Annales Geophysicae
DefMacro('\bibstyle@nlinproc', '\bibpunct{(}{)}{;}{a}{,}{,}');       #Nonlin.Proc.Geophys.
DefMacro('\bibstyle@cospar',   '\bibpunct{/}{/}{,}{n}{}{}');
DefMacro('\bibstyle@esa',      '\bibpunct{(Ref.~}{)}{,}{n}{}{}');
DefMacro('\bibstyle@nature',   '\bibpunct{}{}{,}{s}{}{\textsuperscript{,}}');
DefMacro('\bibstyle@plain',    '\bibpunct{[}{]}{,}{n}{}{,}');
Let('\bibstyle@alpha', '\bibstyle@plain');
Let('\bibstyle@abbrv', '\bibstyle@plain');
Let('\bibstyle@unsrt', '\bibstyle@plain');
DefMacro('\bibstyle@plainnat', '\bibpunct{[}{]}{,}{a}{,}{,}');
Let('\bibstyle@abbrvnat', '\bibstyle@plainnat');
Let('\bibstyle@unsrtnat', '\bibstyle@plainnat');

#======================================================================
# 2.12 Other Formatting Options
# mostly ignored...
DefMacro('\bibname',     'Bibliography');
DefMacro('\refname',     'References');
DefMacro('\bibsection',  '');
DefMacro('\bibpreamble', '');
DefMacro('\bibfont',     '');
DefMacro('\citenumfont', '');
DefMacro('\bibnumfmt{}', '#1');
DefRegister('\bibhang', Dimension(0));
DefRegister('\bibsep',  Dimension(0));

#======================================================================
# 2.13 Automatic Indexing of Citations
# Ignored, but could be done...
# However, it is basically equivalent to backrefs which are
# automatically handled in MakeBibliography, anyway...
RawTeX('\newif\ifciteindex');
DefMacro('\citeindextrue',  '');
DefMacro('\citeindexfalse', '');
DefMacro('\citeindextype',  '');

#======================================================================
# 2.17 Long Author List on First Citation
#  Ignored (for now...)
DefMacro('\shortcites Semiverbatim', '');

#======================================================================
# Less Documented
#  For manually formatted bibliographies, the following magical incantations
# will be recognized to deliniate the author and year:
#   \bibitem[Jones et al.(1990)]{key}...
#   \bibitem[Jones et al.(1990)Jones, Baker, and Williams]{key}...
#   \bibitem[Jones et al., 1990]{key}...
#   \bibitem[\protect\citeauthoryear{Jones, Baker, and Williams}{Jones et al.}{1990}]{key}...
#   \bibitem[\protect\citeauthoryear{Jones et al.}{1990}]{key}...
#   \bibitem[\protect\astroncite{Jones et al.}{1990}]{key}...
#   \bibitem[\protect\citename{Jones et al., }1990]{key}...
#   \harvarditem[Jones et al.]{Jones, Baker, and Williams}{1990}{key}...

DefMacro('\lx@nat@bibitem',
##         '\reset@natbib@cites\refstepcounter{@bibitem}\@ifnextchar[{\@lbibitem}{\@lbibitem[\the@bibitem]}',
  '\reset@natbib@cites\refstepcounter{@bibitem}\@ifnextchar[{\@lbibitem}{\@lbibitem[]}',
  locked => 1);
# Careful: since OmniBus also defines \bibitem and LaTeX.pool has a save/restore mechanism,
# it is safer to \let to a constant natbib-specific macro definition. Test with arXiv:2007.09909
Let('\bibitem', '\lx@nat@bibitem');

RawTeX(<<'EOTeX');
%%%
\def\citeauthoryear#1#2#3(@)(@)\@nil#4{%
  \if\relax#3\relax
    \NAT@wrout{\the@bibitem}{#2}{#1}{}{#4}\else
    \NAT@wrout{\the@bibitem}{#3}{#2}{#1}{#4}\fi}
\let\natbib@citeauthoryear\citeauthoryear
\def\astroncite#1#2(@)(@)\@nil#3{%
  \NAT@wrout{\the@bibitem}{#2}{#1}{}{#3}}
\let\natbib@astroncite\astroncite
\def\citename#1#2(@)(@)\@nil#3{%
  \expandafter\NAT@apalk#1#2, \@nil{#3}}
\let\natbib@citename\citename
\newcommand\harvarditem[4][]{%
  \if\relax#1\relax\bibitem[#2(#3)]{#4}\else\bibitem[#1(#3)#2]{#4}\fi }
%%%%
\newcommand\NAT@ifcmd{\futurelet\NAT@temp\NAT@ifxcmd}
\newcommand\NAT@ifxcmd{\ifx\NAT@temp\relax\else\expandafter\NAT@bare\fi}
\def\NAT@bare#1(#2)#3(@)#4\@nil#5{%
  \if @#2%
    \expandafter\NAT@apalk#1, , \@nil{#5}\else
    \NAT@wrout{\the@bibitem}{#2}{#1}{#3}{#5}\fi}
\def\NAT@apalk#1, #2, #3\@nil#4{%
%  \if\relax#2\relax\NAT@wrout{#1}{}{}{}{#4}\else\NAT@wrout{\the@bibitem}{#2}{#1}{}{#4}\fi}
  \NAT@wrout{\the@bibitem}{#2}{#1}{}{#4}}
%%%%
EOTeX
# Sometimes, perversely, redefined, so re-redefine them now...
DefPrimitiveI('\reset@natbib@cites', undef, sub {
    Let('\citeauthoryear', '\natbib@citeauthoryear');
    Let('\astroncite',     '\natbib@astroncite');
    Let('\citename',       '\natbib@citename'); });

# Kinda rediculous simulating this here (we can almost pull it off in TeX)
# but the TeX tends to be quite brittle in the wilds of arXiv, so...
DefMacro('\lx@NAT@parselabel{}{}', sub {
    my ($gullet, $label, $key) = @_;
    my ($number, $year, $authors, $fullauthors);
    my $bare   = 1;
    my @tokens = $label->unlist;
    shift(@tokens) if Equals(T_CS('\protect'), $tokens[0]);    # Ignore \protect
    if ($tokens[0] && ($tokens[0]->getCatcode == CC_CS)) {
      my $cs = $tokens[0];
      my ($a1, $a2);
      if (Equals(T_CS('\citeauthoryear'), $cs)) {
        # \bibitem[\protect\citeauthoryear{Jones, Baker, and Williams}{Joneset al.}{1990}]{key}
        # \bibitem[\protect\citeauthoryear{Jones et al.}{1990}]{key}...
        # \bibitem[\protect\citeauthoryear{Jones} {1990}]{key}...
        shift(@tokens);
        ($a1, @tokens) = NAT_peel_arg(@tokens);
        ($a2, @tokens) = NAT_peel_arg(@tokens);
        if (@tokens) {
          $fullauthors = $a1; $authors = $a2; $year = Tokens(@tokens); $bare = 0; }
        else {
          $authors = $a1; $year = $a2; $bare = 0; } }
      elsif (Equals(T_CS('\astroncite'), $cs)) {
        # \bibitem[\protect\astroncite{Jones et al.}{1990}]{key}...
        shift(@tokens);
        ($a1, @tokens) = NAT_peel_arg(@tokens);
        ($a2, @tokens) = NAT_peel_arg(@tokens);
        $authors = $a1; $year = $a2; $bare = 0; }
      elsif (Equals(T_CS('\citename'), $cs)) {
        # \bibitem[\protect\citename{Jones et al., }1990]{key}...
        shift(@tokens);
        ($a1, @tokens) = NAT_peel_arg(@tokens);
        $authors = $a1; $year = Tokens(@tokens); $bare = 0; } }
    if ($bare) {
      @tokens = Expand($label)->unlist;
      # \bibitem[Jones et al.(1990)]{key}...
      # \bibitem[Jones et al.(1990)Jones, Baker, and Williams]{key}...
      # \bibitem[Jones et al., 1990]{key}...
      my @arg = ();
      while (@tokens && !T_OTHER('(')->equals($tokens[0])) {
        push(@arg, shift(@tokens)); }
      shift(@tokens);
      my @year = ();
      while (@tokens && !T_OTHER(')')->equals($tokens[0])) {
        push(@year, shift(@tokens)); }
      shift(@tokens);
      if (!@year) {
        while (@arg && ($arg[-1]->getCatcode == CC_OTHER)
          && ($arg[-1]->getString =~ /\d/)) {    # or even digits only?
          unshift(@year, pop(@arg)); }
        #pop(@arg);
      }
      $authors = Tokens(@arg); $year = Tokens(@year); $fullauthors = Tokens(@tokens); }
##    Debug("NATLABEL: ".ToString($label)." #=".ToString($number)." y=".ToString($year)
##          ." a=".ToString($authors)." f=".ToString($fullauthors)." k=".ToString($key));
    return Invocation(T_CS('\NAT@wrout'), ($number || T_CS('\the@bibitem')), $year,
      $authors, $fullauthors, $key) });

sub NAT_peel_arg {
  my (@tokens) = @_;
  my @arg = ();
  if (!@tokens) {
    return; }
  elsif (@tokens && ($tokens[0]->getCatcode != CC_BEGIN)) {
    return (shift(@tokens), @tokens); }
  else {
    my $level = 1;
    shift(@tokens);
    while (my $t = shift(@tokens)) {
      my $cc = $t->getCatcode;
      $level++ if $cc == CC_BEGIN;
      $level-- if $cc == CC_END;
      last unless $level > 0;
      push(@arg, $t); }
    return (Tokens(@arg), @tokens); } }

# By this time, \NAT@wrout should look like:
# \NAT@wrout{number}{year}{authors}{fullauthors}{bibkey}
# So, we'll do one extra step, and format the refnum form
DefMacro('\NAT@wrout{}{}{}{} Semiverbatim', sub {
    my ($gullet, $number, $year, $authors, $fullauthors, $key) = @_;
    my ($style, $open, $close) = map { LookupValue($_) } qw(CITE_STYLE CITE_OPEN CITE_CLOSE);
    $style = 'number' if IsEmpty($authors) || IsEmpty($year);
    if ($style eq 'number') {
      Invocation(T_CS('\NAT@@wrout'), $number, $year, $authors, $fullauthors,
        Tokens($open, $number, $close),
        $key)->unlist; }
    else {
      Invocation(T_CS('\NAT@@wrout'), $number, $year, $authors, $fullauthors,
        Tokens($authors, T_SPACE, $open, $year, $close),
        $key)->unlist; } });

DefConstructor('\NAT@@wrout{}{}{}{}{} Semiverbatim',
  "<ltx:tags>"
    . "?#1(<ltx:tag role='number'>#1</ltx:tag>)"
    . "?#2(<ltx:tag role='year'>#2</ltx:tag>)"
    . "?#3(<ltx:tag role='authors'>#3</ltx:tag>)"
    . "?#4(<ltx:tag role='fullauthors'>#4</ltx:tag>)"
    . "?#5(<ltx:tag role='refnum'>#5</ltx:tag>)"
    . "?#6(<ltx:tag role='key'>#6</ltx:tag>)"
    . "</ltx:tags>",
  # Allow plain & in here ???
  bounded => 1, beforeDigest => sub { Let(T_ALIGN, '\&'); }
);

# see arXiv:cond-mat/0003435 for
# an infinite loop we run into when this isn't locked
# \@@lbibitem is a DefConstructor, so the connection should be kept
DefMacro('\@lbibitem[]{}',
  #    '\@@lbibitem{#2}\NAT@ifcmd#1(@)(@)\@nil{#2}\newblock', locked => 1);
  '\@@lbibitem{#2}\lx@NAT@parselabel{#1}{#2}\newblock', locked => 1);

# Similar to the one defined in LaTeX.pool, but the bibtag's have been setup above.
DefConstructor('\@@lbibitem Semiverbatim',
  "<ltx:bibitem key='#key' xml:id='#id'>",
  afterDigest => sub {
    my $key = CleanBibKey($_[1]->getArg(1));
    my $id  = ToString(Expand(T_CS('\the@bibitem@ID')));
    $_[1]->setProperties(key => $key, id => $id); });

#======================================================================
# These macros allow you to get the pieces used in the current style
# but don't seem to be used in natbib, so redefining them does nothing.
DefMacro('\citestarts',     sub { LookupValue('CITE_OPEN')->unlist; });
DefMacro('\citeends',       sub { LookupValue('CITE_CLOSE')->unlist; });
DefMacro('\betweenauthors', 'and');

DefMacro('\harvardleft',      sub { LookupValue('CITE_OPEN')->unlist; });
DefMacro('\harvardright',     sub { LookupValue('CITE_CLOSE')->unlist; });
DefMacro('\harvardyearleft',  sub { LookupValue('CITE_OPEN')->unlist; });
DefMacro('\harvardyearright', sub { LookupValue('CITE_CLOSE')->unlist; });
DefMacro('\harvardand',       'and');

DefConstructor('\harvardurl Semiverbatim',
  "<ltx:ref href='#href'>#1</ltx:ref>",
  properties => sub { (href => CleanURL(ToString($_[1]))); });

Let('\citeN',      '\cite');
Let('\shortcite',  '\cite');
Let('\citeasnoun', '\cite');

DefMacro('\natexlab{}', '#1');    # ????

1;