-- Handle frontmatter stuff for .docx, html, and typst formats
if FORMAT:match 'latex' then
  return
end


local andreplacement = "and"



local List = require 'pandoc.List'
local utilsapa = require("utilsapa")
local stringify = utilsapa.stringify

local function get_and(m)
  if m.language and m.language["citation-last-author-separator"] then
    andreplacement = stringify(m.language["citation-last-author-separator"])
  end
end

---http://lua-users.org/wiki/StringRecipes
local function ends_with(str, ending)
  return string.sub(str.text, -1) == ending
end

local function file_exists(name)
  local f = io.open(name, 'r')
  if f ~= nil then
    io.close(f)
    return true
  else
    return false
  end
end

-- Check if meta is present or if it has length of 0
local function chkmeta(meta_item)
  ispresent = false
  if meta_item then
    if #meta_item > 0 then
      ispresent = true
    end
  end
  return ispresent
end

-- Convert list to string with oxford comma
local function oxfordcommalister(lists)
  local lastsep = pandoc.Str(", " .. andreplacement .. " ")
  local sep = pandoc.Str(", ")

  if #lists == 2 then
    lastsep = pandoc.Str(" " .. andreplacement .. " ")
  end

  local result = List:new {}
  for i, v in ipairs(lists) do
    if i > 1 then
      if i == #lists then
        result:extend(List:new { lastsep })
      else
        result:extend(List:new { sep })
      end
    end
    result:extend(List:new { v })
  end

  return result
end




local get_author_paragraph = function(authors, different)
  local authordisplay = List:new {}
  local superii = ""
  local sep = ", "

  for i, a in ipairs(authors) do
    if i == 1 then
      sep = ""
    elseif i == #authors then
      if i == 2 then
        sep = " " .. andreplacement .. " "
      else
        sep = ", " .. andreplacement .. " "
      end
    else
      sep = ", "
    end

    authordisplay:extend({ pandoc.Str(sep .. stringify(a.apaauthordisplay)) })

    superii = ""
    if a.affiliations then
      for j, aff in ipairs(a.affiliations) do
        if j > 1 then
          superii = superii .. ","
        end
        superii = superii .. aff.number
      end
    end

    if different then
      authordisplay:extend({ pandoc.Superscript(superii) })
    end
  end
  return pandoc.Para(authordisplay)
end


local extend_paragraph = function(para, meta_item, sep)
  sep = sep or pandoc.Space()



  if meta_item and #meta_item > 0 then
    if #para.content > 1 then
      para.content:extend({ sep })
    end
    para.content:extend(meta_item)
  end
  return para
end


return {
  { Meta = get_and },
  {
    Pandoc = function(doc)
      local body = List:new {}
      local meta = doc.meta

      local documenttitle = ""
      local intabovetitle = 2
      local newline = pandoc.LineBreak()
      if FORMAT:match 'docx' then
        newline = pandoc.SoftBreak()
      end

      if meta.apatitledisplay then
        if meta["blank-lines-above-title"] and #meta["blank-lines-above-title"] > 0 then
          local possiblenumber = stringify(meta["blank-lines-above-title"])
          if type(possiblenumber) == "number" then
            local intnumber = tonumber(possiblenumber) * 1
            intabovetitle = math.floor(intnumber) or 2
          else
            intabovetitle = 2
          end
        end
        for i = 1, intabovetitle do
          body:extend({ newline })
        end
        documenttitle = pandoc.Header(1, meta.apatitledisplay)
        documenttitle.classes = { "title", "unnumbered", "unlisted" }
        documenttitle.identifier = "title"
        if not meta["suppress-title"] then
          body:extend({ documenttitle })
        end
      end

      local byauthor = meta["by-author"]
      local affiliations = meta["affiliations"]

      local authornote = false
      if meta["author-note"] and not meta["suppress-author-note"] then
        authornote = meta["author-note"]
      end

      local mask = false

      if meta["mask"] and stringify(meta["mask"]) == "true" then
        mask = true
      end

      local affilations_different = meta.affiliationsdifferent

      if meta["suppress-affiliation"] then
        affilations_different = false
      end

      local affiliations_str = List()

      local authordiv = pandoc.Div({})
      if not meta["suppress-author"] and byauthor then
        authordiv = pandoc.Div({
          newline,
          get_author_paragraph(byauthor, affilations_different)
        })
      end

      authordiv.classes:insert("Author")
      if affiliations then
        if byauthor then
          for i, a in ipairs(affiliations) do
            affiliations_str = List()

            mysep = pandoc.Str("")

            if affilations_different and not meta["suppress-author"] then
              affiliations_str:extend({ pandoc.Superscript(stringify(a.number)) })
            end

            if chkmeta(a.group) then
              affiliations_str:extend(a.group)
              mysep = pandoc.Str(", ")
            end

            if chkmeta(a.department) then
              affiliations_str:extend({ mysep })
              affiliations_str:extend(a.department)
              mysep = pandoc.Str(", ")
            end

            if chkmeta(a.name) then
              affiliations_str:extend({ mysep })
              affiliations_str:extend(a.name)
              mysep = pandoc.Str(", ")
            end

            if not (chkmeta(a.group) or chkmeta(a.department) or chkmeta(a.name)) then
              mysep = pandoc.Str("")
              if chkmeta(a.city) then
                affiliations_str:extend(a.city)
                mysep = pandoc.Str(", ")
              end
              if chkmeta(a.region) then
                affiliations_str:extend({ mysep })
                affiliations_str:extend(a.region)
              end
            end
            if not meta["suppress-affiliation"] then
              authordiv.content:extend({ pandoc.Para(pandoc.Inlines(affiliations_str)) })
            end
          end
        end
      end

      if not mask then
        body:extend({ authordiv })
      end

      if meta["draft-date"] then
        draftdate = os.date("%B %d, %Y")
        if type(meta["draft-date"]) == "table" then
          draftdate = meta["draft-date"]
        end
        draftdatediv = pandoc.Div({
          pandoc.Para(draftdate)
        })
        draftdatediv.classes:insert("Author")
        body:extend({ draftdatediv })
      end

      local authornoteheadertext = "Author Note"
      if meta.language and meta.language["title-block-author-note"] then
        authornoteheadertext = meta.language["title-block-author-note"]
      end

      local emailword = "Email"
      if meta.language and meta.language.email then
        emailword = stringify(meta.language.email)
      end

      local authornoteheader = pandoc.Header(1, authornoteheadertext)
      authornoteheader.classes = { "unnumbered", "unlisted", "AuthorNote" }
      authornoteheader.identifier = "author-note"

      local intabovenote = 2
      local possiblenumber = 2

      if not mask and not meta["suppress-author-note"] and (byauthor or authornote) then
        if authornote then
          if authornote["blank-lines-above-author-note"] and #authornote["blank-lines-above-author-note"] > 0 then
            possiblenumber = stringify(authornote["blank-lines-above-author-note"])
            intabovenote = math.floor(tonumber(possiblenumber)) or 2
          end
        end

        if meta["blank-lines-above-author-note"] and #meta["blank-lines-above-author-note"] > 0 then
          possiblenumber = stringify(meta["blank-lines-above-author-note"])
          intabovenote = math.floor(tonumber(possiblenumber)) or 2
        end

        for i = 1, intabovenote do
          body:extend({ newline })
        end

        body:extend({ authornoteheader })
      end

      local img

      if byauthor then
        for i, a in ipairs(byauthor) do
          if a.orcid then
            local orcidfile = "_extensions/wjschne/apaquarto/ORCID-iD_icon-vector.svg"
            if not file_exists(orcidfile) then
              orcidfile = "_extensions/apaquarto/ORCID-iD_icon-vector.svg"
            end
            img = pandoc.Image("Orcid ID Logo: A green circle with white letters ID", orcidfile)
            img.attr = pandoc.Attr('orcid', { 'img-fluid' }, { width = '4.23mm' })
            pp = pandoc.Para(pandoc.Str(""))
            pp.content:extend(a.apaauthordisplay)
            pp.content:extend({ pandoc.Space(), img })
            pp.content:extend({ pandoc.Space(), pandoc.Link("https://orcid.org/" .. stringify(a.orcid),
              "https://orcid.org/" .. stringify(a.orcid)) })

            if not mask and not meta["suppress-orcid"] then
              body:extend({ pp })
            end
          end
        end
      end

      if authornote then
        if authornote["status-changes"] then
          local second_paragraph = pandoc.Para(pandoc.Str(""))
          second_paragraph = extend_paragraph(second_paragraph,
            authornote["status-changes"]["affiliation-change"] or authornote["affiliation-change"] or
            meta["affiliation-change"])
          second_paragraph = extend_paragraph(second_paragraph,
            authornote["status-changes"].deceased or authornote.deceased or meta.deceased)

          if #second_paragraph.content > 1 then
            if not mask and not meta["suppress-status-change-paragraph"] then
              body:extend({ second_paragraph })
            end
          end
        end

        if authornote.disclosures then
          local third_paragraph = pandoc.Para(pandoc.Str(""))

          third_paragraph = extend_paragraph(third_paragraph,
            authornote.disclosures["study-registration"] or authornote["study-registration"] or
            meta["study-registration"])
          third_paragraph = extend_paragraph(third_paragraph,
            authornote.disclosures["data-sharing"] or authornote["data-sharing"] or meta["data-sharing"])
          third_paragraph = extend_paragraph(third_paragraph,
            authornote.disclosures["related-report"] or authornote["related-report"] or meta["related-report"])
          third_paragraph = extend_paragraph(third_paragraph,
            authornote.disclosures["conflict-of-interest"] or authornote["conflict-of-interest"] or
            meta["conflict-of-interest"])
          third_paragraph = extend_paragraph(third_paragraph,
            authornote.disclosures["financial-support"] or authornote["financial-support"] or meta["financial-support"])
          third_paragraph = extend_paragraph(third_paragraph,
            authornote.disclosures.gratitude or authornote.gratitude or meta.gratitude)
          third_paragraph = extend_paragraph(third_paragraph,
            authornote.disclosures["authorship-agreements"] or authornote["authorship-agreements"] or
            meta["authorship-agreements"])

          if #third_paragraph.content > 1 then
            if not mask and not meta["suppress-disclosures-paragraph"] then
              body:extend({ third_paragraph })
            end
          end
        end
      end

      local credit_paragraph = pandoc.Para(pandoc.Str(""))

      if byauthor then
        for i, a in ipairs(byauthor) do
          if a.roles then
            credit_paragraph = extend_paragraph(credit_paragraph, { pandoc.Emph(a.apaauthordisplay) }, pandoc.Str(". "))
            credit_paragraph.content:extend({ pandoc.Strong(pandoc.Str(": ")) })
            local rolelist = {}
            for j, role in ipairs(a.roles) do
              if role.role == "Writing - original draft" or role.role == "writing - original draft" then
                role["vocab-term"] = "writing – original draft"
              end
              if role.role == "Writing - reviewing & editing" or role.role == "writing - reviewing & editing" then
                role["vocab-term"] = "Writing – reviewing & editing"
              end

              if role["vocab-term"] then
                role.display = role["vocab-term"]
              else
                role.display = role.role
              end

              if role["degree-of-contribution"] then
                role.display = role.display .. " (" .. role["degree-of-contribution"] .. ")"
              end
              table.insert(rolelist, pandoc.Str(role.display))
            end
            credit_paragraph.content:extend(oxfordcommalister(rolelist))
          end
        end
      end

      if #credit_paragraph.content > 1 then
        local authorroleintroduction = pandoc.Str(
        "Author roles were classified using the Contributor Role Taxonomy (CRediT; https://credit.niso.org/) as follows:")
        if meta.language and meta.language["title-block-role-introduction"] then
          authorroleintroduction = meta.language["title-block-role-introduction"]
          if type(authorroleintroduction) == "string" then
            authorroleintroduction = pandoc.Inlines(authorroleintroduction)
          end
        end

        credit_paragraph.content:insert(1, pandoc.Space())
        for i, j in pairs(authorroleintroduction) do
          credit_paragraph.content:insert(i, j)
        end
        if not mask and not meta["suppress-credit-statement"] then
          body:extend({ credit_paragraph })
        end
      end

      local corresponding_paragraph = pandoc.Para(pandoc.Str(""))
      local check_corresponding = false
      if meta["author-note"] and meta["author-note"]["correspondence-note"] then
        corresponding_paragraph.content:extend(meta["author-note"]["correspondence-note"])
      else
        if byauthor then
          for i, a in ipairs(byauthor) do
            if a.attributes then
              if a.attributes.corresponding and stringify(a.attributes.corresponding) == "true" then
                if check_corresponding then
                  error("There can only be one author marked as the corresponding author. " ..
                  stringify(a.apaauthordisplay) .. " is the second author you have marked as the corresponding author.")
                end
                check_corresponding = true
                corresponding_paragraph.content:extend(a.apaauthordisplay)

                if a.affiliations then
                  local address = a.affiliations[1]
                  if not meta["suppress-corresponding-group"] then
                    corresponding_paragraph = extend_paragraph(corresponding_paragraph, address.group, pandoc.Str(", "))
                  end

                  if not meta["suppress-corresponding-department"] then
                    corresponding_paragraph = extend_paragraph(corresponding_paragraph, address.department,
                      pandoc.Str(", "))
                  end

                  if not meta["suppress-corresponding-affiliation-name"] then
                    corresponding_paragraph = extend_paragraph(corresponding_paragraph, address.name, pandoc.Str(", "))
                  end

                  if not meta["suppress-corresponding-address"] then
                    corresponding_paragraph = extend_paragraph(corresponding_paragraph, address.address, pandoc.Str(", "))
                  end

                  if not meta["suppress-corresponding-city"] then
                    corresponding_paragraph = extend_paragraph(corresponding_paragraph, address.city, pandoc.Str(", "))
                  end

                  if not meta["suppress-corresponding-region"] then
                    corresponding_paragraph = extend_paragraph(corresponding_paragraph, address.region, pandoc.Str(", "))
                  end

                  if not meta["suppress-corresponding-postal-code"] then
                    corresponding_paragraph = extend_paragraph(corresponding_paragraph, address["postal-code"])
                  end

                  if not meta["suppress-corresponding-country"] then
                    corresponding_paragraph = extend_paragraph(corresponding_paragraph, address.country, pandoc.Str(", "))
                  end

                  if not meta["suppress-corresponding-email"] then
                    if a.email then
                      corresponding_paragraph.content:extend({ pandoc.Str(", " .. emailword .. ":") })
                      corresponding_paragraph = extend_paragraph(corresponding_paragraph,
                        { pandoc.Link(stringify(a.email), "mailto:" .. stringify(a.email)) })
                    end
                  end
                end
              end
            end
          end
        end

        if #corresponding_paragraph.content > 1 then
          local correspondencenote = pandoc.Str("Correspondence concerning this article should be addressed to ")
          if meta.language and meta.language["title-block-correspondence-note"] then
            correspondencenote = meta.language["title-block-correspondence-note"]
            if type(correspondencenote) == "string" then
              correspondencenote = pandoc.Inlines(correspondencenote)
            end
          end
          corresponding_paragraph.content:insert(1, pandoc.Space())
          for i, j in pairs(correspondencenote) do
            corresponding_paragraph.content:insert(i, j)
          end
        end
      end

      if (not mask) and (not meta["suppress-corresponding-paragraph"]) then
        body:extend({ corresponding_paragraph })
      end

      if meta.apaabstract and #meta.apaabstract > 0 and not meta["suppress-abstract"] then
        local abstractheadertext = pandoc.Str("Abstract")
        if meta.language and meta.language["section-title-abstract"] then
          abstractheadertext = meta.language["section-title-abstract"]
        end
        local abstractheader = pandoc.Header(1, abstractheadertext)
        abstractheader.classes = { "unnumbered", "unlisted", "AuthorNote" }
        abstractheader.identifier = "abstract"
        if FORMAT:match 'docx' then
          body:extend({ pandoc.RawBlock('openxml', '<w:p><w:r><w:br w:type="page"/></w:r></w:p>') })
        end

        if FORMAT:match 'typst' then
          body:extend({ pandoc.RawBlock('typst', '#pagebreak()\n\n') })
        end

        if FORMAT:match 'html' then
          body:extend({ pandoc.RawBlock('html', '<br>') })
        end

        body:extend({ abstractheader })
        local abstract_paragraph = pandoc.Para(pandoc.Str(""))

        if pandoc.utils.type(meta.apaabstract) == "Inlines" then
          abstract_paragraph.content:extend(meta.apaabstract or meta.abstract)
          local abstractdiv = pandoc.Div(abstract_paragraph)
          abstractdiv.classes:insert("AbstractFirstParagraph")
          body:extend({ abstractdiv })
        end

        if pandoc.utils.type(meta.apaabstract) == "Blocks" then
          local abstractdiv = pandoc.Div({})
          local abstractfirstparagraphdiv = pandoc.Div({})
          local abstractlinecounter = 1
          if FORMAT == "typst" then
            abstractlinecounter = 2
          end
          meta.apaabstract:walk {
            LineBlock = function(lb)
              lb:walk {
                traverse = "topdown",
                Inlines = function(el)
                  local lbpara = pandoc.Para(el)

                  if abstractlinecounter == 1 then
                    abstractfirstparagraphdiv.content:extend({ lbpara })
                    abstractfirstparagraphdiv.classes:insert("AbstractFirstParagraph")
                  else
                    abstractdiv.content:extend({ lbpara })
                    if abstractlinecounter == 2 then
                      abstractdiv.classes:insert("Abstract")
                    end
                  end

                  abstractlinecounter = abstractlinecounter + 1
                  return el, false
                end
              }
            end
          }
          if abstractlinecounter > 1 then
            body:extend({ abstractfirstparagraphdiv })
          end

          if abstractlinecounter > 2 then
            body:extend({ abstractdiv })
          end
        end
      end

      if meta["impact-statement"] and #meta["impact-statement"] > 0 and not meta["suppress-impact-statement"] then
        local impactheadertext = pandoc.Str("Impact Statement")
        if meta.language and meta.language["title-impact-statement"] then
          impactheadertext = meta.language["title-impact-statement"]
        end
        local impactheader = pandoc.Header(1, impactheadertext)
        impactheader.classes = { "unnumbered", "unlisted", "AuthorNote" }
        impactheader.identifier = "impact"
        body:extend({ impactheader })
        local impact_paragraph = pandoc.Para(pandoc.Str(""))
        if pandoc.utils.type(meta["impact-statement"]) == "Inlines" then
          impact_paragraph.content:extend(meta["impact-statement"])
          local impactdiv = pandoc.Div(impact_paragraph)
          impactdiv.classes:insert("AbstractFirstParagraph")
          body:extend({ impactdiv })
        end
      end

      if meta.keywords and not meta["suppress-keywords"] then
        local keywordsword = pandoc.Str("Keywords")
        if meta.language and meta.language["title-block-keywords"] then
          keywordsword = stringify(meta.language["title-block-keywords"])
        end

        local keywords_paragraph = pandoc.Para({ pandoc.Emph(keywordsword), pandoc.Str(":") })

        if pandoc.utils.type(meta.keywords) == "Inlines" then
          keywords_paragraph.content:extend(meta.keywords)
        else
          for i, k in ipairs(meta.keywords) do
            if i == 1 then
              keywords_paragraph = extend_paragraph(keywords_paragraph, k)
            else
              keywords_paragraph = extend_paragraph(keywords_paragraph, k, pandoc.Str(", "))
            end
          end
        end

        body:extend({ keywords_paragraph })
      end

      if meta["word-count"] then
        local word_count_word = "Word Count"
        if meta.language and meta.language["title-block-word-count"] then
          word_count_word = stringify(meta.language["title-block-word-count"])
        end


        local word_count_paragraph = pandoc.Para({ pandoc.Emph(word_count_word), pandoc.Str(": " .. meta.wordn) })
        body:extend({ word_count_paragraph })
      end

      if FORMAT:match 'docx' then
        body:extend({ pandoc.RawBlock('openxml', '<w:p><w:r><w:br w:type="page"/></w:r></w:p>') })
      end

      if FORMAT:match 'typst' then
        local pg = '#pagebreak()\n\n'
        if meta['first-page'] then
          pg = '#counter(page).update(' .. stringify(meta['first-page']) .. ' - 1)\n #pagebreak()\n\n'
        end
        body:extend({ pandoc.RawBlock('typst', pg) })
      end



      if FORMAT:match 'html' then
        body:extend({ pandoc.RawBlock('html', '<br>') })
      end

      local myshorttitle = ""

      if meta["apatitle"] then
        myshorttitle = meta["apatitle"]
      end

      if meta["shorttitle"] and #meta["shorttitle"] > 0 then
        myshorttitle = meta["shorttitle"]
      end

      for i, v in ipairs(myshorttitle) do
        if v.t == "Str" then
          v.text = pandoc.text.upper(v.text)
        end
      end
      if not meta["suppress-short-title"] then
        meta.description = myshorttitle
      else
        meta.description = " "
      end

      if meta["suppress-title-page"] then
        body = List:new {}
      end

      if FORMAT:match 'typst' and PANDOC_WRITER_OPTIONS["table_of_contents"] then
        body:extend({ pandoc.RawBlock('typst', '\n\n#show outline.entry: it => {show link: set text(fill: black)\nlink(it.element.location(),it.indented(none, it.inner(), ))}\n\n#outline(title: [Table of Contents], indent: 1.5em)\n\n') })
        body:extend({ pandoc.RawBlock('typst', '#pagebreak()\n\n') })
      end

      if FORMAT:match 'typst' and meta["list-of-figures"] then
        body:extend({ pandoc.RawBlock('typst',
          '\n\n#outline(title: [List of Figures], target: figure.where(kind: "quarto-float-fig"),)\n\n') })
        body:extend({ pandoc.RawBlock('typst', '#pagebreak()\n\n') })
      end

      if FORMAT:match 'typst' and meta["list-of-tables"] then
        body:extend({ pandoc.RawBlock('typst',
          '\n\n#outline(title: [List of Tables], target: figure.where(kind: "quarto-float-tbl"),)\n\n') })
        body:extend({ pandoc.RawBlock('typst', '#pagebreak()\n\n') })
      end

      if meta.apatitledisplay and not meta["suppress-title-introduction"] then
        local firstpageheader = documenttitle:clone()
        firstpageheader.identifier = "firstheader"
        firstpageheader.classes = { "title", "unnumbered" }
        body:extend({ firstpageheader })
      end

      body:extend(doc.blocks)
      return pandoc.Pandoc(body, meta)
    end
  }
}
