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
    print(parent:serialize())
    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
      -- the rid attribute should not start with #, it must be the exact ID used in the linked element
      local_link = local_link:gsub("^%#", "")
      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
