local M = {}
local xtpipeslib = require "make4ht-xtpipes"
local domfilter = require "make4ht-domfilter"


-- some elements need to be moved from the document flow to the document meta
local article_meta
local elements_to_move_to_meta = {}
local function move_to_meta(el)
 -- we don't move elements immediatelly, because it would prevent them from further
 -- processing in the filter. so we save them in an array, and move them once
 -- the full DOM was processed
 table.insert(elements_to_move_to_meta, el)
end

local elements_to_move_to_title = {}
local function move_to_title_group(el)
 -- there can be only one title and subtitle
 local name = el:get_element_name()
 if not elements_to_move_to_title[name] then
   elements_to_move_to_title[name] = el
 end
end

local elements_to_move_to_contribs = {}
local function move_to_contribs(el)
 table.insert(elements_to_move_to_contribs, el)
end



local function process_moves()
 if article_meta then
   if elements_to_move_to_title["article-title"]
     and #article_meta:query_selector("title-group") == 0 then -- don't move anything if user added title-group from a config file
     local title_group = article_meta:create_element("title-group")
     for _, name in ipairs{ "article-title", "subtitle" } do
       local v = elements_to_move_to_title[name]
       if v then
         title_group:add_child_node(v:copy_node())
         v:remove_node()
       end
     end
     article_meta:add_child_node(title_group, 1)
   end
   if #elements_to_move_to_contribs > 0 then
     local contrib_group = article_meta:create_element("contrib-group")
     for _, el in ipairs(elements_to_move_to_contribs) do
       contrib_group:add_child_node(el:copy_node())
       el:remove_node()
     end
     article_meta:add_child_node(contrib_group)
   end
   for _, el in ipairs(elements_to_move_to_meta) do
     -- move elemnt's copy, and remove the original
     article_meta:add_child_node(el:copy_node())
     el:remove_node()
   end
 end
end

local function has_no_text(el)
 -- detect if element contains only whitespace
 if el:get_text():match("^%s*$") then
   --- if it contains any elements, it has text
   for _, child in ipairs(el:get_children()) do
     if child:is_element() then return false end
   end
   return true
 end
 return false
end

local function is_xref_id(el)
 return el:get_element_name() == "xref" and el:get_attribute("id") and el:get_attribute("rid") == nil and has_no_text(el)
end
-- set id to parent element for <xref> that contain only id
local function xref_to_id(el)
 local parent = el:get_parent()
 -- set id only if it doesn't exist yet
 if parent:get_attribute("id") == nil then
   parent:set_attribute("id", el:get_attribute("id"))
   el:remove_node()
 end
end

local function make_text(el)
 local text = el:get_text():gsub("^%s*", ""):gsub("%s*$", "")
 local text_el = el:create_text_node(text)
 el._children = {text_el}
end

local function is_empty_par(el)
 return el:get_element_name() == "p" and has_no_text(el)
end

local function handle_links(el, params)
 -- we must distinguish between internal links in the document, and external links
 -- to websites etc. these needs to be changed to the <ext-link> element.
 local link = el:get_attribute("rid")
 if link then
   -- try to remove \jobname.xml from the beginning of the link
   -- if the rest starts with #, then it is an internal link
   local local_link = link:gsub("^" .. params.input .. ".xml", "")
   if local_link:match("^%#") then
     el:set_attribute("rid", local_link)
   else
     -- change element to ext-link for extenal links
     el._name = "ext-link"
     el:set_attribute("rid", nil)
     el:set_attribute("xlink:href", link)
   end
 end
end

local function handle_maketitle(el)
 -- <maketitle> is special element produced by TeX4ht from LaTeX's \maketitle
 -- we need to pick interesting info from there, and move it to the header
 local function is_empty(selector)
   return #article_meta:query_selector(selector) == 0
 end
 -- move <aff> to <contrib>
 local affiliations = {}
 for _, aff in ipairs(el:query_selector("aff")) do
   local id = aff:get_attribute("id")
   if id then
     for _,mark in ipairs(aff:query_selector("affmark")) do mark:remove_node() end
     affiliations[id] = aff:copy_node()
   end
 end
 if is_empty("contrib") then
   for _, contrib in ipairs(el:query_selector("contrib")) do
     for _, affref in ipairs(contrib:query_selector("affref")) do
       local id = affref:get_attribute("rid") or ""
       -- we no longer need this node
       affref:remove_node()
       local linked_affiliation = affiliations[id]
       if linked_affiliation then
         contrib:add_child_node(linked_affiliation)
       end
     end
     for _, string_name in ipairs(contrib:query_selector("string-name")) do
       make_text(string_name)
     end
     move_to_contribs(contrib:copy_node())
     -- we need to remove it from here, even though we remove <maketitle> later
     -- we got doubgle contributors without that
     contrib:remove_node()
   end
 end
 if is_empty("pub-date") then
   for _, date in ipairs(el:query_selector("date")) do
     date._name = "pub-date"
     for _, s in ipairs(date:query_selector("string-date")) do
       make_text(s)
     end
     move_to_meta(date:copy_node())
   end
 end
 el:remove_node()
end





function M.prepare_parameters(settings, extensions)
 settings.tex4ht_sty_par = settings.tex4ht_sty_par ..",jats"
 settings = mkutils.extensions_prepare_parameters(extensions, settings)
 return settings
end

function M.prepare_extensions(extensions)
 return extensions
end

function M.modify_build(make)
 filter_settings("joincharacters", {charclasses = {italic=true, bold=true}})

 local process =  domfilter {
   function(dom, params)
     dom:traverse_elements(function(el)
       -- some elements need special treatment
       local el_name = el:get_element_name()
       if is_xref_id(el) then
         xref_to_id(el)
       elseif el_name == "article-meta" then
         -- save article-meta element for further processig
         article_meta = el
       elseif el_name == "article-title" then
         move_to_title_group(el)
       elseif el_name == "subtitle" then
         move_to_title_group(el)
       elseif el_name == "abstract" then
         move_to_meta(el)
       elseif el_name == "string-name" then
         make_text(el)
       elseif el_name == "contrib" then
         move_to_contribs(el)
       elseif is_empty_par(el) then
         -- remove empty paragraphs
         el:remove_node()
       elseif el_name == "xref" then
         handle_links(el, params)
       elseif el_name == "maketitle" then
         handle_maketitle(el)
       elseif el_name == "div" and el:get_attribute("class") == "maketitle" then
         el:remove_node()
       end

     end)
     -- move elements that are marked for move
     process_moves()
     return dom
   end, "joincharacters","mathmlfixes", "tablerows","booktabs"
 }
 local charclasses = {["mml:mi"] = true, ["mml:mn"] = true , italic = true, bold=true, roman = true, ["mml:mtext"] = true, mi=true, mn=true}
 make:match("xml$", process, {charclasses = charclasses})
 return make
end

return M