%% LaTeX package xcntperchap - version 0.4 (2017/04/30 -- 00:53:36)
%% Source file for xcntperchap.sty
%%
%%
%% -------------------------------------------------------------------------------------------
%% Copyright (c) 2016 -- 2017 by Dr. Christian Hupfer <typography dot with dot latex at gmail dot com>
%% -------------------------------------------------------------------------------------------
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public License, either version 1.3
%% of this license or (at your option) any later version.
%% The latest version of this license is in
%%   http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 2005/12/01 or later.
%%
%%
%% This work has the LPPL maintenance status `author-maintained`
%%
%%

\def\xcntperchappackageversion{0.4}
\NeedsTeXFormat{LaTeX2e}

\ProvidesPackage{xcntperchap}[2017/04/30 - v\xcntperchappackageversion]

\RequirePackage[counter]{zref}
\RequirePackage{l3keys2e}
\RequirePackage{xparse}
\RequirePackage{xassoccnt}


\ExplSyntaxOn


%\let\latex@@stepcounter\stepcounter
%\let\latex@@addtocounter\addtocounter

\cs_set_eq:NN \latex@@stepcounter \stepcounter
\cs_set_eq:NN \latex@@addtocounter \addtocounter


\cs_generate_variant:Nn \int_set:Nn {NV, Nx}
\cs_generate_variant:Nn \int_eval:n {V, x}
\cs_generate_variant:Nn \seq_gremove_all:Nn {NV,Nx,cV,cx}


\seq_new:N \g_xcntperchap_tracklevel_seq % This holds all counters which serve as a tracklevel


\ior_new:N \g_trackfile_storage_read
\iow_new:N \g_trackfile_storage_write


\int_new:N \l_tracklevelunit_int
\int_new:N \l_tracklevel_tracked_int


\cs_new:Nn \xcntperchap_add_tracklevel:n {% 
  \seq_if_in:NnF \g_xcntperchap_tracklevel_seq {#1} {%
    \seq_gput_right:Nn \g_xcntperchap_tracklevel_seq {#1}
    \seq_new:c {g_xcntperchap_tracklevel_#1_seq}% Creating a new container for the counter names to be tracked
    \prop_new:c {g_xcntperchap_tracklevel_#1_prop}% Creating a new property list for the counter names to be tracked
    \seq_new:c {g_xcntperchap_tracklevelstored_#1_seq}% Creating a new container for the stored counter values (used later on)
    \IfIsDocumentCounterF{cps@@#1trackleveltotal}{%
      \NewDocumentCounter{cps@@#1trackleveltotal}%
    }
    \DeclareAssociatedCounters{#1}{cps@@#1trackleveltotal}
    \zref@newprop{cps#1total}{\number\value{cps@@#1trackleveltotal}}
  }%
}



\NewDocumentCommand{\RegisterMultipleTrackCounters}{mm}{%
  \clist_set:Nn \l_tmpa_clist {#1}%
  \clist_map_inline:Nn \l_tmpa_clist {%
    \RegisterTrackCounter{##1}{#2}%
  }
}


\NewDocumentCommand{\RegisterTrackCounter}{mm}{%
  \xcntperchap_add_tracklevel:n {#1}
  \seq_set_from_clist:cn {g_xcntperchap_tracklevel_#1_seq} {#2}
  \seq_remove_duplicates:c {g_xcntperchap_tracklevel_#1_seq}
  \int_zero:N \l_tmpa_int
  \seq_map_inline:cn {g_xcntperchap_tracklevel_#1_seq} {%
    \int_incr:N \l_tmpa_int
    \IfIsDocumentCounterF{cps@@#1total##1}{%
      \NewDocumentCounter{cps@@#1total##1}%
      \AddAssociatedCounters{##1}{cps@@#1total##1}% Define associated counters 
    }
    \prop_put:cnV {g_xcntperchap_tracklevel_#1_prop} {##1} \l_tmpa_int % New Version 0.2
  }  
}



\cs_new:Nn \xcntperchap_storetracked_values:n{%
  \group_begin:
  \tl_clear:N \l_tmpa_tl
  \int_zero:N \l_tmpa_int
  \int_zero:N \l_tmpb_int
  \seq_if_in:NnT \g_xcntperchap_tracklevel_seq {#1} {%
    \tl_put_right:Nn \l_tmpa_tl {#1,}%
    \int_set:Nx \l_tmpb_int {\seq_count:c {g_xcntperchap_tracklevel_#1_seq} }
    \int_compare:nNnT { \value{#1} } > {\c_zero }{%
      \int_zero:N \l_tmpa_int
      \seq_map_inline:cn {g_xcntperchap_tracklevel_#1_seq} {%
        \int_incr:N \l_tmpa_int
        \int_compare:nNnTF {\l_tmpa_int } < { \l_tmpb_int } 
        {  \tl_gput_right:Nx \l_tmpa_tl { \number\value{cps@@#1total##1}, } } {  \tl_gput_right:Nx \l_tmpa_tl { \number\value{cps@@#1total##1} } }
      } 
      \iow_now:Nx \g_trackfile_storage_write {\l_tmpa_tl}%
    }
    \seq_map_inline:cn {g_xcntperchap_tracklevel_#1_seq} {
      \setcounter{cps@@#1total##1}{\c_zero}% Reset the total counter values
    }
  }
  \group_end:
}


\NewDocumentCommand{\StoreCounterValues}{m}{%
  \xcntperchap_storetracked_values:n{#1}%
}


% Redefine again, to provide the storage feature

\cs_set:Npn \xcntperchap_internal_stepcounter #1 {%
%\RenewDocumentCommand{\stepcounter}{m}{%
  % Must get reset list first
  \__xassoccnt_getresetlist:n{#1}% 
  \seq_map_inline:Nn \g_xcntperchap_tracklevel_seq {%
     \seq_if_in:NnT \l__xassoccnt_counterreset_seq {##1} {%
     \StoreCounterValues{##1}%
   }
  }
  \StoreCounterValues{#1}%
  \latex@@stepcounter{#1}%
}

\let\foo\stepcounter

\RenewDocumentCommand{\stepcounter}{m}{%
  \xcntperchap_internal_stepcounter{#1}%
}

\NewDocumentCommand{\AddToTrackedCounters}{mm}{%
  \seq_set_from_clist:Nn \l_tmpa_seq {#1} 
  \seq_map_inline:Nn \l_tmpa_seq {%
    \prg_replicate:nn {#2} {\stepcounter{##1}}
  }
}





\cs_new_nopar:Nn \xcntperchap_trackcounters: {%
  \seq_map_inline:cn {g_xcntperchap_tracklevel_seq} {%
    \xcntperchap_storetracked_values:n{##1}%
  }
}

\NewDocumentCommand{\TrackCounters}{}{%
  \xcntperchap_trackcounters:
}


\NewDocumentCommand{\cntperchapsetup}{}{%
% Unused so far  
}

\NewDocumentCommand{\LoadTrackedValues}{}{%
  \IfFileExists{\jobname.cpc}{%
    \xcntperchap_open_trackfile_for_read:n{\jobname.cpc}
    \GetStoredValues%
    \xcntperchap_close_trackfile_for_read:
  }{}
}


\cs_new:Nn \l_xcntperchap_numberoftracks:n {%
  \seq_if_exist:cTF { g_xcntperchap_tracklevelstored_#1_seq} {%
    \int_eval:n {\seq_count:c {g_xcntperchap_tracklevelstored_#1_seq} / \seq_count:c {g_xcntperchap_tracklevel_#1_seq}}
  }{
    \c_zero
  }
}  


\cs_new:Nn \xcntperchap_open_trackfile_for_read:n {%
  \ior_open:Nn \g_trackfile_storage_read {#1}
}

\cs_new_nopar:Nn \xcntperchap_close_trackfile_for_read: {%
  \ior_close:N \g_trackfile_storage_read
}

\cs_new:Nn \xcntperchap_open_trackfile_for_write:n {%
  \iow_open:Nn \g_trackfile_storage_write {#1}
}

\cs_new_nopar:Nn \xcntperchap_close_trackfile_for_write: {%
  \iow_close:N \g_trackfile_storage_write
}

\NewDocumentCommand{\CloseTrackFileForWrite}{}{%
  \xcntperchap_close_trackfile_for_write:%
}


\NewDocumentCommand{\OpenTrackFileForWrite}{m}{%
  \xcntperchap_open_trackfile_for_write:n{#1}%
}



\cs_new:Nn \l_xcntperchap_read_countervalues:n {%
  \clist_set:Nx \l_tmpa_clist {#1}%
  \tl_set:Nx \l_tmpa_tl {\clist_item:Nn \l_tmpa_clist {1}}
  \clist_map_inline:Nn \l_tmpa_clist {%
    \seq_gput_right:cn {g_xcntperchap_tracklevelstored_\l_tmpa_tl _seq} {##1} % Store the value in the tracklevel list for counter \l_tmpa_tl
  }
  % Kick the name of the tracklevel counter from the list
  \seq_gremove_all:cx {g_xcntperchap_tracklevelstored_\l_tmpa_tl _seq} {\tl_use:N \l_tmpa_tl}%
}

\cs_new_nopar:Nn \xcntperchap_gettrackedvalues_from_file:n {%
  \ior_map_inline:Nn #1 { \l_xcntperchap_read_countervalues:n{##1}  }
}


% Unexpandable version!
\cs_new:Nn \xcntperchap_obtaintrackedvalue:nnn {%
  \int_set:Nx \l_tracklevelunit_int {#1}
  \seq_if_in:cnTF {g_xcntperchap_tracklevel_#2_seq} {#3} 
  {
    \int_set:Nx \l_tracklevel_tracked_int {\seq_count:c {g_xcntperchap_tracklevel_#2_seq}}
    \int_zero:N \l_tmpa_int
    \seq_map_inline:cn {g_xcntperchap_tracklevel_#2_seq} {%
      \int_incr:N \l_tmpa_int
      \tl_if_eq:nnT { ##1 } {#3 } { 
        \seq_map_break:
      }
    }
    \int_set:NV \l_tmpb_int { \l_tmpa_int }
    \int_set:Nx \l_tmpa_int { \l_tracklevel_tracked_int * (\l_tracklevelunit_int -1) + \l_tmpb_int  }
  }{%
    % Perhaps some error message here, for the moment only a screen message
    \typeout{#3~is~not~under~track~control~of~#2}
  }
}  

\NewDocumentCommand{\ObtainTrackedValue}{omm}{%
  \IfValueTF{#1}{%
    \xcntperchap_obtaintrackedvalue:nnn {#1}{#2}{#3}%
  }{%
    \xcntperchap_obtaintrackedvalue:nnn {\number\value{#2}}{#2}{#3}%
  }%
}


%%%%% 0.2 Expandable Version

\cs_new:Nn \xcntperchap_obtaintrackedvalue_exp:nnn {% Needs some error checking!!!
  \seq_if_exist:cTF {g_xcntperchap_tracklevel_#2_seq} {
    \seq_item:cn {g_xcntperchap_tracklevelstored_#2_seq} { \seq_count:c {g_xcntperchap_tracklevel_#2_seq} * \int_eval:n{#1-1}+\prop_item:cn {g_xcntperchap_tracklevel_#2_prop} {#3}}
  }{
    \typeout{Error!}%
  }
}

\DeclareExpandableDocumentCommand{\ObtainTrackedValueExp}{omm}{%
  \IfValueTF{#1}{%
    \xcntperchap_obtaintrackedvalue_exp:nnn {#1}{#2}{#3}%
  }{%
    \xcntperchap_obtaintrackedvalue_exp:nnn {\number\value{#2}}{#2}{#3}
  }
}


\NewDocumentCommand{\GetStoredValues}{}{%
  \xcntperchap_gettrackedvalues_from_file:n {\g_trackfile_storage_read}
}




\ExplSyntaxOff

\newif\if@cpscleverefloaded

\AtBeginDocument{%
  \@ifpackageloaded{cleveref}{%
    \global\@cpscleverefloadedtrue
  }{%
    \global\@cpscleverefloadedfalse
  }
}


\NewDocumentCommand{\tracklabel}{som}{%
  \zref@labelbyprops{#3}{counter,cps\LastRefSteppedCounter total}% The counter property is for safety here!
  \IfBooleanF{#1}{% No starred version, generate the usual \label
    % Check first whether we're using cleveref
    \IfValueTF{#2}{%
      \@ifcleverefloadedtrue
      \label[#2]{#3}%
      \else
      % ignore the optional argument provided by tracklabel, it's of use since cleveref is not loaded.
      \label{#3}%
      \fi
    }{% No optional argument
      \label{#3}%
    }%
  }% End of checking for starred command
}



\DeclareExpandableDocumentCommand{\ObtainTrackedValueByLabel}{omm}{%
  \zref@ifrefundefined{#2}{??}{%
    \IfValueTF{#1}{%
      \ObtainTrackedValueExp[\zref@extract{#2}{cps#1total}]{#1}{#3}%
    }{%
      \ObtainTrackedValueExp[\zref@extract{#2}{cps\zref@extract{#2}{counter}total}]{\zref@extract{#2}{counter}}{#3}%
    }%
  }%
}


\NewDocumentCommand{\RegisterCounters}{mm}{%
  \RegisterTrackCounter{#1}{#2}%
}



\@onlypreamble\RegisterTrackCounter
\@onlypreamble\RegisterMultipleTrackCounters
\@onlypreamble\RegisterCounters

\makeatother


\AtBeginDocument{%
  \LoadTrackedValues%
  \OpenTrackFileForWrite{\jobname.cpc}%
}

\AtEndDocument{%
  \TrackCounters%
  \CloseTrackFileForWrite%
}
\endinput

