% Copyright 2026 Open-Guji (https://github.com/open-guji)
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
%     http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.

\ProvidesExplPackage{core/luatex-cn-core-page}{2026/08/07} {0.4.1}{Page geometry and background support}

% Global token lists for page parameters (centralized)
\tl_new:N \l__luatexcn_page_paper_width_tl
\tl_new:N \l__luatexcn_page_paper_height_tl
\tl_new:N \l__luatexcn_page_margin_top_tl
\tl_new:N \l__luatexcn_page_margin_bottom_tl
\tl_new:N \l__luatexcn_page_margin_left_tl
\tl_new:N \l__luatexcn_page_margin_right_tl
\tl_new:N \l__luatexcn_page_background_color_tl
\tl_new:N \l__luatexcn_page_footskip_tl
\tl_new:N \l__luatexcn_page_topskip_tl
\bool_new:N \l__luatexcn_page_split_enabled_bool
\bool_new:N \l__luatexcn_page_split_right_first_bool
% Two-side margin support (for alternating odd/even page margins)
\bool_new:N \l__luatexcn_page_twoside_bool
\tl_new:N \l__luatexcn_page_margin_inner_tl
\tl_new:N \l__luatexcn_page_margin_outer_tl
% Running header support (书名/章节名 displayed on outer margin)
\bool_new:N \l__luatexcn_page_header_enabled_bool
\tl_new:N \l__luatexcn_page_header_font_size_tl

\keys_define:nn { luatexcn / page }
  {
    paper-width .tl_set:N = \l__luatexcn_page_paper_width_tl,
    paper-width .initial:n = {210mm},

    paper-height .tl_set:N = \l__luatexcn_page_paper_height_tl,
    paper-height .initial:n = {297mm},

    background-color .tl_set:N = \l__luatexcn_page_background_color_tl,
    background-color .initial:n = {},

    margin-top .tl_set:N = \l__luatexcn_page_margin_top_tl,
    margin-top .initial:n = {0pt},

    margin-bottom .tl_set:N = \l__luatexcn_page_margin_bottom_tl,
    margin-bottom .initial:n = {0pt},

    margin-left .tl_set:N = \l__luatexcn_page_margin_left_tl,
    margin-left .initial:n = {0pt},

    margin-right .tl_set:N = \l__luatexcn_page_margin_right_tl,
    margin-right .initial:n = {0pt},

    footskip .tl_set:N = \l__luatexcn_page_footskip_tl,
    footskip .initial:n = {0pt},

    topskip .tl_set:N = \l__luatexcn_page_topskip_tl,
    topskip .initial:n = {0pt},

    % Split page (筒子页) configuration
    page-split-enabled .bool_set:N = \l__luatexcn_page_split_enabled_bool,
    page-split-enabled .initial:n = true,

    page-split-right-first .bool_set:N = \l__luatexcn_page_split_right_first_bool,
    page-split-right-first .initial:n = true,

    % Two-side margin configuration (mirror margins for odd/even pages)
    twoside .bool_set:N = \l__luatexcn_page_twoside_bool,
    twoside .initial:n = false,

    margin-inner .tl_set:N = \l__luatexcn_page_margin_inner_tl,
    margin-inner .initial:n = {},

    margin-outer .tl_set:N = \l__luatexcn_page_margin_outer_tl,
    margin-outer .initial:n = {},

    % Running header (书名/章节名 on outer margin)
    header-enabled .bool_set:N = \l__luatexcn_page_header_enabled_bool,
    header-enabled .initial:n = false,

    header-font-size .tl_set:N = \l__luatexcn_page_header_font_size_tl,
    header-font-size .initial:n = {9pt},

    unknown .code:n = {},
  }

\NewDocumentCommand{\pageSetup}{ m }
  {
    \keys_set:nn { luatexcn / page } { #1 }
    \lua_now:e {
      local~page~=~require('core.luatex-cn-core-page')
      page.setup({
        paper_width~=~"\l__luatexcn_page_paper_width_tl",
        paper_height~=~"\l__luatexcn_page_paper_height_tl",
        margin_top~=~"\l__luatexcn_page_margin_top_tl",
        margin_bottom~=~"\l__luatexcn_page_margin_bottom_tl",
        margin_left~=~"\l__luatexcn_page_margin_left_tl",
        margin_right~=~"\l__luatexcn_page_margin_right_tl",
        twoside~=~\bool_if:NTF~\l__luatexcn_page_twoside_bool~{true}~{false},
        margin_inner~=~"\l__luatexcn_page_margin_inner_tl",
        margin_outer~=~"\l__luatexcn_page_margin_outer_tl"
      })
      _G.page~=~_G.page~or~{}
      _G.page.number_style~=~"\l__luatexcn_page_number_style_tl"
      _G.page.number_font_size~=~"\l__luatexcn_page_number_font_size_tl"
      _G.page.header_enabled~=~\bool_if:NTF~\l__luatexcn_page_header_enabled_bool~{true}~{false};
      _G.page.header_font_size~=~"\l__luatexcn_page_header_font_size_tl"
    }
    % Apply geometry when called in preamble (sets paper size)
    \luatexcn_apply_geometry:
    % Set topskip (only safe in \pageSetup, not in per-page \luatexcn_apply_geometry:
    % where it can trigger spurious blank pages via shipout hook interactions)
    \dim_set:Nn \topskip { \l__luatexcn_page_topskip_tl }
    % Apply split page settings if enabled
    \__luatexcn_page_apply_split:
  }

% Flag to track if geometry has been initialized (margins set to 0)
\bool_new:N \g__luatexcn_geometry_initialized_bool

\cs_new:Npn \luatexcn_apply_geometry:
  {
    % IMPORTANT: We set ALL geometry margins to 0, and handle margins manually in Lua.
    % This simplifies coordinate calculations - (0,0) is now at paper edge.
    % The actual margin values are stored in token lists and passed to Lua for positioning.
    \if_meaning:w \@onlypreamble \@notprerr
      % In document body
      \bool_if:NF \g__luatexcn_geometry_initialized_bool
        {
          % First time - need to set margins to 0 via \newgeometry
          \newgeometry{
            top=0pt, bottom=0pt, left=0pt, right=0pt, footskip=0pt
          }
          \bool_gset_true:N \g__luatexcn_geometry_initialized_bool
        }
    \else
      % In preamble
      \geometry{
        paperwidth=\l__luatexcn_page_paper_width_tl,
        paperheight=\l__luatexcn_page_paper_height_tl,
        top=0pt, bottom=0pt, left=0pt, right=0pt, footskip=0pt
      }
      \bool_gset_true:N \g__luatexcn_geometry_initialized_bool
    \fi
    % Set paper/page dimensions (common for all branches)
    % Must set both \paperwidth and \pagewidth for TikZ overlays (e.g. seal stamps)
    \dim_set:Nn \paperwidth { \l__luatexcn_page_paper_width_tl }
    \dim_set:Nn \paperheight { \l__luatexcn_page_paper_height_tl }
    \dim_set:Nn \pagewidth { \l__luatexcn_page_paper_width_tl }
    \dim_set:Nn \pageheight { \l__luatexcn_page_paper_height_tl }
    % NOTE: \topskip is intentionally NOT set here. Setting \topskip in the
    % document body (after \clearpage) interacts with shipout hooks and can
    % cause spurious blank pages. \topskip is set once in \pageSetup (preamble)
    % and in \__luatexcn_content_zero_skips: (scoped to Content groups).
  }

% ============================================================================
% Page Environment
% Applies new page layout settings for content within the environment
% Usage: \begin{page}[paper-width=21cm, paper-height=29.7cm, margin-top=1cm, ...]
% Accepts all parameters from luatexcn/page namespace.
% After the environment ends, page settings are restored to previous values.
% ============================================================================

\RequirePackage{environ}

% Helper to sync Lua page state back to TeX token lists
\cs_new:Npn \__luatexcn_page_sync_from_lua:
  {
    \tl_set:Nx \l__luatexcn_page_paper_width_tl { \lua_now:n { tex.sprint(require('core.luatex-cn-core-page').get_dim_str('paper_width')) } }
    \tl_set:Nx \l__luatexcn_page_paper_height_tl { \lua_now:n { tex.sprint(require('core.luatex-cn-core-page').get_dim_str('paper_height')) } }
    \tl_set:Nx \l__luatexcn_page_margin_top_tl { \lua_now:n { tex.sprint(require('core.luatex-cn-core-page').get_dim_str('margin_top')) } }
    \tl_set:Nx \l__luatexcn_page_margin_bottom_tl { \lua_now:n { tex.sprint(require('core.luatex-cn-core-page').get_dim_str('margin_bottom')) } }
    \tl_set:Nx \l__luatexcn_page_margin_left_tl { \lua_now:n { tex.sprint(require('core.luatex-cn-core-page').get_dim_str('margin_left')) } }
    \tl_set:Nx \l__luatexcn_page_margin_right_tl { \lua_now:n { tex.sprint(require('core.luatex-cn-core-page').get_dim_str('margin_right')) } }
    \str_if_eq:eeTF { \lua_now:n { tex.sprint(require('core.luatex-cn-core-page').get_split_str('enabled')) } } { true }
      { \bool_set_true:N \l__luatexcn_page_split_enabled_bool }
      { \bool_set_false:N \l__luatexcn_page_split_enabled_bool }
    \str_if_eq:eeTF { \lua_now:n { tex.sprint(require('core.luatex-cn-core-page').get_split_str('right_first')) } } { true }
      { \bool_set_true:N \l__luatexcn_page_split_right_first_bool }
      { \bool_set_false:N \l__luatexcn_page_split_right_first_bool }
  }

% Helper: globally set paper/page dimensions after restore inside \NewEnviron.
% \NewEnviron creates a group; local assignments from split.enable/disable are
% undone when the group ends.  This must be called inside the \NewEnviron body
% AFTER \__luatexcn_page_sync_from_lua: so that the tl variables reflect the
% restored state.  It respects split mode: when split is enabled, \paperwidth
% and \pagewidth are set to half the paper width.
\cs_new:Npn \__luatexcn_page_restore_dims_globally:
  {
    \bool_if:NTF \l__luatexcn_page_split_enabled_bool
      {
        \dim_gset:Nn \paperwidth { \l__luatexcn_page_paper_width_tl / 2 }
        \dim_gset:Nn \paperheight { \l__luatexcn_page_paper_height_tl }
        \dim_gset:Nn \pagewidth { \l__luatexcn_page_paper_width_tl / 2 }
        \dim_gset:Nn \pageheight { \l__luatexcn_page_paper_height_tl }
      }
      {
        \dim_gset:Nn \paperwidth { \l__luatexcn_page_paper_width_tl }
        \dim_gset:Nn \paperheight { \l__luatexcn_page_paper_height_tl }
        \dim_gset:Nn \pagewidth { \l__luatexcn_page_paper_width_tl }
        \dim_gset:Nn \pageheight { \l__luatexcn_page_paper_height_tl }
      }
  }

\NewEnviron{Page}[1][]{%
    \clearpage
    % Save current settings in Lua (includes split state)
    \lua_now:n { require('core.luatex-cn-core-page').save() }
    % Apply new settings
    \keys_set:nn { luatexcn / page } { #1 }
    % Sync page dimensions to Lua (must be before split enable/disable)
    \lua_now:e {
      local~page~=~require('core.luatex-cn-core-page');~
      page.setup({
        paper_width~=~"\l__luatexcn_page_paper_width_tl",
        paper_height~=~"\l__luatexcn_page_paper_height_tl",
        margin_top~=~"\l__luatexcn_page_margin_top_tl",
        margin_bottom~=~"\l__luatexcn_page_margin_bottom_tl",
        margin_left~=~"\l__luatexcn_page_margin_left_tl",
        margin_right~=~"\l__luatexcn_page_margin_right_tl"
      });~
    }
    \luatexcn_apply_geometry:
    % Sync split page settings to Lua and apply
    \__luatexcn_page_split_sync:
    \bool_if:NTF \l__luatexcn_page_split_enabled_bool
      { \lua_now:n { require('core.luatex-cn-core-page').split.enable() } }
      { \lua_now:n { require('core.luatex-cn-core-page').split.disable() } }
    \BODY
    \clearpage
    % Restore previous settings from Lua (includes split state)
    % Note: page.restore() calls split.enable/disable which sets pagewidth/pageheight directly
    \lua_now:n { require('core.luatex-cn-core-page').restore() }
    % Sync restored Lua state back to TeX token lists (for subsequent code that reads them)
    \__luatexcn_page_sync_from_lua:
    % Globally restore paper/page dimensions so they survive \NewEnviron group end
    \__luatexcn_page_restore_dims_globally:
}

% (CJK alias for Page moved to end of file)

% ============================================================================
% Page Split (筒子页) - splits each page into two half-pages
% State is stored in _G.page.split (Lua global) for cross-module communication
% ============================================================================

% Sync TeX keys to Lua global state
\cs_new:Npn \__luatexcn_page_split_sync:
  {
    \lua_now:e {
      _G.page.split.enabled = \bool_if:NTF \l__luatexcn_page_split_enabled_bool {true} {false};
      _G.page.split.right_first = \bool_if:NTF \l__luatexcn_page_split_right_first_bool {true} {false};
    }
  }

% Enable split page processing
\NewDocumentCommand{\enableSplitPage}{ O{} }
  {
    \tl_if_empty:nF { #1 } { \keys_set:nn { luatexcn / page } { #1 } }
    \__luatexcn_page_split_sync:
    \lua_now:n { require('core.luatex-cn-core-page').split.enable() }
  }

% Disable split page processing
\NewDocumentCommand{\disableSplitPage}{ }
  {
    \lua_now:n { require('core.luatex-cn-core-page').split.disable() }
  }

% (CJK aliases moved to end of file)

% Helper: apply split page if enabled
\cs_new:Npn \__luatexcn_page_apply_split:
  {
    \bool_if:NT \l__luatexcn_page_split_enabled_bool
      { \enableSplitPage }
  }

% ============================================================================
% Page Number Style
% ============================================================================
% Supported styles:
%   digits  - 逐位中文数字 (915 → 九一五)
%   chinese - 传统中文数字 (915 → 九百十五)
%   arabic  - 阿拉伯数字 (915)
%   none    - 不显示页码

\tl_new:N \l__luatexcn_page_number_style_tl
\tl_new:N \l__luatexcn_page_number_font_size_tl

\keys_define:nn { luatexcn / page }
  {
    page-number-style .tl_set:N = \l__luatexcn_page_number_style_tl,
    page-number-style .initial:n = {none},

    page-number-font-size .tl_set:N = \l__luatexcn_page_number_font_size_tl,
    page-number-font-size .initial:n = {9pt},
  }

% DrawVerticalPageNumber: defined outside ExplSyntax to avoid catcode issues
% in shipout hooks. Reads style from _G.page.number_style (synced by \pageSetup).
\ExplSyntaxOff
\newcommand{\DrawVerticalPageNumber}{%
  \directlua{
    local style = _G.page and _G.page.number_style or "none"
    if style ~= "none" and style ~= "" then
      local utils = require('util.luatex-cn-utils')
      local pg = tex.count[0]
      local s
      if style == "digits" then
        s = utils.to_chinese_digits(pg)
      elseif style == "chinese" then
        s = utils.to_chinese_numeral(pg)
      elseif style == "arabic" then
        s = tostring(pg)
      else
        return
      end
      local parts = {}
      if style == "arabic" then
        for i = 1, string.len(s) do
          table.insert(parts, string.sub(s, i, i))
        end
      else
        for p, c in utf8.codes(s) do
          table.insert(parts, utf8.char(c))
        end
      end
      tex.sprint(table.concat(parts, "\\\\\\noexpand\\relax "))
    end
  }%
}

% DrawVerticalText: renders arbitrary text vertically (one character per line).
% Used by vbook running headers to display book name / chapter title.
\newcommand{\DrawVerticalText}[1]{%
  \directlua{
    local s = "\luaescapestring{#1}"
    if s == "" then return end
    local parts = {}
    for p, c in utf8.codes(s) do
      table.insert(parts, utf8.char(c))
    end
    tex.sprint(table.concat(parts, "\\\\\\noexpand\\relax "))
  }%
}
\ExplSyntaxOn

% ============================================================================
% Page Number Commands
% ============================================================================

% Command to reset page number to 1
\NewDocumentCommand{\ResetPageNumber}{ }
  {
    \lua_now:e { core.reset_page_number(); }
  }

% Command to set page number to arbitrary value
\NewDocumentCommand{\SetPageNumber}{ m }
  {
    \lua_now:e { core.set_page_number(#1); }
  }
% ============================================================================
% Override page break commands for grid content
% ============================================================================

\cs_new_protected:Npn \__luatexcn_page_override_pagebreak:
  {
    \RenewDocumentCommand{\newpage}{}{\penalty-10003 }
    \RenewDocumentCommand{\clearpage}{}{\penalty-10003 }
    % Override \chapter inside BodyText: insert marker node + optional text output
    % \chapter{text}          → marker + styled text
    % \chapter[banxin]{text}  → marker (banxin title) + styled text (body text)
    % \chapter*{text}         → marker only
    % \chapter*[banxin]{text} → marker only (banxin title)
    \RenewDocumentCommand \chapter { s o m }
      {
        \IfBooleanTF {##1}
          {
            \IfValueTF {##2}
              { \__luatexcn_chapter_bodytext_star:n {##2} }
              { \__luatexcn_chapter_bodytext_star:n {##3} }
          }
          {
            \IfValueTF {##2}
              { \__luatexcn_chapter_bodytext_nostar:nn {##2} {##3} }
              { \__luatexcn_chapter_bodytext_nostar:nn {##3} {##3} }
          }
      }
  }

% ============================================================
% Chinese aliases / 中文别名
% ============================================================
% Simplified Chinese / 简体
\NewEnvironmentCopy{页}{Page}
\NewCommandCopy{\页面设置}{\pageSetup}
\NewCommandCopy{\开启分页}{\enableSplitPage}
\NewCommandCopy{\关闭分页}{\disableSplitPage}
\NewCommandCopy{\设置页码}{\SetPageNumber}
\NewCommandCopy{\重置页码}{\ResetPageNumber}
% \换页 must delegate to \newpage (not copy) because \newpage is
% overridden inside Content groups to emit PENALTY_FORCE_PAGE.
\NewDocumentCommand{\换页}{}{\newpage}
% Traditional Chinese / 繁体
\NewCommandCopy{\頁面設置}{\pageSetup}
\NewCommandCopy{\開啟分頁}{\enableSplitPage}
\NewCommandCopy{\關閉分頁}{\disableSplitPage}
\NewCommandCopy{\設置頁碼}{\SetPageNumber}
\NewCommandCopy{\重置頁碼}{\ResetPageNumber}
\NewCommandCopy{\換頁}{\换页}

% ============================================================
% Chinese key aliases / 中文 Key 别名
% ============================================================
\keys_define:nn { luatexcn / page }
  {
    % 简体
    纸宽 .tl_set:N = \l__luatexcn_page_paper_width_tl,
    纸高 .tl_set:N = \l__luatexcn_page_paper_height_tl,
    底色 .tl_set:N = \l__luatexcn_page_background_color_tl,
    上边距 .tl_set:N = \l__luatexcn_page_margin_top_tl,
    下边距 .tl_set:N = \l__luatexcn_page_margin_bottom_tl,
    左边距 .tl_set:N = \l__luatexcn_page_margin_left_tl,
    右边距 .tl_set:N = \l__luatexcn_page_margin_right_tl,
    页脚距 .tl_set:N = \l__luatexcn_page_footskip_tl,
    页头距 .tl_set:N = \l__luatexcn_page_topskip_tl,
    开启分页 .bool_set:N = \l__luatexcn_page_split_enabled_bool,
    右页优先 .bool_set:N = \l__luatexcn_page_split_right_first_bool,
    页码样式 .tl_set:N = \l__luatexcn_page_number_style_tl,
    页码字号 .tl_set:N = \l__luatexcn_page_number_font_size_tl,
    启用页眉 .bool_set:N = \l__luatexcn_page_header_enabled_bool,
    页眉字号 .tl_set:N = \l__luatexcn_page_header_font_size_tl,
    % 繁体（与简体不同形的）
    紙寬 .tl_set:N = \l__luatexcn_page_paper_width_tl,
    紙高 .tl_set:N = \l__luatexcn_page_paper_height_tl,
    上邊距 .tl_set:N = \l__luatexcn_page_margin_top_tl,
    下邊距 .tl_set:N = \l__luatexcn_page_margin_bottom_tl,
    左邊距 .tl_set:N = \l__luatexcn_page_margin_left_tl,
    右邊距 .tl_set:N = \l__luatexcn_page_margin_right_tl,
    頁腳距 .tl_set:N = \l__luatexcn_page_footskip_tl,
    頁頭距 .tl_set:N = \l__luatexcn_page_topskip_tl,
    開啟分頁 .bool_set:N = \l__luatexcn_page_split_enabled_bool,
    右頁優先 .bool_set:N = \l__luatexcn_page_split_right_first_bool,
    頁碼樣式 .tl_set:N = \l__luatexcn_page_number_style_tl,
    頁碼字號 .tl_set:N = \l__luatexcn_page_number_font_size_tl,
    啟用頁眉 .bool_set:N = \l__luatexcn_page_header_enabled_bool,
    頁眉字號 .tl_set:N = \l__luatexcn_page_header_font_size_tl,
  }

\endinput
