# -*- CPERL -*-
# /=====================================================================\ #
# |  latexml.ltxml                                                      | #
# | Style file for latexml documents                                    | #
# |=====================================================================| #
# | 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=/ #

#======================================================================
# LaTeXML Implementation of latexml semantic enhancement macros
#======================================================================
package LaTeXML::Package::Pool;
use LaTeXML::Package;
use LaTeXML::Util::KeyVal;
no warnings 'redefine';

DefMacro('\lxDocumentID{}','\def\thedocument@ID{#1}');

# From latexml.sty
DefConstructor('\URL[] Semiverbatim',"<ltx:ref href='#2'>?#1(#1)(#2)</ltx:ref>");
DefMacro('\XML',     '\URL[\texttt{XML}]{http://www.w3.org/XML/}');
DefMacro('\SGML',    '\URL[\texttt{HTML}]{http://www.w3.org/MarkUp/SGML/}');
DefMacro('\HTML',    '\URL[\texttt{HTML}]{http://www.w3.org/HTML/}');
DefMacro('\XHTML',   '\URL[\texttt{XHTML}]{http://www.w3.org/XHTML/}');
DefMacro('\XSLT',    '\URL[\texttt{XSLT}]{http://www.w3.org/Style/XSL/}');
DefMacro('\CSS',     '\URL[\texttt{CSS}]{http://www.w3.org/Style/CSS/}');
DefMacro('\MathML',  '\URL[\texttt{MathML}]{http://www.w3.org/Math/}');
DefMacro('\OpenMath','\URL[\texttt{OpenMath}]{http://www.openmath.org/}');
DefMacro('\LaTeXML', '\URL[\texttt{LaTeXML}]{http://dlmf.nist.gov/LaTeXML/}');

#======================================================================
# Exposing internal functionality of LaTeXML to the LaTeX document.
#======================================================================

DefConstructor('\lxAddClass{}',"^ class='#1'");

#======================================================================
# \LXMID{id}{math}  Associate the identifier id with the given math expression.
DefMacro('\LXMID{}{}','\@XMArg{#1}{#2}');

# \LXRef{id} Refer to the math expression associated with id.
# In presentation, this is similar to using a shorthand macro.
# In content situations, an XMRef is generated.
DefMacro('\LXMRef{}','\@XMRef{#1}');

# \LxFcn{f} treat f as a function here (only).
DefConstructor('\lxFcn{}',"<ltx:XMWrap role='FUNCTION'>#1</ltx:XMWrap>", 
	       requireMath=>1, reversion=>'#1', alias=>'');
DefConstructor('\lxID{}',  "<ltx:XMWrap role='ID'>#1</ltx:XMWrap>", 
	       requireMath=>1, reversion=>'#1', alias=>'');
DefConstructor('\lxPunct{}',"<ltx:XMWrap role='PUNCT'>#1</ltx:XMWrap>", 
	       requireMath=>1, reversion=>'#1', alias=>'');

#======================================================================
# Define a mathematical object with both presentation & content information
#   \lxDefMath{\name}[nargs][optional]{presentation body}[content keywords]
#  The first part is essentially equivalent to \newcommand, it defines
# an expansion for \name used for the presentation.
# The content keywords are used to define the semantics of the object.
# See DefMath in LaTeXML::Package for more information.
DefPrimitive('\lxDefMath{}[][]{} OptionalKeyVals:XMath', sub {
  my($stomach,$cs,$nargs,$opt,$presentation,$params)=@_;
  my($name,$meaning,$cd,$role)= 
    $params && map($_ && ToString($_),map($params->getValue($_),
             qw(name meaning cd role)));
  DefMathI($cs,convertLaTeXArgs($nargs,$opt),$presentation,
	  name=>$name, meaning=>$meaning, omcd=>$cd, role=>$role);
});
#DefKeyVal('XMath',name,string);

#======================================================================
# NOTE: I'm concerned about the order of applying these filters.
# even though it seems right so far.

# Keyword options:
#    scope=<scope> : Specifies the scope of the declaration, ie. to what portion of
#            the document the declarations apply
#            You can specify one of the counters associated with sections, 
#            equations, etc.  
#            If unspecified, the declaration is scoped to the current unit.
#            Note that this applies to equations, as well.
#    label=<label> : assigns a label to the declaration so that it can be reused
#            at another point in the document (with \lxRefDeclaration), particularly when
#            that point is not otherwise within the scope of the original declaration.
# To effect the declaration:
#    role=<role>  : Assigns a grammatical role to the matched item for parsing.
#    name=<name>  : Assigns a name to the matched item.
#    meaning=<meaning>  : Assigns a semantic name to the matched item.
#  Alternatively, use
#     replace : provides a replacement for the matched expression, rather than adding attributes.

# Potential keywords/operations needed(?)
#   nodef : inhibits the marking of the current point as the `definition' of the expression.
#          (a ref declaration would normally not be a def anyway)

DefKeyVal('Declare','wrap','{}',1);
DefKeyVal('Declare','trace','{}',1);
DefKeyVal('Declare','replace','UndigestedKey');

our $declare_keys = {scope=>1,role=>1,tag=>1,name=>1,meaning=>1,
		     trace=>1,wrap=>1,replace=>1,label=>1};
DefConstructor('\lxDeclare OptionalMatch:* OptionalKeyVals:Declare {}',
##	       "^<ltx:declare id='#id'>?&KeyVal(#2,tag)(<ltx:tag>&KeyVal(#2,tag)</ltx:tag>)</ltx:declare>",
	       sub { my($document,$flag,$kv,$pattern,%props)=@_;
		     if(my $id = $props{id}){
		       my $save = $document->floatToElement('ltx:declare');
		       $document->openElement('ltx:declare','xml:id'=>$id);
		       if(my $tag = $kv->getValue('tag')){
			 $document->insertElement('ltx:tag',$tag); }
		       $document->closeElement('ltx:declare');
		       $document->setNode($save); }},
	       mode=>'text',
	       # Screwy bit to `neutralize' the font in the matches.
	       beforeDigest=>sub { AssignValue(font=>LaTeXML::Font->default(),'local');
				   AssignValue(mathfont=>LaTeXML::MathFont->default(), 'local');
				   return; },
	       afterDigest=>sub { my($stomach,$whatsit)=@_;
				  my($star,$keys,$pattern)=$whatsit->getArgs;
				  return unless $keys;
				  CheckOptions("\\lxDeclare keys",$declare_keys,%{$keys->getKeyVals});
				  foreach my $key (qw(role tag name meaning replace)){
				    if(my $value = $keys->getValue($key)){
				      Warn(":unexpected:$key Repeated $key: ".join('; ',map(Stringify($_),@$value)))
					if ref $value eq 'ARRAY'; }}
				  my $id = ($keys->getValue('tag') ? next_declaration_id() : undef);
				  if($id && LookupValue('InPreamble')){
				    Warn(":unexpected:tag Declaration with tag cannot appear in preamble"
					 .Stringify($whatsit)); }
				  $whatsit->setProperties(scope   => getDeclarationScope($keys),
							  role    => ToString($keys->getValue('role')),
							  name    => ToString($keys->getValue('name')),
							  meaning => ToString($keys->getValue('meaning')),
							  trace   => defined $keys->getValue('trace'),
							  wrap    => defined $keys->getValue('wrap'),
							  id      => $id,
							  match   => $pattern,
							  replace => $keys->getValue('replace'));
				  if(my $label = ToString($keys->getValue('label'))){
				    PushValue("Declaration_$label",$whatsit); }
				  return; },
	       afterConstruct=>sub { my($document,$whatsit)=@_;
				     my $scope = $whatsit->getProperty('scope');
				     createDeclarationRewrite($document,$scope,$whatsit); },
	       properties=>{alignmentSkippable=>1},
	       reversion=>'');

DefConstructor('\lxRefDeclaration OptionalKeyVals:Declare {}','',
	       afterDigest=>sub { my($stomach,$whatsit)=@_;
				  my($keys,$labels)=$whatsit->getArgs;
				  $whatsit->setProperties(scope=>getDeclarationScope($keys),
							  labels=>[split(',',ToString($labels))]); },
	       afterConstruct=>sub { my($document,$whatsit)=@_;
				     my $scope  = $whatsit->getProperty('scope');
				     foreach my $label (@{$whatsit->getProperty('labels')}){
				       if(my $declaration = LookupValue("Declaration_$label")){
					 map(createDeclarationRewrite($document,$scope,$_),
					     @$declaration); }
				       else {
					 Warn(":unexpected:$label No Declaration with label=$label was found"); }}},
	       properties=>{alignmentSkippable=>1},
	       reversion=>'');

our $XMDeclID=0;
sub next_declaration_id {
##  ToString(LookupValue('Chapter')).":LXDeclID".$XMDeclID++; }

##  my $docid = LookupValue('DOCUMENTID');
##  ($docid ? "$docid.XMD" : 'XMD').++$XMDeclID; }

  StepCounter('@XMDECL');
  DefMacroI(T_CS('\@@XMDECL@ID'),undef,
	    Tokens(Explode(LookupValue('\c@@XMDECL')->valueOf)),
	    scope=>'global');
  ToString(Expand(T_CS('\the@XMDECL@ID'))); }

sub getDeclarationScope {
  my($keys)=@_;
  # Sort out the scope.
  my $scope = $keys && $keys->getValue('scope');
  $scope = ($scope ? ToString($scope) : LookupValue('current_counter'));
  if($scope && LookupValue("\\c\@$scope")){ # Scope is some counter.
    $scope =  "id:".ToString(Digest(Expand(T_CS("\\the$scope\@ID")))); }
  $scope; }


sub createDeclarationRewrite {
  my($document,$scope,$whatsit)=@_;
  my %props = $whatsit->getProperties;
  my($id,$match,$wrap,$role,$name,$meaning,$ref,$trace,$replace)
    =map($props{$_}, qw(id match wrap role name meaning ref trace replace));
###  print STDERR "REWRITER: scope=>$scope, match=>$match, role=>$role, dec_id=>$id\n";
  $STATE->getModel->prependRewriteRule('math',
				       ($trace ? (trace=>$trace):()),
				       ($scope ? (scope=>$scope):()),
				       ($match ? (match=>$match):()),
				       ($wrap  ? (wrap =>$wrap) :()),
				       ($replace
					? (replace=>$replace)
					: attributes=>{ ($role  ? (role=>$role) :()),
							($name  ? (name=>$name) :()),
							($meaning ? (meaning=>$meaning) :()),
							($id    ? (dec_id=>$id) :()),
						     }),
				       );
  return; }

#======================================================================
1;
