module IsoDoc::Ietf
  class RfcConvert < ::IsoDoc::Convert
    def cleanup(docxml)
      image_cleanup(docxml)
      figure_cleanup(docxml)
      table_cleanup(docxml)
      footnote_cleanup(docxml)
      sourcecode_cleanup(docxml)
      annotation_cleanup(docxml)
      deflist_cleanup(docxml)
      bookmark_cleanup(docxml)
      aside_cleanup(docxml)
      front_cleanup(docxml)
      docxml
    end

    # TODO: insert <u>
    
    def front_cleanup(xmldoc)
        xmldoc.xpath("//title").each { |s| s.children = s.text }
        xmldoc.xpath("//reference/front[not(author)]").each do |f|
          insert = f.at("./seriesInfo[last()]") || f.at("./title")
          insert.next = "<author surname='Unknown'/>"
        end
      end

    def table_footnote_cleanup(docxml)
      docxml.xpath("//table[descendant::fn]").each do |t|
        t.xpath(".//fn").each do |a|
          t << "<aside>#{a.remove.children}</aside>"
        end
      end
    end

    def figure_footnote_cleanup(docxml)
      docxml.xpath("//figure[descendant::fn]").each do |t|
        t.xpath(".//fn").each do |a|
          t << "<aside>#{a.remove.children}</aside>"
        end
      end
    end

    def table_cleanup(docxml)
      table_footnote_cleanup(docxml)
    end

    def figure_cleanup(docxml)
      figure_postamble(docxml)
      figure_wrap_artwork(docxml)
      figure_unnest(docxml)
      figure_footnote_cleanup(docxml)
    end

    def figure_wrap_artwork(docxml)
      docxml.xpath("//artwork[not(parent::figure)] | "\
                   "//sourcecode[not(parent::figure)]").each do |a|
        a.wrap("<figure></figure>")
      end
    end

    def figure_unnest(docxml)
      docxml.xpath("//figure[descendant::figure]").each do |f|
        insert = f
        f.xpath(".//figure").each do |a|
          insert.next = a.remove
          insert = insert.next_element
        end
      end
    end

    def figure_postamble(docxml)
      make_postamble(docxml)
      move_postamble(docxml)
      move_preamble(docxml)
    end

    def make_postamble(docxml)
      docxml.xpath("//figure").each do |f|
        a = f&.at("./artwork | ./sourcecode") || next
        name = f&.at("./name")&.remove
        b = a&.xpath("./preceding-sibling::*")&.remove
        c = a&.xpath("./following-sibling::*")&.remove
        a = a.remove
        name and f << name
        b.empty? or f << "<preamble>#{b.to_xml}</preamble>"
        a and f << a
        c.empty? or f << "<postamble>#{c.to_xml}</postamble>"
      end
    end

    def move_postamble(docxml)
      docxml.xpath("//postamble").each do |p|
        insert = p.parent
        p.remove.elements.each do |e|
          insert.next = e
          insert = insert.next_element
        end
      end
    end

    def move_preamble(docxml)
      docxml.xpath("//preamble").each do |p|
        insert = p.parent
        p.remove.elements.each do |e|
          insert.previous = e
        end
      end
    end

    def footnote_cleanup(docxml)
      fn = footnote_refs_cleanup(docxml)
      endnotes = make_endnotes(docxml)
      docxml.xpath("//section[descendant::fn] | "\
                   "//abstract[descendant::fn]").each do |s|
        s.xpath(".//fn").each do |f|
          ref = f.at(".//ref") and ref.replace("[#{fn[ref.text]}] ")
          endnotes << f.remove.children
        end
      end
    end

    def footnote_refs_cleanup(docxml)
      i = 0
      fn = {}
      docxml.xpath("//fnref").each do |f|
        unless fn[f.text]
          i = i + 1
          fn[f.text] = i.to_s
        end
        f.replace(" [#{fn[f.text]}]")
      end
      fn
    end

    def make_endnotes(docxml)
      return unless docxml.at("//fn")
      endnotes = docxml.at("//back") or
      docxml << "<back/>" and endnotes = docxml.at("//back")
      endnotes << "<section><name>Endnotes</name></section>"
      endnotes = docxml.at("//back/section[last()]")
    end

    def image_cleanup(docxml)
      docxml.xpath("//t[descendant::artwork]").each do |t|
        insert = t
        t.xpath(".//artwork").each_with_index do |a, i|
          insert.next = a.dup
          insert = insert.next
          a.replace("[IMAGE #{i+1}]")
        end
      end
    end

    # for markup in pseudocode
    def sourcecode_cleanup(docxml)
      docxml.xpath("//sourcecode").each do |s|
        s.children = s.children.to_xml.gsub(%r{<br/>\n}, "\n").
          gsub(%r{\s+(<t[ >])}, "\\1").gsub(%r{</t>\s+}, "</t>")
        sourcecode_remove_markup(s)
        text = HTMLEntities.new.decode(s.children.to_xml.sub(/\A\n+/, ""))
        s.children = "<![CDATA[#{text}]]>"
      end
    end

    def sourcecode_remove_markup(s)
      s.traverse do |n|
        next if n.text?
        next if %w(name callout annotation note sourcecode).include? n.name
        if n.name == "br" then n.replace("\n")
        elsif n.name == "t" then n.replace("\n\n#{n.children}")
        else
          n.replace(n.children)
        end
      end
    end

    def annotation_cleanup(docxml)
      docxml.xpath("//reference").each do |r|
        next unless r&.next_element&.name == "aside"
        aside = r.next_element
        aside.name = "annotation"
        aside.traverse do |n|
          n.name == "t" and n.replace(n.children)
        end
        r << aside
      end
      docxml.xpath("//references/aside").each { |r| r.remove }
    end

    def deflist_cleanup(docxml)
      dt_cleanup(docxml)
      dd_cleanup(docxml)
    end

    def dt_cleanup(docxml)
      docxml.xpath("//dt").each do |d|
        d&.first_element_child&.name == "bookmark" and
          d["anchor"] ||= d.first_element_child["anchor"]
        d.xpath(".//t").each do |t|
          d["anchor"] ||= t["anchor"]
          t.replace(t.children)
        end
      end
    end

    def dd_cleanup(docxml)
      docxml.xpath("//dd").each do |d|
        d&.first_element_child&.name == "bookmark" and
          d["anchor"] ||= d.first_element_child["anchor"]
      end
    end

    def bookmark_cleanup(docxml)
      docxml.xpath("//bookmark").each do |b|
        b.remove
      end
    end

    def aside_cleanup(docxml)
      docxml.xpath("//t[descendant::aside] | //table[descendant::aside] | "\
                   "//figure[descendant::aside]").each do |p|
        insert = p
        p.xpath(".//aside").each do |a|
          insert.next = a.remove
          insert = insert.next_element
        end
      end
    end
  end
end