# -*- mode: Perl -*-
# /=====================================================================\ #
# |  ntheorem                                                           | #
# | 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;

#**********************************************************************
# Basically, this package is similar to amsthm.sty (or theorem.sty)
# with two main enhancements:
#   endmarks
#   list of theorem (which should actually be covered in some postprocessor?)
RequirePackage('amsthm');
RequirePackage('ifthen');
# but re-redefine from amsthm from LaTeX; use save note font as head font
DefRegister('\thm@notefont' => Tokens(T_CS('\the'), T_CS('\thm@headfont')));

# options....

DefConditional('\if@thref',      undef);
DefConditional('\ifthm@inframe', undef);
DefConditional('\ifthm@tempif',  undef);

#  thmmarks, leqno, fleqn, thref
# standard, noconfig, amsthm,
# framed (needs framed.sty)

#======================================================================
foreach my $option (qw(leqno fleqn
  amsmath amsthm hyperref)) {
  DeclareOption($option, undef); }

DeclareOption('standard', sub { AssignValue('thm@usestd' => 1); });    # ?
DeclareOption('noconfig', sub { });                                    # ?

DeclareOption('framed', sub { });    # ?  [and needs framed.sty to be loaded]

DeclareOption('thmmarks', sub { });  # ?

DeclareOption('thref', sub {
    # Redefine \label to take 2nd optional argument, being the type of thing.
    # [seems out-of-place here, but...]
    # [Nominally would require support from aux file, but we'll have to see...]
    Let('\orig@label', '\label');
    # Perhaps this type could be attached somehow?
    # [too bad there's no way to record the type of objects...(too verbose)]
    DefMacro('\label Semiverbatim []', '\orig@label{#1}');
    # \thref{}  should also output the type from the above.
    # We'll use latexml's show attribute to mimic the effect
    DefConstructor('\thref OptionalMatch:* Semiverbatim',
      "<ltx:ref labelref='#label' show='typerefnum' _force_font='true'/>",
      properties => sub { (label => CleanLabel($_[2])); });
});

ProcessOptions();

#, \InTheoType should be set within theorem environment to the theorem
#  \<type>Keyword
#  \<type>Symbol
# , \Oldeqnnum, \OrganizeTheoremSymbol, \PotEndMark,
# \RestoreTags,
# \SetEndMark, \SetOnlyEndMark, \SetTagPlusEndMark, \TagsPlusEndmarks,

# \endalign, \endalignat, \endflalign, \endgather, \endmathdisplay, \endxalignat, \endxxalignat,
# \getKeywordOf,
# \mysavskip,

# \newframedtheorem, \newshadedtheorem,
# \FrameCommand

# \proofSymbol,
#, \renewtheoremstyle,
# \shadecolor,
# \theoremframecommand, \theoremkeyword,

# Note:
# * the namespace of \theoremstyle{<theoremstyle>}, \newtheoremstyle{name}
#   is distinct from the namespace of newtheorem{<theoremclass>} ,\begin{<theoremclass>}

# theoremstyle{nmae}
#    should set a recorded (sub?)set of params
# newtheoremstyle{name}<params...>
#    should associate ALL (?) params with the named style?

# newtheorem{name}
#    should record all(?) current params for that name

# \begin{name} should merge current values with ones recorded for name

DefRegister('\theoremindent'                => Dimension('0em'));
DefRegister('\theoremrightindent'           => Dimension('0em'));
DefRegister('\theorempreskipamount'         => Dimension('0em'));
DefRegister('\theorempostskipamount'        => Dimension('0em'));
DefRegister('\theoremframepreskipamount'    => Dimension('0em'));
DefRegister('\theoremframepostskipamount'   => Dimension('0em'));
DefRegister('\theoreminframepreskipamount'  => Dimension('0em'));
DefRegister('\theoreminframepostskipamount' => Dimension('0em'));
DefMacro('\theorempreskip{}',         '');
DefMacro('\theorempostskip{}',        '');
DefMacro('\theoremframepreskip{}',    '');
DefMacro('\theoremframepostskip{}',   '');
DefMacro('\theoreminframepreskip{}',  '');
DefMacro('\theoreminframepostskip{}', '');
DefMacro('\None',                     'None');
DefMacro('\NoneSymbol',               'None');
DefMacro('\NoneKeyword',              'None');
DefRegister('\shadecolor' => Tokens());

# Include a few other theorem paramters in definitions
setSavableTheoremParameters(qw(
    \thm@headfont \thm@bodyfont \thm@headpunct
    \thm@styling \thm@headstyling thm@swap
    \thm@numbering \thm@prework \thm@postwork \thm@symbol));

DefMacro('\theoremheaderfont{}', sub {
    AssignRegister('\thm@headfont' => $_[1]); });

DefMacro('\theoremseparator{}', sub {
    AssignRegister('\thm@headpunct' => $_[1]); });
DefMacro('\theoremsymbol{}', sub {
    AssignRegister('\thm@symbol' => $_[1]); });

# ???
DefMacro('\theoremprework{}', sub {
    AssignRegister('\thm@prework' => $_[1]); });
DefMacro('\theorempostwork{}', sub {
    AssignRegister('\thm@postwork' => $_[1]); });
DefMacro('\theoremnumbering{}', sub {
    AssignRegister('\thm@numbering' => T_CS('\\' . ToString($_[1]))); });

# This should turn on the same parameters as the env,
# much like \theoremstyle{style} does, givcen a theoremstyle
DefPrimitive('\theoremclass{}', sub {
    return; });
#======================================================================
# ???
###DefMacro('\TheoremSymbol','');
# need some styling here to put at right...
DefMacro('\TheoremSymbol', '\@qedbox{\the\thm@symbol}');
DefConstructor('\@qedbox{}', "<ltx:text class='ltx_align_floatright'>#1</ltx:text>");
RawTeX('\newif\ifsetendmark\setendmarktrue');
DefMacro('\NoEndMark', '\global\setendmarkfalse');
DefMacroI('\thm@doendmark', undef, '\ifsetendmark\TheoremSymbol\fi');
DefRegister('\qedsymbol' => Tokens());
DefMacro('\qed', '\@qedbox{\the\qedsymbol}');

#======================================================================

Let('\orig@newtheorem', '\newtheorem');
# This defines BOTH thm & thm* envs, but note that the *'d form of theorem uses same counter!
DefMacro('\newtheorem OptionalMatch:* {}[]{}[]',
  '\orig@newtheorem#1{#2}[#3]{#4}[#5]'
    . '\ifx.#3.\orig@newtheorem#1{#2*}[#2]{#4}[#5]'
    . '\else\orig@newtheorem#1{#2*}[#3]{#4}[#5]\fi'
);

DefMacro('\Theoremname', '\lx@thistheorem');
Let('\renewtheorem', '\newtheorem');

#======================================================================
# do something about recording & adding frame
# This needs to tie into framed.sty!!!!
# Really should execute \theoremframecommand in isolation,
# then copy the relevant attributes (framing, spacing, color, ...) to the theorem!
### NEEDS TO integrate with framed.sty better
### AND in the meantime, this needs a better API
DefConstructorI('\lx@addframing', undef, sub {
    my ($doc, %props) = @_;
    my $node = $doc->getElement;
    $doc->setAttribute($node, framed => 'rectangle');
    my $css = $node->getAttribute('cssstyle');
    my $pad = 'padding:' . $props{margin} . 'pt;';
    $doc->setAttribute($node, cssstyle => ($css ? $css . ';' . $pad : $pad)); },
  properties => sub { (framecolor => Black,
      margin => LookupRegister('\FrameSep')->ptValue); });

# Since \theoremframecommand can be defined to be pretty much anything,
# we don't have a good handle on what colors, etc, it may use.
# Here we try something pretty bizarre:
#  We execute the theoremframecommand on some dummy text, capture the result,
# and copy the relevent attributes to the theorem.
DefMacroI('\lx@snapshot@framing', undef, '\lx@@snapshot@framing{\theoremframecommand{foo}}');
DefConstructor('\lx@@snapshot@framing{}', sub {
    my ($document, $framebox) = @_;
    my $theorem = $document->getElement;
    my $capture = $document->openElement('_Capture_');
    my ($frame, @rest) = $document->absorb($framebox);
    $document->closeElement('_Capture_');
    if (my $c = join(' ', grep { $_ } $theorem->getAttribute('class'), $frame->getAttribute('class'))) {
      $document->setAttribute($theorem, class => $c); }
    if (my $c = join(';', grep { $_ } $theorem->getAttribute('cssstyle'),
        $frame->getAttribute('cssstyle'))) {
      $document->setAttribute($theorem, cssstyle => $c); }
    if (my $b = $frame->getAttribute('backgroundcolor')
      || $document->getNodeFont($frame)->getBackground) {
      $document->setAttribute($theorem, backgroundcolor => $b); }
    foreach my $attr (qw(framed framecolor)) {
      if (my $v = $frame->getAttribute($attr)) {
        $document->setAttribute($theorem, $attr => $v); } }
    $document->removeNode($capture);
  },
  reversion => '');

DefMacro('\newframedtheorem{}[]{}[]',
  '\begingroup'
    . '\thm@styling{\lx@addframing}'
    . '\newtheorem{#1}[#2]{#3}[#4]'
    . '\endgroup');

DefMacro('\newshadedtheorem{}[]{}[]',
  '\begingroup'
    . '\ifx\theoremframecommand\relax'
    . '\def\theoremframecommand{\colorbox{shadecolor}}\fi'
    . '\thm@styling{\lx@snapshot@framing}'
    . '\newtheorem{#1}[#2]{#3}[#4]'
    . '\endgroup');

#======================================================================
DefPrimitive('\lx@ntheorem@newtheoremstyle{}{}{}{}{}{}', sub {
    my ($stomach, $name, $headfont, $bodyfont, $headstyle, $swap, $numbering) = @_;
    $name = ToString($name);
    saveTheoremStyle($name,
      '\thm@bodyfont'    => $bodyfont,
      '\thm@headfont'    => $headfont,
      '\thm@headstyling' => $headstyle,
      'thm@swap'         => ToString($swap) eq 'S',
      '\thm@numbering'   => $numbering,
      '\thm@symbol'      => LookupRegister('\thm@symbol'),
    );
    DefMacroI(T_CS('\th@' . $name), undef, sub { useTheoremStyle($name); });
    return; });

RawTeX(<<'EoTeX');
\lx@ntheorem@newtheoremstyle{plain}{\bfseries}{\itshape}{\lx@makerunin}{N}{\arabic}
\lx@ntheorem@newtheoremstyle{break}{\bfseries}{\slshape}{}{N}{\arabic}
\lx@ntheorem@newtheoremstyle{change}{\bfseries}{\slshape}{\lx@makerunin}{S}{\arabic}
\lx@ntheorem@newtheoremstyle{margin}{\bfseries}{\slshape}{\lx@makerunin\lx@makeoutdent}{S}{\arabic}
\lx@ntheorem@newtheoremstyle{marginbreak}{\bfseries}{\slshape}{\lx@makeoutdent}{S}{\arabic}
\lx@ntheorem@newtheoremstyle{changebreak}{\bfseries}{\slshape}{}{S}{\arabic}
\lx@ntheorem@newtheoremstyle{nonumberplain}{\bfseries}{\itshape}{\lx@makerunin}{N}{}
\lx@ntheorem@newtheoremstyle{nonumberbreak}{\bfseries}{\slshape}{}{N}{}
\lx@ntheorem@newtheoremstyle{empty}{}{}{\lx@makerunin}{N}{}
\lx@ntheorem@newtheoremstyle{emptybreak}{}{}{}{N}{}
EoTeX
# Load the other styles from the ntheorem's standard defintions

InputDefinitions('ntheorem.std') if LookupValue('thm@usestd');

# Start off as plain style.
useTheoremStyle('plain');
#======================================================================
# Lists of Theorems.

DefMacro('\addtheoremline OptionalMatch:* {}{}', '');
DefMacro('\addtotheoremfile[]{}',                '');

DefMacro('\theoremlisttype{}',            '');
DefMacro('\newtheoremlisttype{}{}{}{}',   '');
DefMacro('\renewtheoremlisttype{}{}{}{}', '');

# This is Wrong!
# It should list only specific subsets
# TOC needs to be generalized to allow more flexiblity, maybe XPath?
# Should accept comma separated list of theorem classes, including 'all'
# (theorem class being the 1st arg to \newtheorem)
DefConstructor('\listtheorems{}',
  "<ltx:TOC lists='#lists' name='#name'/>",
  properties => sub {
    my @types = split(/\s*,\s*/, ToString($_[1]));
    (lists => join(' ', map { ($_ eq 'all' ? 'thm' : 'theorem:' . $_) } @types));
  });

# \theoremlistdo,

# Presumably a user never needs to call these?
# [they implement the predefined theorem list types]
DefMacro('\theoremlistall',         '');
DefMacro('\theoremlistallname',     '');
DefMacro('\theoremlistalloptional', '');
DefMacro('\theoremlistalloptname',  '');

#======================================================================
# Add capability for greek numbering
DefMacro('\greek{}', sub {
    ExplodeText(radix_greek(CounterValue(ToString(Expand($_[1])))->valueOf)); });
DefMacro('\Greek{}', sub {
    ExplodeText(radix_Greek(CounterValue(ToString(Expand($_[1])))->valueOf)); });
#======================================================================

1;