require "asciidoctor-pdf"
require_relative "nabehelper"

module Asciidoctor
  module PDF
    class Converter
      include NabeHelper
      def convert_paragraph node
        add_dest_for_block node if node.id
        prose_opts = { margin_bottom: 0, hyphenate: true }
        lead = (roles = node.roles).include? 'lead'
        if (align = resolve_alignment_from_role roles)
          prose_opts[:align] = align
        end

        if (text_indent = @theme.prose_text_indent || 0) > 0
          prose_opts[:indent_paragraphs] = text_indent
        end

        # TODO: check if we're within one line of the bottom of the page
        # and advance to the next page if so (similar to logic for section titles)
        layout_caption node.title if node.title?

        if lead
          theme_font :lead do
            layout_prose node.content, prose_opts
          end
        else
          layout_prose node.content, prose_opts
        end

        dlist = node&.parent&.parent&.parent

        if dlist&.style=="horizontal"
          m = get_node_attriute_float( dlist, "margin-bottom", nil )
          if m
            margin_bottom m
            return
          end
        end
        if (margin_inner_val = @theme.prose_margin_inner) &&
            (next_block = (siblings = node.parent.blocks)[(siblings.index node) + 1]) && next_block.context == :paragraph
          margin_bottom margin_inner_val
        else
          margin_bottom @theme.prose_margin_bottom
        end
      end

      def convert_dlist node
        add_dest_for_block node if node.id

        case (style = node.style)
        when 'unordered', 'ordered'
          if style == 'unordered'
            list_style = :ulist
            (markers = @list_bullets) << :disc
          else
            list_style = :olist
            (markers = @list_numerals) << 1
          end
          list = List.new node.parent, list_style
          stack_subject = node.has_role? 'stack'
          subject_stop = node.attr 'subject-stop', (stack_subject ? nil : ':'), false
          node.items.each do |subjects, dd|
            subject = [*subjects].first.text
            list_item_text = %(+++<strong>#{subject}#{(StopPunctRx.match? sanitize subject) ? '' : subject_stop}</strong>#{dd.text? ? "#{stack_subject ? '<br>' : ' '}#{dd.text}" : ''}+++)
            list_item = ListItem.new list, list_item_text
            dd.blocks.each {|it| list_item << it }
            list << list_item
          end
          convert_outline_list list
          markers.pop
        when 'horizontal'
          table_data = []
          term_padding = desc_padding = term_line_metrics = term_inline_format = term_kerning = nil
          max_term_width = 0
          theme_font :description_list_term do
            if (term_font_styles = font_styles).empty?
              term_inline_format = true
            else
              term_inline_format = [inherited: { styles: term_font_styles }]
            end
            margin_left = get_node_attriute_float(node, 'margin-left', 10)
            margin_bottom = get_node_attriute_float(node, 'margin-bottom', (@theme.prose_margin_bottom || 0) * 0.5 )
            term_line_metrics = calc_line_metrics @theme.description_list_term_line_height || @theme.base_line_height
            term_padding = [
              term_line_metrics.padding_top, # up
              10, # right
              margin_bottom + term_line_metrics.padding_bottom, # bottom
              margin_left # left
            ]
            desc_padding = [
              0, # up
              10, # right
              margin_bottom, # bottom
              10 # left
            ]
            term_kerning = default_kerning?
          end
          node.items.each do |terms, desc|
            term_text = terms.map(&:text).join ?\n
            if (term_width = width_of term_text, inline_format: term_inline_format, kerning: term_kerning) > max_term_width
              max_term_width = term_width
            end
            row_data = [{
              text_color: @font_color,
              kerning: term_kerning,
              content: term_text,
              inline_format: term_inline_format,
              padding: term_padding,
              leading: term_line_metrics.leading,
              # FIXME: prawn-table doesn't have support for final_gap option
              #final_gap: term_line_metrics.final_gap,
              valign: :top,
            }]
            desc_container = Block.new desc, :open
            desc_container << (Block.new desc_container, :paragraph, source: (desc.instance_variable_get :@text), subs: :default) if desc.text?
            desc.blocks.each {|b| desc_container << b } if desc.block?
            row_data << {
              content: (::Prawn::Table::Cell::AsciiDoc.new self, content: desc_container, text_color: @font_color, padding: desc_padding, valign: :top),
            }
            table_data << row_data
          end
          max_term_width += (term_padding[1] + term_padding[3])
          term_column_width = [max_term_width, bounds.width * 0.5].min
          table table_data, position: :left, cell_style: { border_width: 0 }, column_widths: [term_column_width] do
            @pdf.layout_table_caption node if node.title?
          end
          margin_bottom 0 # (@theme.prose_margin_bottom || 0) * 0.5
        when 'qanda'
          @list_numerals << '1'
          convert_outline_list node
          @list_numerals.pop
        else
          # TODO: check if we're within one line of the bottom of the page
          # and advance to the next page if so (similar to logic for section titles)
          layout_caption node.title, category: :description_list if node.title?

          term_line_height = @theme.description_list_term_line_height || @theme.base_line_height
          line_metrics = theme_font(:description_list_term) { calc_line_metrics term_line_height }
          node.items.each do |terms, desc|
            # NOTE: don't orphan the terms (keep together terms and at least one line of content)
            allocate_space_for_list_item line_metrics, (terms.size + 1), ((@theme.description_list_term_spacing || 0) + 0.05)
            theme_font :description_list_term do
              if (term_font_styles = font_styles).empty?
                term_font_styles = nil
              end
              terms.each do |term|
                # QUESTION should we pass down styles in other calls to layout_prose
                layout_prose term.text, margin_top: 0, margin_bottom: @theme.description_list_term_spacing, align: :left, line_height: term_line_height, normalize_line_height: true, styles: term_font_styles
              end
            end
            indent(@theme.description_list_description_indent || 0) do
              traverse_list_item desc, :dlist_desc, normalize_line_height: true
            end if desc
          end
        end
      end
    end
  end
end