% 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.
% vertical.sty
% LuaTeX 竖排排版宏包
% 这是 luatex_cn 的子包
%
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{luatex-cn-vertical}[2026/01/19 v0.1.1 Chinese vertical typesetting for LuaTeX]

% Check if LuaTeX is being used
\RequirePackage{ifluatex}
\ifluatex\else
  \PackageError{luatex-cn-vertical}{This package requires LuaTeX}{%
    Please compile your document with LuaLaTeX.}
\fi

% Load luatexbase for attributes
\RequirePackage{luatexbase}
\RequirePackage{xcolor}
\RequirePackage{enumitem}
% 设置 itemize 默认样式：空标签 + 紧凑间距（符合古籍排版风格）
\setlist[itemize]{label={}, nosep}
\newluatexattribute\cnverticalindent
\newluatexattribute\cnverticalrightindent
\newluatexattribute\cnverticaltextboxwidth
\newluatexattribute\cnverticaltextboxheight
\newluatexattribute\cnverticaltextboxdistribute
\newluatexattribute\cnverticaljiazhu
\newluatexattribute\cnverticaljiazhusub

% Load the Lua modules
% Split into multiple directlua blocks for better error reporting

% Robust Lua loader with error reporting
\NewDocumentCommand{\cnvLua}{ +m }{%
  \directlua{
    local status, err = pcall(function() #1 end)
    if not status then
      tex.error("vertical Lua error: " .. tostring(err))
    end
  }
}

% Initialise Lua Namespace
\cnvLua{
  _G.vertical = _G.vertical or {}
  _G.vertical.debug = _G.vertical.debug or {
      enabled = false,
      verbose_log = true,
      show_grid = true,
      show_boxes = true
  }
}

% Clear cache
\cnvLua{
  package.loaded["luatex-cn-vertical-base-constants"] = nil
  package.loaded["luatex-cn-vertical-base-utils"] = nil
  package.loaded["luatex-cn-vertical-base-text-utils"] = nil
  package.loaded["luatex-cn-vertical-flatten-nodes"] = nil
  package.loaded["luatex-cn-vertical-layout-grid"] = nil
  package.loaded["luatex-cn-vertical-render-page"] = nil
  package.loaded["luatex-cn-vertical-render-border"] = nil
  package.loaded["luatex-cn-vertical-render-background"] = nil
  package.loaded["luatex-cn-vertical-render-position"] = nil
  package.loaded["luatex-cn-vertical-core-main"] = nil
  package.loaded["luatex-cn-vertical-core-textbox"] = nil
  package.loaded["luatex-cn-vertical-core-textflow"] = nil
  package.loaded["luatex-cn-vertical-core-sidenote"] = nil
}

% Load Modules
\cnvLua{
  vertical_constants = require('luatex-cn-vertical-base-constants')
  vertical_utils = require('luatex-cn-vertical-base-utils')
  vertical_text_utils = require('luatex-cn-vertical-base-text-utils')
  vertical_hooks = require('luatex-cn-vertical-base-hooks')
  vertical_flatten = require('luatex-cn-vertical-flatten-nodes')
  vertical_layout = require('luatex-cn-vertical-layout-grid')
  vertical_text_position = require('luatex-cn-vertical-render-position')
  vertical_render = require('luatex-cn-vertical-render-page')
  vertical_border = require('luatex-cn-vertical-render-border')
  vertical_background = require('luatex-cn-vertical-render-background')
}


% Load core logic
\cnvLua{
  vertical_textbox = require('luatex-cn-vertical-core-textbox')
  vertical_textflow = require('luatex-cn-vertical-core-textflow')
  vertical_sidenote = require('luatex-cn-vertical-core-sidenote')
  vertical = require('luatex-cn-vertical-core-main')
}

% Define keys for SideNode
\ExplSyntaxOn
\dim_new:N \l_cnv_sidenode_yoffset_dim
\dim_new:N \l_cnv_sidenode_grid_height_dim
\tl_new:N \l_cnv_sidenode_font_size_tl
\tl_new:N \l_cnv_sidenode_color_tl
\dim_new:N \l_cnv_sidenode_padding_top_dim
\dim_new:N \l_cnv_sidenode_padding_bottom_dim

\keys_define:nn { SideNode }
  {
    yoffset .dim_set:N = \l_cnv_sidenode_yoffset_dim,
    yoffset .initial:n = 0pt,
    grid-height .dim_set:N = \l_cnv_sidenode_grid_height_dim,
    font-size .tl_set:N = \l_cnv_sidenode_font_size_tl,
    font-size .initial:n = 10pt,
    color .tl_set:N = \l_cnv_sidenode_color_tl,
    color .initial:n = red,
    border-padding-top .dim_set:N = \l_cnv_sidenode_padding_top_dim,
    border-padding-top .initial:n = 0pt,
    border-padding-bottom .dim_set:N = \l_cnv_sidenode_padding_bottom_dim,
    border-padding-bottom .initial:n = 0pt,
  }

\NewDocumentCommand{\SideNode}{ O{} +m }{%
  \group_begin:
    \keys_set:nn { SideNode } { #1 }
    % Use explicit font size
    \tl_if_empty:NTF \l_cnv_sidenode_font_size_tl
      { \dimen0=\f@size pt }
      { \dimen0=\l_cnv_sidenode_font_size_tl\relax }
    \fontsize{\dimen0}{\dimen0}\selectfont
    % Color must be inside the box to be captured in the node list
    \setbox0=\hbox{\textcolor{\l_cnv_sidenode_color_tl}{#2}}%
    
    % Prepare metadata
    \dimen2=\l_cnv_sidenode_grid_height_dim
    % If grid-height not set, default to font size (tight packing)
    \dim_compare:nF { \dimen2 > 0pt } { \dimen2=\dimen0 }

    \directlua{vertical_sidenote.register_sidenote(0, {
      yoffset = \number\l_cnv_sidenode_yoffset_dim,
      grid_height = \number\dimen2,
      padding_top = \number\l_cnv_sidenode_padding_top_dim,
      padding_bottom = \number\l_cnv_sidenode_padding_bottom_dim
    })}%
  \group_end:
}

% PiZhu (批注) - Floating annotation box
\tl_new:N \l_cnv_pizhu_font_size_tl
\tl_new:N \l_cnv_pizhu_color_tl
\tl_new:N \l_cnv_pizhu_grid_width_tl
\tl_new:N \l_cnv_pizhu_grid_height_tl
\tl_new:N \l_cnv_pizhu_x_tl
\tl_new:N \l_cnv_pizhu_y_tl
\int_new:N \l_cnv_pizhu_height_int

\keys_define:nn { PiZhu }
  {
    x .tl_set:N = \l_cnv_pizhu_x_tl,
    y .tl_set:N = \l_cnv_pizhu_y_tl,
    height .int_set:N = \l_cnv_pizhu_height_int,
    font-size .tl_set:N = \l_cnv_pizhu_font_size_tl,
    font-size .initial:n = {18pt},
    color .tl_set:N = \l_cnv_pizhu_color_tl,
    color .initial:n = {1~0~0},
    grid-width .tl_set:N = \l_cnv_pizhu_grid_width_tl,
    grid-width .initial:n = {20pt},
    grid-height .tl_set:N = \l_cnv_pizhu_grid_height_tl,
    grid-height .initial:n = {19pt},
  }

\NewDocumentCommand{\PiZhu}{ O{} +m }{%
  \group_begin:
    \keys_set:nn { PiZhu } { #1 }
    \TextBox[
      floating=true,
      x=\l_cnv_pizhu_x_tl,
      y=\l_cnv_pizhu_y_tl,
      height=\l_cnv_pizhu_height_int,
      font-color=\l_cnv_pizhu_color_tl,
      font-size=\l_cnv_pizhu_font_size_tl,
      grid-width=\l_cnv_pizhu_grid_width_tl,
      grid-height=\l_cnv_pizhu_grid_height_tl
    ]{#2}
  \group_end:
}

\ExplSyntaxOff



% Use l3keys for key-value parameters
\RequirePackage{xparse}

\ExplSyntaxOn

\tl_new:N \l_cnv_banxin_upper_tl
\tl_new:N \l_cnv_banxin_middle_tl
\tl_new:N \l_cnv_banxin_lower_tl

\bool_new:N \g_cnv_debug_bool
\bool_set_false:N \g_cnv_debug_bool

\bool_new:N \l__cn_vertical_banxin_bool
\bool_set_false:N \l__cn_vertical_banxin_bool

\NewDocumentCommand{\cnvdebugon}{}{\bool_set_true:N \g_cnv_debug_bool}
\NewDocumentCommand{\cnvdebugoff}{}{\bool_set_false:N \g_cnv_debug_bool}

% Define keys
\keys_define:nn { vertical }
  {
    height .tl_set:N = \l__cn_vertical_height_tl,
    height .initial:n = ,

    spacing-col .tl_set:N = \l__cn_vertical_col_spacing_tl,
    spacing-col .initial:n = {},

    grid-width .tl_set:N = \l__cn_vertical_grid_width_tl,
    grid-width .initial:n = {1.5em},

    grid-height .tl_set:N = \l__cn_vertical_grid_height_tl,
    grid-height .initial:n = {1.2em},

    cols .int_set:N = \l__cn_vertical_cols_int,
    cols .initial:n = 0,

    debug .code:n = {
      \str_if_eq:nnTF { #1 } { true }
        { \bool_gset_true:N \g_cnv_debug_bool }
        { \bool_gset_false:N \g_cnv_debug_bool }
    },

    border .bool_set:N = \l__cn_vertical_border_bool,
    border .initial:n = false,

    banxin .bool_set:N = \l__cn_vertical_banxin_bool,
    banxin .initial:n = false,

    outer-border .bool_set:N = \l__cn_vertical_outer_border_bool,
    outer-border .initial:n = false,

    outer-border-thickness .tl_set:N = \l__cn_vertical_outer_border_thickness_tl,
    outer-border-thickness .initial:n = {2pt},

    outer-border-sep .tl_set:N = \l__cn_vertical_outer_border_sep_tl,
    outer-border-sep .initial:n = {2pt},

    border-padding-top .tl_set:N = \l__cn_vertical_border_padding_top_tl,
    border-padding-top .initial:n = {0.1em},

    border-padding-bottom .tl_set:N = \l__cn_vertical_border_padding_bottom_tl,
    border-padding-bottom .initial:n = {0.1em},

    border-thickness .tl_set:N = \l__cn_vertical_border_thickness_tl,
    border-thickness .initial:n = {0.4pt},

    vertical-align .tl_set:N = \l__cn_vertical_valign_tl,
    vertical-align .initial:n = {center},

    n-column .int_set:N = \l__cn_vertical_n_column_int,
    n-column .initial:n = 8,

    page-columns .tl_set:N = \l__cn_vertical_page_columns_tl,
    page-columns .initial:n = {},

    border-color .tl_set:N = \l__cn_vertical_border_color_tl,
    border-color .initial:n = {black},

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

    font-color .tl_set:N = \l__cn_vertical_font_color_tl,
    font-color .initial:n = {},

    paper-width .tl_set:N = \l__cn_vertical_paper_width_tl,
    paper-width .initial:n = {0pt},

    paper-height .tl_set:N = \l__cn_vertical_paper_height_tl,
    paper-height .initial:n = {0pt},

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

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

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

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

    jiazhu-font-size .tl_set:N = \l__cn_vertical_jiazhu_size_tl,
    jiazhu-font-size .initial:n = {},

    % Jiazhu alignment: outward (default), inward, center, left, right
    jiazhu-align .tl_set:N = \l__cn_vertical_jiazhu_align_tl,
    jiazhu-align .initial:n = {outward},

    % Banxin (版心) configuration
    banxin-upper-ratio .tl_set:N = \l_cnv_banxin_upper_tl,
    banxin-upper-ratio .initial:n = {0.28},

    banxin-middle-ratio .tl_set:N = \l_cnv_banxin_middle_tl,
    banxin-middle-ratio .initial:n = {0.56},

    % Book Name (formerly Banxin text)
    book-name .tl_set:N = \l_cnv_book_name_tl,
    book-name .initial:n = {},

    % Banxin padding (版心独立的间距设置)
    banxin-padding-top .tl_set:N = \l_cnv_banxin_padding_top_tl,
    banxin-padding-top .initial:n = {2pt},

    banxin-padding-bottom .tl_set:N = \l_cnv_banxin_padding_bottom_tl,
    banxin-padding-bottom .initial:n = {10pt},

    % Lower Yuwei (下鱼尾) - optional decoration
    lower-yuwei .bool_set:N = \l_cnv_lower_yuwei_bool,
    lower-yuwei .initial:n = true,

    % Chapter title (章节标题) - displayed in section 2 of banxin
    chapter-title .tl_set:N = \l_cnv_chapter_title_tl,
    chapter-title .initial:n = {},

    chapter-title-top-margin .tl_set:N = \l_cnv_chapter_title_top_margin_tl,
    chapter-title-top-margin .initial:n = {20pt},

    chapter-title-cols .int_set:N = \l_cnv_chapter_title_cols_int,
    chapter-title-cols .initial:n = 1,

    chapter-title-font-size .tl_set:N = \l_cnv_chapter_title_font_size_tl,
    chapter-title-font-size .initial:n = {},

    chapter-title-grid-height .tl_set:N = \l_cnv_chapter_title_grid_height_tl,
    chapter-title-grid-height .initial:n = {},

    book-name-align .tl_set:N = \l_cnv_book_name_align_tl,
    book-name-align .initial:n = {center},

    book-name-grid-height .tl_set:N = \l_cnv_book_name_grid_height_tl,
    book-name-grid-height .initial:n = {},

    upper-yuwei .bool_set:N = \l_cnv_upper_yuwei_bool,
    upper-yuwei .initial:n = true,

    banxin-divider .bool_set:N = \l_cnv_banxin_divider_bool,
    banxin-divider .initial:n = true,

    page-number-align .tl_set:N = \l_cnv_page_number_align_tl,
    page-number-align .initial:n = {right-bottom},

    page-number-font-size .tl_set:N = \l_cnv_page_number_font_size_tl,
    page-number-font-size .initial:n = {15pt},
    
    font-size .tl_set:N = \l__cn_vertical_font_size_tl,
    font-size .initial:n = {12pt},
  }

\tl_new:N \l__cn_vertical_border_rgb_tl
\tl_new:N \l__cn_vertical_background_rgb_tl
\tl_new:N \l__cn_vertical_font_rgb_tl

% Internal function to call Lua for Grid Layout
\cs_new:Npn \__cn_vertical_process_grid:n #1
  {
    % Put content into a vbox to preserve structure (paragraphs)
    \setbox0=\vbox{\hsize=\maxdimen \hfuzz=\maxdimen \hbadness=10000 \parindent=0pt #1}
    % Call Lua to transform the box
    \setlength{\topskip}{0pt}
    
    % Support direct RGB input (e.g. 0.1 0.2 0.3 or 0.1,0.2,0.3 or 100,50,0)
    \tl_if_in:NnTF \l__cn_vertical_border_color_tl { , }
      { \tl_set_eq:NN \l__cn_vertical_border_rgb_tl \l__cn_vertical_border_color_tl }
      {
        \tl_if_in:NnTF \l__cn_vertical_border_color_tl { ~ }
          { \tl_set_eq:NN \l__cn_vertical_border_rgb_tl \l__cn_vertical_border_color_tl }
          {
             % Try extraction, but wrap in safety
             \cs_if_exist:cTF { color @ \l__cn_vertical_border_color_tl }
             {
               \exp_args:NV \extractcolorspec \l__cn_vertical_border_color_tl \l_tmpa_tl
               \exp_args:NNx \convertcolorspec \l_tmpa_tl {rgb} \l_tmpb_tl
               \tl_set:Nx \l__cn_vertical_border_rgb_tl { \l_tmpb_tl }
             }
             { \tl_set_eq:NN \l__cn_vertical_border_rgb_tl \l__cn_vertical_border_color_tl }
          }
      }
    \tl_replace_all:Nnn \l__cn_vertical_border_rgb_tl { , } { ~ }

    \tl_if_empty:NTF \l__cn_vertical_background_color_tl
      { \tl_set:Nn \l__cn_vertical_background_rgb_tl { nil } }
      {
        \tl_if_in:NnTF \l__cn_vertical_background_color_tl { , }
          { \tl_set_eq:NN \l__cn_vertical_background_rgb_tl \l__cn_vertical_background_color_tl }
          {
            \tl_if_in:NnTF \l__cn_vertical_background_color_tl { ~ }
              { \tl_set_eq:NN \l__cn_vertical_background_rgb_tl \l__cn_vertical_background_color_tl }
              {
                 \cs_if_exist:cTF { color @ \l__cn_vertical_background_color_tl }
                 {
                   \exp_args:NV \extractcolorspec \l__cn_vertical_background_color_tl \l_tmpa_tl
                   \exp_args:NNx \convertcolorspec \l_tmpa_tl {rgb} \l_tmpb_tl
                   \tl_set:Nx \l__cn_vertical_background_rgb_tl { \l_tmpb_tl }
                 }
                 { \tl_set_eq:NN \l__cn_vertical_background_rgb_tl \l__cn_vertical_background_color_tl }
              }
          }
      }
    \tl_replace_all:Nnn \l__cn_vertical_background_rgb_tl { , } { ~ }

    \tl_if_empty:NTF \l__cn_vertical_font_color_tl
      { \tl_set:Nn \l__cn_vertical_font_rgb_tl { nil } }
      {
        \tl_if_in:NnTF \l__cn_vertical_font_color_tl { , }
          { \tl_set_eq:NN \l__cn_vertical_font_rgb_tl \l__cn_vertical_font_color_tl }
          {
            \tl_if_in:NnTF \l__cn_vertical_font_color_tl { ~ }
              { \tl_set_eq:NN \l__cn_vertical_font_rgb_tl \l__cn_vertical_font_color_tl }
              {
                 \cs_if_exist:cTF { color @ \l__cn_vertical_font_color_tl }
                 {
                   \exp_args:NV \extractcolorspec \l__cn_vertical_font_color_tl \l_tmpa_tl
                   \exp_args:NNx \convertcolorspec \l_tmpa_tl {rgb} \l_tmpb_tl
                   \tl_set:Nx \l__cn_vertical_font_rgb_tl { \l_tmpb_tl }
                 }
                 { \tl_set_eq:NN \l__cn_vertical_font_rgb_tl \l__cn_vertical_font_color_tl }
              }
          }
      }
    \tl_replace_all:Nnn \l__cn_vertical_font_rgb_tl { , } { ~ }

    \typeout{DEBUG: banxin-padding-top is \l_cnv_banxin_padding_top_tl}
    \directlua{
      vertical.process_from_tex(0, {
        height = [=[\luaescapestring{\l__cn_vertical_height_tl}]=],
        grid_width = [=[\luaescapestring{\l__cn_vertical_grid_width_tl}]=],
        grid_height = [=[\luaescapestring{\l__cn_vertical_grid_height_tl}]=],
        col_limit = \int_use:N \l__cn_vertical_cols_int,
        debug_on = [=[\bool_if:NTF \g_cnv_debug_bool {true} {false}]=],
        banxin_on = [=[\bool_if:NTF \l__cn_vertical_banxin_bool {true} {false}]=],
        border_on = [=[\bool_if:NTF \l__cn_vertical_border_bool {true} {false}]=],
        border_padding_top = [=[\luaescapestring{\l__cn_vertical_border_padding_top_tl}]=],
        border_padding_bottom = [=[\luaescapestring{\l__cn_vertical_border_padding_bottom_tl}]=],
        vertical_align = [=[\luaescapestring{\l__cn_vertical_valign_tl}]=],
        border_thickness = [=[\luaescapestring{\l__cn_vertical_border_thickness_tl}]=],
        outer_border_on = [=[\bool_if:NTF \l__cn_vertical_outer_border_bool {true} {false}]=],
        outer_border_thickness = [=[\luaescapestring{\l__cn_vertical_outer_border_thickness_tl}]=],
        outer_border_sep = [=[\luaescapestring{\l__cn_vertical_outer_border_sep_tl}]=],
        n_column = \int_use:N \l__cn_vertical_n_column_int,
        page_columns = [=[\luaescapestring{\l__cn_vertical_page_columns_tl}]=],
        border_color = [=[\l__cn_vertical_border_rgb_tl]=],
        background_color = [=[\l__cn_vertical_background_rgb_tl]=],
        font_color = [=[\l__cn_vertical_font_rgb_tl]=],
        paper_width = [=[\luaescapestring{\l__cn_vertical_paper_width_tl}]=],
        paper_height = [=[\luaescapestring{\l__cn_vertical_paper_height_tl}]=],
        margin_top = [=[\luaescapestring{\l__cn_vertical_margin_top_tl}]=],
        margin_bottom = [=[\luaescapestring{\l__cn_vertical_margin_bottom_tl}]=],
        margin_left = [=[\luaescapestring{\l__cn_vertical_margin_left_tl}]=],
        margin_right = [=[\luaescapestring{\l__cn_vertical_margin_right_tl}]=],
        banxin_upper_ratio = [=[\luaescapestring{\l_cnv_banxin_upper_tl}]=],
        banxin_middle_ratio = [=[\luaescapestring{\l_cnv_banxin_middle_tl}]=],
        book_name = [=[\luaescapestring{\l_cnv_book_name_tl}]=],
        banxin_padding_top = [=[\luaescapestring{\l_cnv_banxin_padding_top_tl}]=],
        banxin_padding_bottom = [=[\luaescapestring{\l_cnv_banxin_padding_bottom_tl}]=],
        lower_yuwei = [=[\bool_if:NTF \l_cnv_lower_yuwei_bool {true} {false}]=],
        chapter_title = [=[\luaescapestring{\l_cnv_chapter_title_tl}]=],
        chapter_title_top_margin = [=[\luaescapestring{\l_cnv_chapter_title_top_margin_tl}]=],
        chapter_title_cols = \int_use:N \l_cnv_chapter_title_cols_int,
        chapter_title_font_size = [=[\luaescapestring{\l_cnv_chapter_title_font_size_tl}]=],
        chapter_title_grid_height = [=[\luaescapestring{\l_cnv_chapter_title_grid_height_tl}]=],
        book_name_align = [=[\luaescapestring{\l_cnv_book_name_align_tl}]=],
        book_name_grid_height = [=[\luaescapestring{\l_cnv_book_name_grid_height_tl}]=],
        upper_yuwei = [=[\bool_if:NTF \l_cnv_upper_yuwei_bool {true} {false}]=],
        banxin_divider = [=[\bool_if:NTF \l_cnv_banxin_divider_bool {true} {false}]=],
        page_number_align = [=[\luaescapestring{\l_cnv_page_number_align_tl}]=],
        page_number_font_size = [=[\luaescapestring{\l_cnv_page_number_font_size_tl}]=],
        jiazhu_font_size = [=[\luaescapestring{\l__cn_vertical_jiazhu_size_tl}]=],
        jiazhu_align = [=[\luaescapestring{\l__cn_vertical_jiazhu_align_tl}]=],
        font_size = [=[\luaescapestring{\l__cn_vertical_font_size_tl}]=]
      })
    }
  }

% Define Main Command with Key-Value interface
% Syntax: \VerticalRTT[key=val]{text}
\NewDocumentCommand{\VerticalRTT}{ O{} +m }
  {
    \par\noindent
    \group_begin:
    \keys_set:nn { vertical } { #1 }
    \__cn_vertical_process_grid:n { #2 }
    \group_end:
  }

% Alias
\NewDocumentCommand{\vertical}{ O{} +m }
  {
    \VerticalRTT[#1]{#2}
  }

% Grid Textbox Command (Simplified for Single Column)
% Syntax: \GridTextbox[height=N, distribute=true/false]{content}
% height is in grid units (integers), width is always 1
% Key-value interface for \GridTextbox
\bool_new:N \l__cn_vertical_textbox_border_bool
\bool_new:N \l__cn_vertical_textbox_debug_bool
\tl_new:N \l__cn_vertical_textbox_box_align_tl
\tl_new:N \l__cn_vertical_textbox_inner_gw_tl
\tl_new:N \l__cn_vertical_textbox_inner_gh_tl
\dim_new:N \l__cn_vertical_textbox_inner_gw_dim
\dim_new:N \l__cn_vertical_textbox_inner_gh_dim
\bool_new:N \l__cn_vertical_textbox_floating_bool
\tl_new:N \l__cn_vertical_textbox_x_tl
\tl_new:N \l__cn_vertical_textbox_y_tl
\tl_new:N \l__cn_vertical_textbox_bg_color_tl
\tl_new:N \l__cn_vertical_textbox_font_color_tl
\tl_new:N \l__cn_vertical_textbox_bg_rgb_tl
\tl_new:N \l__cn_vertical_textbox_font_rgb_tl
\tl_new:N \l__cn_vertical_textbox_font_size_tl

\keys_define:nn { vertical / textbox }
  {
    height .int_set:N = \l__cn_vertical_textbox_height_int,
    height .initial:n = 1,
    n-cols .int_set:N = \l__cn_vertical_textbox_n_cols_int,
    n-cols .initial:n = 0,
    inner-grid-width .tl_set:N = \l__cn_vertical_textbox_inner_gw_tl,
    inner-grid-width .initial:n = {},
    inner-grid-height .tl_set:N = \l__cn_vertical_textbox_inner_gh_tl,
    inner-grid-height .initial:n = {},
    distribute .bool_set:N = \l__cn_vertical_textbox_distribute_bool,
    distribute .initial:n = false,
    box-align .tl_set:N = \l__cn_vertical_textbox_box_align_tl,
    box-align .initial:n = {top},
    border .bool_set:N = \l__cn_vertical_textbox_border_bool,
    border .initial:n = false,
    banxin .bool_set:N = \l__cn_vertical_banxin_bool,
    column-align .tl_set:N = \l__cn_vertical_textbox_col_align_tl,
    column-align .initial:n = {},
    debug .bool_set:N = \l__cn_vertical_textbox_debug_bool,
    debug .initial:n = false,
    vertical-align .tl_set:N = \l__cn_vertical_textbox_box_align_tl,
    floating .bool_set:N = \l__cn_vertical_textbox_floating_bool,
    floating .initial:n = false,
    x .tl_set:N = \l__cn_vertical_textbox_x_tl,
    x .initial:n = 0pt,
    y .tl_set:N = \l__cn_vertical_textbox_y_tl,
    y .initial:n = 0pt,
    background-color .tl_set:N = \l__cn_vertical_textbox_bg_color_tl,
    background-color .initial:n = {},
    font-color .tl_set:N = \l__cn_vertical_textbox_font_color_tl,
    font-color .initial:n = {},
    font-size .tl_set:N = \l__cn_vertical_textbox_font_size_tl,
    font-size .initial:n = {},
    grid-width .tl_set:N = \l__cn_vertical_textbox_inner_gw_tl,
    grid-height .tl_set:N = \l__cn_vertical_textbox_inner_gh_tl,
  }

\NewDocumentCommand{\TextBox}{ O{} +m }
  {
    \group_begin:
    % Inherit debug setting from global environment
    \bool_set_eq:NN \l__cn_vertical_textbox_debug_bool \g_cnv_debug_bool
    \keys_set:nn { vertical / textbox } { #1 }
    % Width is always 1 for outer layout (textbox occupies 1 column externally)
    \setluatexattribute\cnverticaltextboxwidth{1}
    \setluatexattribute\cnverticaltextboxheight{\l__cn_vertical_textbox_height_int}
    
    % Calculate inner grid dimensions (hybrid approach)
    % If inner-grid-width not specified, auto-calculate: outer_width / n_cols
    \tl_if_empty:NTF \l__cn_vertical_textbox_inner_gw_tl
      {
        \int_compare:nNnTF \l__cn_vertical_textbox_n_cols_int > 0
          { \dim_set:Nn \l__cn_vertical_textbox_inner_gw_dim { \l__cn_vertical_grid_width_tl / \l__cn_vertical_textbox_n_cols_int } }
          { \dim_set:Nn \l__cn_vertical_textbox_inner_gw_dim { \l__cn_vertical_grid_width_tl } }
      }
      { \dim_set:Nn \l__cn_vertical_textbox_inner_gw_dim { \l__cn_vertical_textbox_inner_gw_tl } }
    % If inner-grid-height not specified, user outer grid-height directly (for alignment)
    \tl_if_empty:NTF \l__cn_vertical_textbox_inner_gh_tl
      { \dim_set:Nn \l__cn_vertical_textbox_inner_gh_dim { \l__cn_vertical_grid_height_tl } }
      { \dim_set:Nn \l__cn_vertical_textbox_inner_gh_dim { \l__cn_vertical_textbox_inner_gh_tl } }
    
    % Capture content in a box and process it via Lua for inner verticality
    \setbox0=\vbox{
      \setluatexattribute\cnverticaltextboxwidth{0}
      \setluatexattribute\cnverticaltextboxheight{0}
      % Reset paragraph spacing to prevent unwanted glues in grid layout
      \parindent=0pt
      \parskip=0pt
      \topskip=0pt
      % Scale font size to match the inner grid width, but slightly smaller (0.85x) to avoid crowding
      % Scale font size
      \tl_if_empty:NTF \l__cn_vertical_textbox_font_size_tl
        {
          % Default auto-scaling for multi-column boxes
          \int_compare:nNnT \l__cn_vertical_textbox_n_cols_int > 1
            {
              \fontsize{\dim_eval:n { 0.85 \l__cn_vertical_textbox_inner_gw_dim }}{\l__cn_vertical_textbox_inner_gw_dim}\selectfont
            }
        }
        {
          % User-specified font size
          \fontsize{\l__cn_vertical_textbox_font_size_tl}{\l__cn_vertical_textbox_font_size_tl}\selectfont
        }
      % Inner line length should be the grid height of the block
      \hsize=\dim_eval:n { \l__cn_vertical_textbox_inner_gh_dim * \int_use:N \l__cn_vertical_textbox_height_int }
      #2
    }
    
    % Expand variables to ensure they contain the color string, not a macro name
    % Use o-expansion to preserve spaces (standard x-expansion strips them in ExplSyntax)
    \tl_set:No \l__cn_vertical_textbox_bg_color_tl { \l__cn_vertical_textbox_bg_color_tl }
    \tl_set:No \l__cn_vertical_textbox_font_color_tl { \l__cn_vertical_textbox_font_color_tl }

    % Convert background color to RGB
    \tl_if_in:NnTF \l__cn_vertical_textbox_bg_color_tl { , }
      { \tl_set_eq:NN \l__cn_vertical_textbox_bg_rgb_tl \l__cn_vertical_textbox_bg_color_tl }
      {
        \tl_if_in:NnTF \l__cn_vertical_textbox_bg_color_tl { ~ }
          { \tl_set_eq:NN \l__cn_vertical_textbox_bg_rgb_tl \l__cn_vertical_textbox_bg_color_tl }
          {
             \cs_if_exist:cTF { color @ \l__cn_vertical_textbox_bg_color_tl }
             {
               \exp_args:NV \extractcolorspec \l__cn_vertical_textbox_bg_color_tl \l_tmpa_tl
               \exp_args:NNx \convertcolorspec \l_tmpa_tl { rgb } \l_tmpb_tl
               \tl_set:Nx \l__cn_vertical_textbox_bg_rgb_tl { \l_tmpb_tl }
             }
             { \tl_set_eq:NN \l__cn_vertical_textbox_bg_rgb_tl \l__cn_vertical_textbox_bg_color_tl }
          }
      }
    \tl_replace_all:Nnn \l__cn_vertical_textbox_bg_rgb_tl { , } { ~ }

    % Convert font color to RGB
    \tl_if_in:NnTF \l__cn_vertical_textbox_font_color_tl { , }
      { \tl_set_eq:NN \l__cn_vertical_textbox_font_rgb_tl \l__cn_vertical_textbox_font_color_tl }
      {
        \tl_if_in:NnTF \l__cn_vertical_textbox_font_color_tl { ~ }
          { \tl_set_eq:NN \l__cn_vertical_textbox_font_rgb_tl \l__cn_vertical_textbox_font_color_tl }
          {
             \cs_if_exist:cTF { color @ \l__cn_vertical_textbox_font_color_tl }
             {
               \exp_args:NV \extractcolorspec \l__cn_vertical_textbox_font_color_tl \l_tmpa_tl
               \exp_args:NNx \convertcolorspec \l_tmpa_tl { rgb } \l_tmpb_tl
               \tl_set:Nx \l__cn_vertical_textbox_font_rgb_tl { \l_tmpb_tl }
             }
             { \tl_set_eq:NN \l__cn_vertical_textbox_font_rgb_tl \l__cn_vertical_textbox_font_color_tl }
          }
      }
    \tl_replace_all:Nnn \l__cn_vertical_textbox_font_rgb_tl { , } { ~ }

    \directlua{vertical_textbox.process_inner_box(0, {
      n_cols = \int_use:N \l__cn_vertical_textbox_n_cols_int, 
      height = \int_use:N \l__cn_vertical_textbox_height_int, 
      grid_width = "\dim_to_decimal:n { \l__cn_vertical_textbox_inner_gw_dim } pt", 
      grid_height = "\dim_to_decimal:n { \l__cn_vertical_textbox_inner_gh_dim } pt", 
      box_align = "\luaescapestring{\tl_use:N \l__cn_vertical_textbox_box_align_tl}", 
      column_aligns = "\luaescapestring{\tl_use:N \l__cn_vertical_textbox_col_align_tl}",
      debug = "\bool_if:NTF \l__cn_vertical_textbox_debug_bool {true}{false}",
      border = "\bool_if:NTF \l__cn_vertical_textbox_border_bool {true}{false}",
      background_color = [=[\l__cn_vertical_textbox_bg_rgb_tl]=],
      font_color = [=[\l__cn_vertical_textbox_font_rgb_tl]=],
      font_size = [=[\luaescapestring{\l__cn_vertical_textbox_font_size_tl}]=]
    })}
    \setluatexattribute\cnverticaltextboxwidth{0}
    \setluatexattribute\cnverticaltextboxheight{0}
    \bool_if:NTF \l__cn_vertical_textbox_floating_bool
      {
        \directlua{vertical_textbox.register_floating_box(0, {
          x = [=[\luaescapestring{\l__cn_vertical_textbox_x_tl}]=],
          y = [=[\luaescapestring{\l__cn_vertical_textbox_y_tl}]=]
        })}
      }
      {
        \leavevmode\box0
      }
    \group_end:
  }

% TextFlow Command (双行小注/夹注)
% Syntax: \TextFlow{content}
\makeatletter
\NewDocumentCommand{\TextFlow}{ +m }
  {
    \group_begin:
    
    % Get jiazhu font size: default to 70% of current font size, user can override with explicit size (must include unit like "14pt")
    % Expand the tl first to check if it's really blank
    \tl_set:Nx \l_tmpb_tl { \l__cn_vertical_jiazhu_size_tl }
    \tl_if_empty:NTF \l_tmpb_tl
      {
        % Default: 0.7 * current font size
        \tl_set:Nx \l_tmpa_tl { \fp_eval:n { 0.7 * \f@size } pt }
      }
      {
        % User specified size (must include unit like "14pt")
        \tl_set_eq:NN \l_tmpa_tl \l_tmpb_tl
      }
    % Capture attribute values using edef to avoid register limitations
    \edef\savedindent{\the\cnverticalindent}
    \edef\savedblockid{\the\cnverticalblockid}
    \edef\savedfirst{\the\cnverticalfirstindent}
    \edef\savedright{\the\cnverticalrightindent}
    
    \fontsize{\l_tmpa_tl}{\l_tmpa_tl}\selectfont
    
    % CRITICAL: Set jiazhu attribute AFTER selectfont
    \setluatexattribute\cnverticaljiazhu{1}

    % Restore
    \cnverticalindent = \savedindent\relax
    \cnverticalblockid = \savedblockid\relax
    \cnverticalfirstindent = \savedfirst\relax
    \cnverticalrightindent = \savedright\relax
    
    #1
    \group_end:
  }
\makeatother

% Space Command - inserts a space that occupies grid cells in vertical layout
% In CJK text, normal ASCII spaces are often absorbed; use this for explicit spacing
% Syntax: \Space or \Space[width]
% Default width is 1em (one grid cell)
\NewDocumentCommand{\Space}{ O{1em} }
  {
    \regex_match:nnTF { ^\d+$ } { #1 }
      { \hspace{\dim_eval:n { \l__cn_vertical_grid_height_tl * #1 }} }
      { \hspace{#1} }
  }

% Paragraph Environment
% Supports block indentation with first-line/hanging indent differentiation
% Syntax: \begin{Paragraph}[indent=2, first-indent=0, bottom-indent=1]
\newluatexattribute\cnverticalblockid
\newluatexattribute\cnverticalfirstindent

\newcounter{cnverticalblockid}
\setcounter{cnverticalblockid}{0}

\keys_define:nn { vertical / paragraph }
  {
    indent .int_set:N = \l__cn_vertical_para_indent_int,
    indent .initial:n = 0,
    first-indent .int_set:N = \l__cn_vertical_para_first_indent_int,
    first-indent .initial:n = -1, % -1 means use indent value
    bottom-indent .int_set:N = \l__cn_vertical_para_bottom_indent_int,
    bottom-indent .initial:n = 0,
  }

\NewDocumentEnvironment{Paragraph}{ O{} }
  {
    \par
    \stepcounter{cnverticalblockid}
    \keys_set:nn { vertical / paragraph } { #1 }
    
    % Set Block ID
    \setluatexattribute\cnverticalblockid{\value{cnverticalblockid}}
    
    % Set Base Indent (also serves as Hanging Indent)
    \setluatexattribute\cnverticalindent{\l__cn_vertical_para_indent_int}
    
    % Set First Indent
    \int_compare:nNnTF \l__cn_vertical_para_first_indent_int = {-1}
      { \setluatexattribute\cnverticalfirstindent{\l__cn_vertical_para_indent_int} }
      { \setluatexattribute\cnverticalfirstindent{\l__cn_vertical_para_first_indent_int} }
      
    % Set Bottom Indent
    \setluatexattribute\cnverticalrightindent{\l__cn_vertical_para_bottom_indent_int}
  }
  {
    % Reset indent attributes to default BEFORE \par
    % so subsequent content won't inherit paragraph's indent
    \setluatexattribute\cnverticalindent{0}
    \setluatexattribute\cnverticalfirstindent{-1}
    \setluatexattribute\cnverticalrightindent{0}
    \par
  }

\ExplSyntaxOff

% ============================================================================
% Command Aliases (Defined outside ExplSyntax for CJK support)
% ============================================================================
\NewCommandCopy{\竖排}{\VerticalRTT}
\NewCommandCopy{\文本框}{\TextBox}
\NewDocumentCommand{\填充文本框}{ O{1} +m }
  {%
    \TextBox[height=#1, box-align=fill]{#2}%
  }
\NewCommandCopy{\文本流}{\TextFlow}
\NewCommandCopy{\空格}{\Space}
\NewCommandCopy{\段落}{\Paragraph}
\NewCommandCopy{\CePi}{\SideNode}
\NewCommandCopy{\侧批}{\SideNode}
\NewCommandCopy{\批注}{\PiZhu}

\directlua{
  _G.vertical.debug.verbose_log = true
  _G.vertical.debug.enabled = false
}
\endinput
