%D \module
%D   [       file=lxml-ini,
%D        version=2007.08.17,
%D          title=\CONTEXT\ \XML\ Support,
%D       subtitle=Initialization,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

%D Todo: auto apply setups (manage at lua end)

\writestatus{loading}{ConTeXt XML Support / Initialization}

%registerctxluafile{lxml-tab}{1.001} % loader
%registerctxluafile{lxml-lpt}{1.001} % parser
%registerctxluafile{lxml-xml}{1.001} % xml finalizers
%registerctxluafile{lxml-aux}{1.001} % extras using parser
%registerctxluafile{lxml-mis}{1.001} % extras independent of parser
\registerctxluafile{char-ent}{1.001}
\registerctxluafile{lxml-ent}{1.001} % entity hacks
\registerctxluafile{lxml-tex}{1.001} % tex finalizers
\registerctxluafile{lxml-dir}{1.001} % ctx hacks

\unprotect % todo \!!bs \!!es where handy (slower)

\def\ctxlxml               #1{\ctxlua{lxml.#1}}

\def\xmlmain               #1{\ctxlxml{main("#1")}}
\def\xmlmatch              #1{\ctxlxml{match("#1")}}
\def\xmlall              #1#2{\ctxlxml{all("#1","#2")}}
\def\xmlatt              #1#2{\ctxlxml{att("#1","#2")}}
\def\xmlattdef         #1#2#3{\ctxlxml{att("#1","#2","#3")}}
\def\xmlchainatt         #1#2{\ctxlxml{chainattribute("#1","/","#2")}}
\def\xmlchainattdef    #1#2#3{\ctxlxml{chainattribute("#1","/","#2","#3")}}
\def\xmlattribute      #1#2#3{\ctxlxml{attribute("#1","#2","#3")}}
\def\xmlattributedef #1#2#3#4{\ctxlxml{attribute("#1","#2","#3","#4")}}
\def\xmlcommand        #1#2#3{\ctxlxml{command("#1","#2","#3")}}
\def\xmlconcat         #1#2#3{\ctxlxml{concat("#1","#2",[[\detokenize{#3}]])}}
\def\xmlconcatrange#1#2#3#4#5{\ctxlxml{concatrange("#1","#2","#3","#4",[[\detokenize{#5}]])}}
\def\xmlcount            #1#2{\ctxlxml{count("#1","#2")}}
\def\xmldelete           #1#2{\ctxlxml{delete("#1","#2")}}
\def\xmldirectives         #1{\ctxlxml{directives.setup("#1")}}
\def\xmldirectivesbefore   #1{\ctxlxml{directives.before("#1")}}
\def\xmldirectivesafter    #1{\ctxlxml{directives.after("#1")}}
\def\xmlfilter           #1#2{\ctxlxml{filter("#1",\!!bs#2\!!es)}}
\def\xmlfilterlist       #1#2{\ctxlxml{filterlist("#1",\!!bs#2\!!es)}}
\def\xmlfunction         #1#2{\ctxlxml{applyfunction("#1",\!!bs#2\!!es)}}
\def\xmlfirst            #1#2{\ctxlxml{first("#1","#2")}}
\def\xmlflush              #1{\ctxlxml{flush("#1")}}
\def\xmlflushlinewise      #1{\ctxlxml{flushlinewise("#1")}}
\def\xmlflushspacewise     #1{\ctxlxml{flushspacewise("#1")}}
%def\xmlcontent            #1{\ctxlxml{content("#1")}}
%def\xmlflushstripped      #1{\ctxlxml{strip("#1",true)}}
\def\xmldirect             #1{\ctxlxml{direct("#1")}} % in loops, not dt but root
\def\xmlidx            #1#2#3{\ctxlxml{idx("#1","#2",\number#3)}}
\def\xmlinclude        #1#2#3{\ctxlxml{include("#1","#2","#3",true)}}
\def\xmlindex          #1#2#3{\ctxlxml{index("#1","#2",\number#3)}}
\def\xmlinfo               #1{\hbox{\ttxx[\ctxlxml{info("#1")}]}}
\def\xmlshow               #1{\startpacked\ttx\xmlverbatim{#1}\stoppacked}
\def\xmllast             #1#2{\ctxlxml{last("#1","#2")}}
\def\xmlname               #1{\ctxlxml{name("#1")}}
\def\xmlnamespace          #1{\ctxlxml{namespace("#1")}}
\def\xmlnonspace         #1#2{\ctxlxml{nonspace("#1","#2")}}
\def\xmlraw              #1#2{\ctxlxml{raw("#1","#2")}}
\def\xmlcontext          #1#2{\ctxlxml{context("#1","#2")}}
\def\xmlflushcontext       #1{\ctxlxml{context("#1")}}
\def\xmlsnippet          #1#2{\ctxlxml{snippet("#1",#2)}}
\def\xmlelement          #1#2{\ctxlxml{element("#1",#2)}}
\def\xmlregisterns       #1#2{\ctxlua{xml.registerns("#1","#2")}}                    % document
\def\xmlremapname    #1#2#3#4{\ctxlua{xml.remapname(lxml.id("#1"),"#2","#3","#4")}}  % element
\def\xmlremapnamespace #1#2#3{\ctxlua{xml.renamespace(lxml.id("#1"),"#2","#3")}}    % document
\def\xmlchecknamespace #1#2#3{\ctxlua{xml.checknamespace(lxml.id("#1"),"#2","#3")}} % element
\def\xmlsetfunction    #1#2#3{\ctxlxml{setaction("#1","#2",#3)}}
\def\xmlsetsetup       #1#2#3{\ctxlxml{setsetup("#1","#2","#3")}}
\def\xmlstrip            #1#2{\ctxlxml{strip("#1","#2")}}
\def\xmlstripnolines     #1#2{\ctxlxml{strip("#1","#2",true)}}
\def\xmlstripanywhere    #1#2{\ctxlxml{strip("#1","#2",true,true)}}
\def\xmlstripped         #1#2{\ctxlxml{stripped("#1","#2")}}
\def\xmlstrippednolines  #1#2{\ctxlxml{stripped("#1","#2",true)}}
\def\xmltag                #1{\ctxlxml{tag("#1")}}
\def\xmltext             #1#2{\ctxlxml{text("#1","#2")}}
\def\xmlverbatim           #1{\ctxlxml{verbatim("#1")}}
\def\xmldisplayverbatim    #1{\ctxlxml{displayverbatim("#1")}}
\def\xmlinlineverbatim     #1{\ctxlxml{inlineverbatim("#1")}}

\def\xmlload             #1#2{\ctxlxml{load("#1","#2","\p_lxml_entities","\p_lxml_compress")}}
\def\xmlloadbuffer       #1#2{\ctxlxml{loadbuffer("#1","#2","\p_lxml_entities","\p_lxml_compress")}}
\def\xmlloaddata         #1#2{\ctxlxml{loaddata("#1",\!!bs#2\!!es,"\p_lxml_entities","\p_lxml_compress")}}
\def\xmlloadregistered   #1#2{\ctxlxml{loadregistered("#1","\p_lxml_entities","\p_lxml_compress")}}
\def\xmlloaddirectives     #1{\ctxlxml{directives.load("any:///#1")}}
\def\xmlpos                #1{\ctxlxml{pos("#1")}}

\def\xmltoparameters       #1{\ctxlxml{toparameters("#1")}}

\def\xmltofile         #1#2#3{\ctxlxml{tofile("#1","#2","#3")}} % id pattern filename

% kind of special:

\def\xmlstartraw{\ctxlxml{startraw()}}
\def\xmlstopraw {\ctxlxml{stopraw()}}

% todo: \xmldoifelseattribute

\def\xmldoif             #1#2{\ctxlxml{doif        (\!!bs#1\!!es,\!!bs#2\!!es)}} % expandable
\def\xmldoifnot          #1#2{\ctxlxml{doifnot     (\!!bs#1\!!es,\!!bs#2\!!es)}} % expandable
\def\xmldoifelse         #1#2{\ctxlxml{doifelse    (\!!bs#1\!!es,\!!bs#2\!!es)}} % expandable
\def\xmldoiftext         #1#2{\ctxlxml{doiftext    (\!!bs#1\!!es,\!!bs#2\!!es)}} % expandable
\def\xmldoifnottext      #1#2{\ctxlxml{doifnottext (\!!bs#1\!!es,\!!bs#2\!!es)}} % expandable
\def\xmldoifelsetext     #1#2{\ctxlxml{doifelsetext(\!!bs#1\!!es,\!!bs#2\!!es)}} % expandable

%def\xmldoifelseempty    #1#2{\ctxlxml{doifelseempty("#1","#2")}} % #2, "*" or "" == self not yet implemented
%def\xmldoifelseselfempty  #1{\ctxlxml{doifelseempty("#1")}}

% \startxmlsetups xml:include
%     \xmlinclude{main}{include}{filename|href}
% \stopxmlsetups
%
% \xmlprependsetup{xml:include}

\let\xmlgrab\xmlsetsetup % obsolete
\let\xmlself\s!unknown   % obsolete

\def\xmlsetup#1#2{\setupwithargument{#2}{#1}}

\let\xmls\xmlsetup
\let\xmlw\setupwithargument

\newtoks \registeredxmlsetups

% todo: 1:xml:whatever always before 3:xml:something

\unexpanded\def\xmlprependsetup           #1{\ctxlxml{installsetup(1,"*","#1")}}
\unexpanded\def\xmlappendsetup            #1{\ctxlxml{installsetup(2,"*","#1")}}
\unexpanded\def\xmlbeforesetup          #1#2{\ctxlxml{installsetup(3,"*","#1","#2")}}
\unexpanded\def\xmlaftersetup           #1#2{\ctxlxml{installsetup(4,"*","#1","#2")}}

\unexpanded\def\xmlprependdocumentsetup #1#2{\ctxlxml{installsetup(1,"#1","#2")}}
\unexpanded\def\xmlappenddocumentsetup  #1#2{\ctxlxml{installsetup(2,"#1","#2")}}
\unexpanded\def\xmlbeforedocumentsetup#1#2#3{\ctxlxml{installsetup(3,"#1","#2","#3")}}
\unexpanded\def\xmlafterdocumentsetup #1#2#3{\ctxlxml{installsetup(4,"#1","#2","#3")}}

\unexpanded\def\xmlremovesetup            #1{\ctxlxml{removesetup("*","#1")}}
\unexpanded\def\xmlremovedocumentsetup  #1#2{\ctxlxml{removesetup("#1","#2")}}

\unexpanded\def\xmlflushdocumentsetups  #1#2{\ctxlxml{flushsetups("#1","*","#2")}} % #1 == id where to apply * and #2
\unexpanded\def\xmlresetdocumentsetups    #1{\ctxlxml{resetsetups("#1")}}

\let\xmlregistersetup        \xmlappendsetup
\let\xmlregisterdocumentsetup\xmlappenddocumentsetup

\def\xmldocument{main}

\unexpanded\def\xmlregisteredsetups
  {\xmlstarttiming
   \xmlflushsetups
   \xmldefaulttotext\xmldocument % after include
   \xmlstoptiming}

\unexpanded\def\xmlregistereddocumentsetups#1#2% id setups
  {\xmlstarttiming
   % todo: test for duplicates !
   \xmlflushdocumentsetups{#1}{#2}%
   \xmldefaulttotext{#1}% after include
   \xmlstoptiming}

\unexpanded\def\xmlstarttiming{\ctxlua{statistics.starttiming(lxml)}}
\unexpanded\def\xmlstoptiming {\ctxlua{statistics.stoptiming (lxml)}}

\def\lxml_process#1#2#3#4#5% flag \loader id name what initializersetup
  {\begingroup
   \edef\xmldocument{#3}% #2 can be \xmldocument and set as such
   %xmlpushdocument{#3}%
   #2{#3}{#4}%
   \setcatcodetable\notcatcodes
   \doifelsenothing{#5}
     {\xmlsetup{#3}{xml:process}}
     {\xmlsetup{#3}{#5}}%
   %xmlpopdocument
   \endgroup}

\unexpanded\def\xmlprocessfile      {\lxml_process\plusone  \xmlload}
\unexpanded\def\xmlprocessdata      {\lxml_process\zerocount\xmlloaddata}
\unexpanded\def\xmlprocessbuffer    {\lxml_process\zerocount\xmlloadbuffer}
\unexpanded\def\xmlprocessregistered{\lxml_process\zerocount\xmlloadregistered}
           \let\xmlprocess           \xmlprocessfile

\startxmlsetups xml:flush
    \xmlflush{#1}
\stopxmlsetups

\startxmlsetups xml:process
    \xmlregistereddocumentsetups{#1}{#1}
    \xmlmain{#1}
\stopxmlsetups

\unexpanded\def\xmlloadonly#1#2#3%
  {\xmlload{#1}{#2}%
   \xmlregistereddocumentsetups{#1}{#3}}

% beware: \xmlmain takes the real root, so also processing
% instructions preceding the root element; well, in some
% sense that is the root

\unexpanded\def\xmlconnect#1#2#3% inefficient
  {\scratchcounter\xmlcount{#1}{#2}\relax
   \ifcase\scratchcounter \or
     \xmlall{#1}{#2}%
   \else
     \dorecurse \scratchcounter
       {\ifnum\recurselevel>\plusone#3\fi
        \xmlidx{#1}{#2}\recurselevel}%
   \fi}

\unexpanded\def\xmlcdataobeyedline {\obeyedline}
\unexpanded\def\xmlcdataobeyedspace{\strut\obeyedspace}
\unexpanded\def\xmlcdatabefore     {\bgroup\tt}
\unexpanded\def\xmlcdataafter      {\egroup}

% verbatim (dodo:pre/post whitespace, maybe splot verbatim and
% cdata commands), experimental:
%
% \xmlsetfunction{main}{verbatim}{lxml.displayverbatim}
% \xmlsetfunction{main}{verb}    {lxml.inlineverbatim}

% \unexpanded\def\startxmldisplayverbatim[#1]{}
% \unexpanded\def\stopxmldisplayverbatim     {}
% \unexpanded\def\startxmlinlineverbatim [#1]{}
% \unexpanded\def\stopxmlinlineverbatim      {}

% we use an xml: namespace so one has to define a suitable verbatim, say
%
% \definetyping[xml:verbatim][typing]
%
% this is experimental!

\unexpanded\def\startxmldisplayverbatim[#1]%
  {\startpacked % \begingroup
   \edef\currenttyping{xml:#1}%
   \unexpanded\def\stopxmldisplayverbatim
     {\endofverbatimlines
      \stoppacked} % \endgroup
   \doinitializeverbatim
   \beginofverbatimlines}

\unexpanded\def\startxmlinlineverbatim[#1]%
  {\begingroup
   \edef\currenttype{xml:#1}%
   \let\stopxmldisplayverbatim\endgroup
   \doinitializeverbatim}

% will move but is developed for xml

\newtoks \collectingtoks

\unexpanded\def\startcollect#1\stopcollect
  {\collectingtoks\@EA{\the\collectingtoks#1}}

\unexpanded\def\startexpandedcollect#1\stopexpandedcollect
  {\normalexpanded{\collectingtoks{\the\collectingtoks#1}}}

\unexpanded\def\startcollecting{\collectingtoks\emptytoks}
\unexpanded\def\stopcollecting {\the\collectingtoks}

\def\inlinemessage #1{\dontleavehmode{\tttf#1}}
\def\displaymessage#1{\blank\inlinemessage{#1}\blank}

% \def\xmltraceentities % settextcleanup is not defined
%   {\ctxlua{xml.settextcleanup(lxml.trace_text_entities)}%
%    \appendtoks\ctxlxml{showtextentities()}\to\everygoodbye}

% processing instructions

\def\xmlcontextdirective#1% kind class key value
  {\executeifdefined{xml#1directive}\gobblethreearguments}

% setting up xml:
%
% \setupxml[\c!default=]        % mkiv only == text
% \setupxml[\c!default=\v!none] % mkiv only, undefined -> hidden
% \setupxml[\c!default=\v!text] % mkiv only, undefined -> text

% \def\xmlctxdirective#1#2#3{\doif{#1}{clue}{\doif{#2}{page}}{\page[#3]}}

\newconstant\xmlprocessingmode % 0=unset, 1=text, 2=hidden

\installcorenamespace{xml}
\installcorenamespace{xmldefaults}
\installcorenamespace{xmlmapvalue}

\installdirectcommandhandler \??xml {xml}

\letvalue{\??xmldefaults\v!normal}\zerocount
\letvalue{\??xmldefaults\v!none  }\zerocount
\letvalue{\??xmldefaults\v!text  }\plusone
\letvalue{\??xmldefaults\v!hidden}\plustwo

\unexpanded\def\xmldefaulttotext#1%
  {\ifcase\xmlprocessingmode
     % unset
   \or
     \ctxlxml{setcommandtotext("#1")}% 1
   \or
     \ctxlxml{setcommandtonone("#1")}% 2
   \else
     % unset
   \fi}

\appendtoks
    \xmlprocessingmode\executeifdefined{\??xmldefaults\directxmlparameter\c!default}\plusone
\to \everysetupxml

\unexpanded\def\initializexmlprocessing % is this still needed?
  {\the\everysetupxml}

\let\p_lxml_entities\empty
\let\p_lxml_compress\empty

\appendtoks
    \edef\p_lxml_entities{\directxmlparameter\c!entities}%
    \edef\p_lxml_compress{\directxmlparameter\c!compress}%
\to \everysetupxml

\setupxml
  [\c!default=,          % flush all
   \c!compress=\v!no,    % strip comment
   \c!entities=\v!yes]   % replace entities

\def\xmlmapvalue    #1#2#3{\setvalue{\??xmlmapvalue#1:#2}{#3}} % keep #3 to grab spaces
\def\xmlvalue       #1#2#3{\executeifdefined{\??xmlmapvalue#1:#2}{#3}}
%def\xmlvalue         #1#2{\ifcsname\??xmlmapvalue#1:#2\endcsname\csname\??xmlmapvalue#1:#2\expandafter\expandafter\gobbleoneargument\expandafter\endcsname\else\expandafter\firstofoneargument\fi}
\def\xmldoifelsevalue #1#2{\ifcsname\??xmlmapvalue#1:#2\endcsname\expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi}

\let\xmlmapval\xmlmapvalue
\let\xmlval   \xmlvalue

%D Experimental:

\def\xmlgetindex    #1{\ctxlxml{getindex("\xmldocument","#1")}}
\def\xmlrawindex    #1{\ctxlxml{rawindex("#1")}}
\def\xmlwithindex #1#2{\ctxlxml{withindex("\xmldocument","#1","#2")}}
\def\xmlreference #1#2{\string\xmlwithindex{#1}{#2}}

%D Entities (might change):

\setnewconstant\xmlautoentities\plusone % 0=off, 1=upper, 2=upper,lower

\def\xmlsetentity#1#2{\ctxlua{xml.registerentity('#1',\!!bs\detokenize{#2}\!!es)}}
\def\xmltexentity#1#2{\ctxlua{lxml.registerentity('#1',\!!bs\detokenize{#2}\!!es)}}

% \xmlsetentity{tex}{\TEX{}} % {} needed

\unexpanded\def\xmle
  {\ifcase\xmlautoentities
     \expandafter\lxml_e_none
   \or
     \expandafter\lxml_e_upper
   \or
     \expandafter\lxml_e_upperlower
   \else
     \expandafter\lxml_e_none
   \fi}

\def\lxml_e_none#1#2% safe
  {#1}

\def\lxml_e_upper#1#2% can be abbreviation
  {\ifcsname#2\endcsname
     \csname#2\expandafter\endcsname
   \else
     #1%
   \fi}

\def\lxml_e_upperlower#1#2% can be anything, so unsafe
  {\ifcsname#2\endcsname
     \csname#2\expandafter\endcsname
   \else\ifcsname#1\endcsname
     \csname#1\expandafter\expandafter\expandafter\endcsname
   \else
     #1%
   \fi\fi}

% handy helpers (analogue to MP and LUA and TEX and also MkII)

\unexpanded\def\processXMLbuffer
  {\dosingleempty\lxml_process_buffer}

\def\lxml_process_buffer[#1]%
  {\xmlprocessbuffer{temp}{#1}{}}

\unexpanded\def\processXMLfile#1%
  {\xmlprocessfile{temp}{#1}{}}

\unexpanded\def\XMLdata#1%
  {\xmlprocessdata{temp}{#1}{}}

\let\processxmlbuffer\processXMLbuffer
\let\processxmlfile  \processXMLfile
\let\xmldata         \XMLdata

\protect \endinput

% \newcount\charactersactiveoffset \charactersactiveoffset="10000
%
% \startextendcatcodetable\ctxcatcodes
%     \catcode\numexpr\charactersactiveoffset+`<\relax=13
%     \catcode\numexpr\charactersactiveoffset+`&\relax=13
%     \catcode\numexpr\charactersactiveoffset+`>\relax=13
% \stopextendcatcodetable
%
% \startextendcatcodetable\xmlcatcodes % not needed
%     \catcode\numexpr\charactersactiveoffset+`<\relax=13
%     \catcode\numexpr\charactersactiveoffset+`&\relax=13
%     \catcode\numexpr\charactersactiveoffset+`>\relax=13
% \stopextendcatcodetable
%
% \ctxlua { % entities are remembered in the format
%     commands.remapentity("<",characters.activeoffset + utf.byte("<"))
%     commands.remapentity("&",characters.activeoffset + utf.byte("&"))
%     commands.remapentity(">",characters.activeoffset + utf.byte(">"))
% }