-- Select Latin companions before LuaTeX hyphenates and ligatures text.
-- Part of latexbangla, LPPL 1.3c or later; copyright 2026 Adib Hasan.
-- TeX owns font selection and scoping; this module only handles text nodes.

local glyph_id = node.id("glyph")
local math_id = node.id("math")
local disc_id = node.id("disc")

-- Match the Latin Unicode blocks enabled by ucharclasses in the XeTeX backend.
-- Joiners and Bengali marks are deliberately excluded: changing their font
-- independently would split the clusters HarfBuzz needs to shape.
local latin_characters = {}
local latin_blocks = {
    {0x0020, 0x024F}, -- Basic Latin, Latin-1 Supplement, Latin Extended A/B.
    {0x0300, 0x036F}, -- Combining accents used by decomposed Latin letters.
    {0x1E00, 0x1EFF}, -- Latin Extended Additional.
    {0x2C60, 0x2C7F}, -- Latin Extended C.
    {0xA720, 0xA7FF}, -- Latin Extended D.
    {0xAB30, 0xAB6F}, -- Latin Extended E.
    {0xFB00, 0xFB4F}, -- Alphabetic Presentation Forms.
    {0x10780, 0x107BF}, -- Latin Extended F.
    {0x1DF00, 0x1DFFF}, -- Latin Extended G.
}
for _, block in ipairs(latin_blocks) do
    for codepoint = block[1], block[2] do latin_characters[codepoint] = true end
end
for _, codepoint in ipairs({0x2013, 0x2014, 0x2018, 0x2019, 0x201C, 0x201D, 0x2026}) do
    latin_characters[codepoint] = true
end

-- Leave glue alone. As with XeTeX's inter-character transitions, ordinary
-- spaces are made by the enclosing text font. This also preserves WordSpace,
-- sentence spacing, explicit skips and glue inserted by other packages.
return function(latin_font_attribute)
    local function switch_text_fonts(head)
        local in_math = false
        for current in node.traverse(head) do
            if current.id == math_id then
                in_math = current.subtype == 0
            elseif not in_math then
                if current.id == glyph_id then
                    local target = node.has_attribute(current, latin_font_attribute)
                    -- LuaTeX adds 256 to protected glyph subtypes. Those glyphs
                    -- have already been processed and must keep their font.
                    if target and current.subtype < 256 and latin_characters[current.char] then
                        current.font = target
                    end
                elseif current.id == disc_id then
                    -- Author-supplied discretionary text needs its companion
                    -- too. Automatic hyphens are created afterwards, using the
                    -- correct surrounding font, so Bangla clusters stay intact.
                    switch_text_fonts(current.pre)
                    switch_text_fonts(current.post)
                    switch_text_fonts(current.replace)
                end
            end
        end
    end
    -- The pre-shaping callback is too late: native TeX ligatures have already
    -- run, and changing an automatic Bangla hyphen's font breaks HarfBuzz's
    -- contextual shaping across that discretionary. Select fonts before both
    -- operations, then delegate hyphenation to LuaTeX's standard algorithm.
    -- This callback also runs for text packed into horizontal boxes.
    luatexbase.add_to_callback("hyphenate", function(head, tail)
        switch_text_fonts(head)
        lang.hyphenate(head, tail)
    end, "latexbangla.font-switching")
end
