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

local text_module = {}

local element
local ir_node
local output
local util

local using_luatex, kpse = pcall(require, "kpse")
if using_luatex then
 element = require("citeproc-element")
 ir_node = require("citeproc-ir-node")
 output = require("citeproc-output")
 util = require("citeproc-util")
else
 element = require("citeproc.element")
 ir_node = require("citeproc.ir-node")
 output = require("citeproc.output")
 util = require("citeproc.util")
end

local Element = element.Element
local Rendered = ir_node.Rendered
local SeqIr = ir_node.SeqIr
local YearSuffix = ir_node.YearSuffix
local GroupVar = ir_node.GroupVar
local PlainText = output.PlainText
local Linked = output.Linked


-- [Text](https://docs.citationstyles.org/en/stable/specification.html#text)
---@class Text: Element
---@field variable string?
---@field form string?
---@field macro string?
---@field term string?
---@field plural string?
---@field value string?
---@field prefix string?
---@field suffix string?
---@field display string?
---@field quotes boolean?
---@field strip_periods string?
---@field text_case string?
local Text = Element:derive("text", {
 -- Default attributes
 variable = nil,
 form = "long",
 macro = nil,
 term = nil,
 plural = false,
 value = nil,
 -- Style behavior
 formatting = nil,
 affixes = nil,
 display = nil,
 quotes = false,
 strip_periods = false,
 text_case = nil,
})

function Text:from_node(node)
 local o = Text:new()
 o:set_attribute(node, "variable")
 o:set_attribute(node, "form")
 o:set_attribute(node, "macro")
 o:set_attribute(node, "term")
 o:set_bool_attribute(node, "plural")
 o:set_attribute(node, "value")

 o:set_formatting_attributes(node)
 o:set_affixes_attributes(node)
 o:set_display_attribute(node)
 o:set_quotes_attribute(node)
 o:set_strip_periods_attribute(node)
 o:set_text_case_attribute(node)
 -- `apa-annotated-bibliography.csl` uses `display="block"` for annotation.
 -- Use display="indent" instead
 if o.display == "block" and (o.variable == "note" or o.variable == "abstract") then
   o.display = "indent"
 end
 return o
end

function Text:build_ir(engine, state, context)
 local ir = nil
 if self.variable then
   ir = self:build_variable_ir(engine, state, context)
 elseif self.macro then
   ir = self:build_macro_ir(engine, state, context)
 elseif self.term then
   ir = self:build_term_ir(engine, state, context)
 elseif self.value then
   ir = self:build_value_ir(engine, state, context)
 end
 return ir
end

function Text:build_variable_ir(engine, state, context)
 ---@type string
 local variable = self.variable
 local text

 if variable == "year-suffix" then
   return self:build_year_suffix_ir(engine, state, context)
 elseif variable == "citation-label" then
   return self:build_citation_label_ir(engine, state, context)
 end

 if not state.suppressed[variable] then
   text = context:get_variable(variable, self.form)
   ---@case text string | number?
 end

 if not text or text == "" then
   local ir = Rendered:new({}, self)
   ir.group_var = GroupVar.Missing
   return ir
 end
 if type(text) == "number" then
   text = tostring(text)
 end
 if util.variable_types[variable] == "number" then
   text = self:format_number(text, variable, "numeric", context)
 end

 local inlines
 -- if not engine.opt then
 --   print(debug.traceback())
 -- end
 -- if engine.opt.wrap_url_and_doi and (variable == "URL" or variable == "DOI" or
 --     variable == "PMID" or variable == "PMID") then
 if variable == "URL" or variable == "DOI" or variable == "PMID" or variable == "PMCID" then
   inlines = self:render_linked(engine, state, context, variable, text)
 else
   inlines = self:render_text_inlines(text, context)
 end

 local ir = Rendered:new(inlines, self)
 ir.group_var = GroupVar.Important

 if variable == "citation-number" then
   ir.citation_number = context.reference["citation-number"]
 end

 -- Suppress substituted name variable
 if state.name_override and not context.sort_key then
   state.suppressed[variable] = true
 end

 return ir
end

function Text:render_linked(engine, state, context, variable, text)
 local href
 local url_prefix = false  -- The prefix is used as part of the URL.
 if variable == "URL" then
   href = text
 elseif self.affixes and self.affixes.prefix and string.match(self.affixes.prefix, "https?://") then
   text = self.affixes.prefix .. text
   href = text
   url_prefix = true
 elseif variable == "DOI" then
   href = "https://doi.org/" .. text
 elseif variable == "PMID" then
   href = "https://www.ncbi.nlm.nih.gov/pubmed/" .. text
 elseif variable == "PMCID" then
   href = "https://www.ncbi.nlm.nih.gov/pmc/articles/" .. text
 end

 local inlines = {Linked:new(text, href)}
 local output_format = context.format
 local localized_quotes = nil
 if self.quotes then
   localized_quotes = context:get_localized_quotes()
 end
 inlines = output_format:with_format(inlines, self.formatting)
 inlines = output_format:affixed_quoted(inlines, self.affixes, localized_quotes)
 if url_prefix then
   table.remove(inlines, 1)
 end
 return output_format:with_display(inlines, self.display)
end

function Text:build_year_suffix_ir(engine, state, context)
 local text = context:get_variable(self.variable, self.form)
 local group_var
 if text then
   group_var = GroupVar.Important
 else
   text = ""
   group_var = GroupVar.Missing
 end

 local ir = YearSuffix:new({PlainText:new(text)}, self)
 ir.group_var = group_var
 ir.affixes = util.clone(self.affixes)
 ir.display = self.display
 if self.display == "left-margin" then
   engine.registry.second_field_align = "flush"
 end
 ir.formatting = util.clone(self.formatting)
 if self.quotes then
   ir.quotes = context:get_localized_quotes()
 end
 return ir
end

function Text:build_citation_label_ir(engine, state, context)
 local text = context:get_variable(self.variable, self.form)
 local group_var
 if text then
   group_var = GroupVar.Important
 else
   text = ""
   group_var = GroupVar.Missing
 end

 local ir = Rendered:new({PlainText:new(text)}, self)
 -- The citation-label may have a year-suffix
 ir = SeqIr:new({ir}, self)
 ir.group_var = group_var
 ir.affixes = util.clone(self.affixes)
 ir.display = self.display
 ir.formatting = util.clone(self.formatting)
 if self.quotes then
   ir.quotes = context:get_localized_quotes()
 end

 ir.is_year = true
 return ir
end

function Text:build_macro_ir(engine, state, context)
 ---@type Macro
 local macro = context:get_macro(self.macro)
 if not macro then
   util.error(string.format("Macro '%s' not found", self.macro))
   return nil
 end
 state:push_macro(self.macro)
 local ir = macro:build_ir(engine, state, context)
 state:pop_macro(self.macro)
 if ir then
   ir.affixes = util.clone(self.affixes)
   ir.display = self.display
   ir.formatting = util.clone(self.formatting)
   if self.quotes then
     ir.quotes = context:get_localized_quotes()
   end
 end
 return ir
end

function Text:build_term_ir(engine, state, context)
 local str = context:get_simple_term(self.term, self.form, self.plural)
 if not str then
   return nil
 end
 local inlines = self:render_text_inlines(str, context)
 return Rendered:new(inlines, self)
end

function Text:build_value_ir(engine, state, context)
 local inlines = self:render_text_inlines(self.value, context)
 return Rendered:new(inlines, self)
end


text_module.Text = Text

return text_module