%
% Copyright (c) 2021-2025 Zeping Lee
% Released under the MIT License.
% Repository: https://github.com/zepinglee/citeproc-lua
%

% ## Initialization at `\begin{document}`

\hook_gput_code:nnn { begindocument } { . } { \__csl_at_begin_document_hook: }


\cs_new:Npn \__csl_at_begin_document_hook:
 {
   \__csl_write_aux_info:
   \sys_if_engine_luatex:TF
     { \__csl_initialize_lua_module: }
     {
       \__csl_load_bbl:
       \__csl_get_style_class:
     }
   \__csl_read_entry_ids:
   \__csl_set_ref_section_level:
 }


\clist_new:N \g__csl_aux_bib_files_clist

\cs_new:Npn \__csl_write_aux_info:
 {
   \tl_if_empty:NF \l__csl_style_tl
     { \exp_args:NV \__csl_write_aux_bibstyle:n \l__csl_style_tl }
   \tl_if_empty:NT \l__csl_style_tl
     { \tl_set_eq:NN \l__csl_style_tl \g__csl_aux_bibstyle_tl }
   \tl_if_empty:NT \l__csl_style_tl
     {
       \msg_warning:nn { citation-style-language } { missing-style-name }
       \tl_set:Nn \l__csl_style_tl { apa }
     }
   \tl_set_eq:NN \csl@style \l__csl_style_tl
   \clist_if_empty:NTF \l__csl_bib_resources_clist
     {
       \clist_if_empty:NTF \g__csl_aux_bib_files_clist
         { \msg_warning:nn { citation-style-language } { empty-bib-resources } }
         {
           \clist_map_inline:Nn \g__csl_aux_bib_files_clist
             { \addbibresource {##1} }
         }
     }
     {
       \clist_map_inline:Nn \l__csl_bib_resources_clist
         {
           \tl_clear:N \l_tmpa_tl
           \prop_get:NnN \g__csl_bib_resource_options_prop {##1} \l_tmpa_tl
           \exp_args:NnV \__csl_write_aux_bibdata:nn {##1} \l_tmpa_tl
         }
     }

   \__csl_write_aux_csl_options:
 }

\cs_new:Npn \__csl_write_aux_bibstyle:n #1
 {
   \if@filesw
     \iow_now:Ne \@auxout
       {
         \token_to_str:N \csl@aux@style
           { \int_use:N \g__csl_ref_section_index_int } {#1}
       }
   \fi
 }

\cs_new:Npn \__csl_write_aux_bibdata:nn #1#2
 {
   \if@filesw
     % Full expansion for files like \jobname.bib
     \iow_now:Ne \@auxout
       {
         \token_to_str:N \csl@aux@data
           { \int_use:N \g__csl_ref_section_index_int } {#1} {#2}
       }
   \fi
 }

% In earlier time, \bibdata{xxx.json} was used but this causes latexmk unable
% to find xxx.json.bib and it refuses to run the $bibtex procedure.
% John Collins suggests using a different command than \bibdata.
\cs_new:Npn \csl@aux@data #1#2#3
 {
   \str_if_eq:nnT {#1} { 0 }
     {
       \clist_map_inline:nn {#2}
         {
           \clist_gput_right:Nn \g__csl_aux_bib_files_clist {##1}
           \prop_gput:Nnn \g__csl_bib_resource_options_prop {##1} {#3}
         }
     }
 }



\cs_new:Npn \__csl_initialize_lua_module:
 {
   \bool_if:NT \l__csl_regression_test_bool
     {
       \lua_now:n { csl_citation_manager.regression_test = true }
     }
   \lua_now:e
     {
       csl_citation_manager:init(
         "\l__csl_style_tl",
         "\l__csl_locale_tl"
       )
     }
   \str_if_eq:eeTF
     {
       \lua_now:n
         {
           tex.print(tostring(
             csl_citation_manager.ref_section.initialized
           ))
         }
     }
     { true }
     { \bool_set_true:N \l__csl_engine_initialized_bool }
     { \bool_set_false:N \l__csl_engine_initialized_bool }
   \__csl_get_style_class_luatex:
   \@ifpackageloaded { hyperref }
     { \lua_now:n { csl_citation_manager:enable_linking() } }
     { }
 }

\msg_new:nnn { citation-style-language } { missing-style-name }
 { Missing~ style~ name.~ Will~ use~ default~ APA~ style. }

\msg_new:nnn { citation-style-language } { empty-bib-resources }
 { Empty~ bibliographic~ resources.~ Use~ \token_to_str:N \addbibresource. }


% \str_new:N \l__csl_style_class_str
% In-text (including numeric or author-date) or note style
% \bool_new:N \l__csl_note_style_bool

\cs_new:Npn \__csl_get_style_class_luatex: {
 \bool_set_false:N \l__csl_note_bool
 \bool_if:NT \l__csl_engine_initialized_bool
   {
     \tl_set:Ne \l__csl_class_tl { \lua_now:n { tex.print(csl_citation_manager:get_style_class()) } }
     \tl_if_eq:NnT \l__csl_class_tl { note }
       { \bool_set_true:N \l__csl_note_bool }
   }
 \int_compare:nNnT { \g__csl_ref_section_index_int } = { 0 }
   { \bool_gset_eq:NN \g__csl_global_note_class_bool \l__csl_note_bool }
}


\bool_new:N \l__csl_engine_initialized_bool

\prop_new:N \l__csl_language_code_map_prop
\prop_set_from_keyval:Nn \l__csl_language_code_map_prop
 {
   acadian         = fr-CA,
   american        = en-US,
   australian      = en-AU,
   afrikaans       = af-ZA,
   albanian        = sq-AL,
   amharic         = am-ET,
   arabic          = ar,
   armenian        = hy-AM,
   asturian        = ast-ES,
   austrian        = de-AT,
   bahasa          = id-ID,
   bahasai         = id-ID,
   bahasam         = id-ID,
   basque          = eu-ES,
   bengali         = bn-BD,
   bgreek          = el-GR,
   brazil          = pt-BR,
   brazilian       = pt-BR,
   breton          = br-FR,
   british         = en-GB,
   bulgarian       = bg-BG,
   canadian        = en-CA,
   canadien        = fr-CA,
   catalan         = ca-AD,
   coptic          = cop,
   croatian        = hr-HR,
   czech           = cs-CZ,
   danish          = da-DK,
   divehi          = dv-MV,
   dutch           = nl-NL,
   english         = en-US,
   esperanto       = eo-001,
   estonian        = et-EE,
   ethiopia        = am-ET,
   farsi           = fa-IR,
   finnish         = fi-FI,
   francais        = fr-FR,
   french          = fr-FR,
   frenchle        = fr-FR,
   friulan         = fur-IT,
   galician        = gl-ES,
   german          = de-DE,
   germanb         = de-DE,
   greek           = el-GR,
   hebrew          = he-IL,
   hindi           = hi-IN,
   ibygreek        = el-CY,
   icelandic       = is-IS,
   indon           = id-ID,
   indonesia       = id-ID,
   interlingua     = ia-FR,
   irish           = ga-IE,
   italian         = it-IT,
   japanese        = ja-JP,
   kannada         = kn-IN,
   lao             = lo-LA,
   latin           = la-Latn,
   latvian         = lv-LV,
   lithuanian      = lt-LT,
   lowersorbian    = dsb-DE,
   lsorbian        = dsb-DE,
   magyar          = hu-HU,
   malay           = id-ID,
   malayalam       = ml-IN,
   marathi         = mr-IN,
   meyalu          = id-ID,
   mongolian       = mn-Cyrl,
   naustrian       = de-AT,
   newzealand      = en-NZ,
   ngerman         = de-DE,
   nko             = ha-NG,
   norsk           = nb-NO,
   norwegian       = nn-NO,
   nynorsk         = nn-NO,
   occitan         = oc-FR,
   piedmontese     = pms-IT,
   pinyin          = pny,
   polish          = pl-PL,
   polutonikogreek = el-GR,
   portuges        = pt-PT,
   portuguese      = pt-PT,
   romanian        = ro-RO,
   romansh         = rm-CH,
   russian         = ru-RU,
   samin           = se-NO,
   sanskrit        = sa-IN,
   scottish        = gd-GB,
   serbian         = sr-Latn,
   serbianc        = sr-Cyrl,
   slovak          = sk-SK,
   slovene         = sl-SI,
   slovenian       = sl-SI,
   spanish         = es-ES,
   swedish         = sv-SE,
   swiss           = de-CH,
   swissgerman     = de-CH,
   nswissgerman    = de-CH,
   syriac          = syc,
   tamil           = ta-IN,
   telugu          = te-IN,
   thai            = th-TH,
   thaicjk         = th-TH,
   tibetan         = bo-CN,
   turkish         = tr-TR,
   turkmen         = tk-TM,
   ukrainian       = uk-UA,
   urdu            = ur-IN,
   UKenglish       = en-GB,
   uppersorbian    = hsb-DE,
   USenglish       = en-US,
   usorbian        = hsb-DE,
   vietnamese      = vi-VN,
   welsh           = cy-GB,
 }


\cs_new:Npn \__csl_write_aux_csl_options:
 {
   \clist_clear:N \l_tmpa_clist  % list of options to write to aux file
   % locale
   \tl_if_empty:NT \l__csl_locale_tl
     { \__csl_get_locale_from_babel: }
   \tl_if_empty:NF \l__csl_locale_tl
     {
       \clist_put_right:Ne \l_tmpa_clist
         { locale = \l__csl_locale_tl }
     }
   % linking
   \@ifpackageloaded { hyperref }
     { \clist_put_right:Nn \l_tmpa_clist { linking = true } }
     { }
   % write to aux file
   \prop_if_empty:NF \l_tmpa_clist
     {
       \exp_args:Ne \__csl_write_aux_options:n
         { \clist_use:Nn \l_tmpa_clist { , } }
     }
 }

% This is the hook for `babel` package.
\cs_new:Npn \__csl_get_locale_from_babel: { }

\cs_new:Npn \__csl_write_aux_options:n #1
 {
   \if@filesw
     \iow_now:Ne \@auxout
       {
         \token_to_str:N \csl@aux@options
         { \int_use:N \g__csl_ref_section_index_int } {#1}
       }
   \fi
 }

\tl_new:N \l__csl_ref_section_index_tl

\cs_new:Npn \csl@aux@options #1#2
 {
   \tl_set:Nn \l__csl_ref_section_index_tl {#1}
   \keys_set:nn { csl / options } {#2}
 }


% Load .bbl at the beginning of document to save one pass of latex.
% In this procedure, the \cslcitation command is processed and the contents
% of `thebibliography` is stored into \g__csl_bibliographies_prop.
\cs_new:Npn \__csl_load_bbl:
 {
   % The \@input@ prints "No file ....bbl" in the .log file from which
   % the latexmk decides to run $bibtex or not.
   \__csl_collect_bibliography:n { \@input@ { \jobname .bbl } }
 }


% A document may have multiple bibliographies or biblists and they are stored
% in `\g__csl_bibliographies_prop` by their index.
\prop_new:N \g__csl_bibliographies_prop


% Collection the bibliography into \g__csl_bibliographies_prop
\cs_new:Npn \__csl_collect_bibliography:n #1
 {
   \group_begin:
     % URLs may contain "%" and "#" characters.
     \char_set_catcode_other:N \%
     \char_set_catcode_other:N \#
     \RenewDocumentEnvironment { thebibliography } { m +b }
       {
         \tl_set:Nn \l__csl_bib_index_tl { 1 }
         \keys_set:nn { csl / bib-options } {##1}
         \prop_gput:NVn \g__csl_bibliographies_prop \l__csl_bib_index_tl
           {
             \begin { thebibliography } {##1}
               ##2
             \end { thebibliography }
           }
       }
       { }
     % Perform the execution
     #1
   \group_end:
 }

% At the moment, the `\csloptions` only reads the style class from `.bbl`
% generated by `citeproc-lua`.
% #1: refsection index
% #2: refsection options
\NewDocumentCommand \csloptions { m m }
 {
   \tl_set:Nn \l__csl_ref_section_index_tl {#1}
   \keys_set:nn { csl / options } {#2}
 }

% The class option is in the `<style>=<class>` form
% (e.g. `\csloptions{class={apa=in-text}}`).
\keys_define:nn { csl / options }
 {
   class .code:n = { \prop_gput_from_keyval:Nn \g__csl_style_class_prop {#1} },
   entry-ids .code:n = { \__csl_process_entry_ids:n {#1} } ,
   excluded-ids .code:n = { \__csl_process_excluded_ids:n {#1} } ,
   unknown .code:n = { } ,
 }

\cs_new:Npn \__csl_process_entry_ids:n #1 { }

\cs_new:Npn \__csl_process_excluded_ids:n #1 { }

\prop_new:N \g__csl_style_class_prop

% The `\textcite` bahaves differently with note styles.
% Thus we read the style class from the `.csl` file with non-LuaTeX engines.
\ior_new:N \l__csl_style_ior
\cs_new:Npn \__csl_get_style_class:
 {
   \tl_clear:N \l__csl_class_tl
   \bool_set_false:N \l__csl_note_bool
   \prop_get:NVNF \g__csl_style_class_prop \l__csl_style_tl \l__csl_class_tl
     { \__csl_read_style_class: }
   \tl_if_empty:NF \l__csl_class_tl
     {
       \str_case:VnTF \l__csl_class_tl
         {
           { note } { \bool_set_true:N \l__csl_note_bool }
           { in-text } { \bool_set_false:N \l__csl_note_bool }
         }
         {
           \int_compare:nNnT { \g__csl_ref_section_index_int } = { 0 }
             {
               \bool_gset_eq:NN \g__csl_global_note_class_bool \l__csl_note_bool
             }
         }
         {
           \msg_error:nnV { citation-style-language } { invalid-style-class }
           \l__csl_class_tl
         }
     }
 }

\cs_new:Npn \__csl_read_style_class:
 {
   \tl_if_blank:VF \l__csl_style_tl
     {
       \exp_args:NNe \ior_open:Nn \l__csl_style_ior { \l__csl_style_tl .csl }
       \ior_map_inline:Nn \l__csl_style_ior
         {
           % \tl_show:n {##1}
           \tl_if_in:nnTF {##1} { class="note" }
             {
               \tl_set:Nn \l__csl_class_tl { note }
               \bool_set_true:N \l__csl_note_bool
               \prop_gput:NVn \g__csl_style_class_prop \l__csl_style_tl { note }
               \ior_map_break:
             }
             {
               \tl_if_in:nnT {##1} { class="in-text" }
                 {
                   \tl_set:Nn \l__csl_class_tl { in-text }
                   \bool_set_false:N \l__csl_note_bool
                   \prop_gput:NVn \g__csl_style_class_prop \l__csl_style_tl { in-text }
                   \ior_map_break:
                 }
             }
         }
       \ior_close:N \l__csl_style_ior
     }
 }

\msg_new:nnn { citation-style-language } { invalid-style-class }
 { Invalid~ style~ class~ '#1'. }

% \msg_new:nnn { citation-style-language } { file-non-exist }
%   { No~ file~ #1. }

% This is a hook for `hyperref`.
\cs_new:Npn \__csl_read_entry_ids: {}

\cs_new:Npn \__csl_set_ref_section_level:
 {
   \str_case:Vn \l__csl_bib_ref_section_str
     {
       { none } { }
       { part }
         { \__csl_patch_ref_section:n { part } }
       { chapter }
         { \__csl_patch_ref_section:n { chapter } }
       { chapter+ }
         {
           \__csl_patch_ref_section:n { part }
           \__csl_patch_ref_section:n { chapter }
         }
       { section }
         { \__csl_patch_ref_section:n { section } }
       { section+ }
         {
           \__csl_patch_ref_section:n { part }
           \__csl_patch_ref_section:n { chapter }
           \__csl_patch_ref_section:n { section }
         }
       { subsection }
         { \__csl_patch_ref_section:n { subsection } }
       { subsection+ }
         {
           \__csl_patch_ref_section:n { part }
           \__csl_patch_ref_section:n { chapter }
           \__csl_patch_ref_section:n { section }
           \__csl_patch_ref_section:n { subsection }
         }
     }
 }

\cs_new:Npn \__csl_patch_ref_section:n #1
 {
   \hook_gput_code:nnn { cmd / #1 / before } { . } { \newrefsection }
 }