module Tabulo

  # @!visibility private
  class Column

    attr_accessor :width
    attr_reader :header
    attr_reader :index
    attr_reader :left_padding
    attr_reader :right_padding

    def initialize(
      align_body:,
      align_header:,
      extractor:,
      formatter:,
      header:,
      header_styler:,
      index:,
      left_padding:,
      padding_character:,
      right_padding:,
      styler:,
      truncation_indicator:,
      wrap_preserve:,
      width:)

      @align_body = align_body
      @align_header = align_header
      @extractor = extractor
      @formatter = formatter
      @header = header
      @index = index
      @left_padding = left_padding
      @right_padding = right_padding

      @header_styler =
        if header_styler
          case header_styler.arity
          when 3
            -> (_, str, cell_data, line_index) { header_styler.call(str, cell_data.column_index, line_index) }
          when 2
            -> (_, str, cell_data) { header_styler.call(str, cell_data.column_index) }
          else
            -> (_, str) { header_styler.call(str) }
          end
        else
          -> (_, str) { str }
        end

      @padding_character = padding_character
      @styler = styler || -> (_, s) { s }
      @truncation_indicator = truncation_indicator
      @wrap_preserve = wrap_preserve
      @width = width
    end

    def header_cell
      if @header_styler.arity >= 3
        cell_data = CellData.new(nil, nil, @index)
      end
      Cell.new(
        alignment: @align_header,
        cell_data: cell_data,
        formatter: -> (s) { s },
        left_padding: @left_padding,
        padding_character: @padding_character,
        right_padding: @right_padding,
        styler: @header_styler,
        truncation_indicator: @truncation_indicator,
        value: @header,
        wrap_preserve: @wrap_preserve,
        width: @width,
      )
    end

    def body_cell(source, row_index:, column_index:)
      if body_cell_data_required?
        cell_data = CellData.new(source, row_index, @index)
      end
      Cell.new(
        alignment: @align_body,
        cell_data: cell_data,
        formatter: @formatter,
        left_padding: @left_padding,
        padding_character: @padding_character,
        right_padding: @right_padding,
        styler: @styler,
        truncation_indicator: @truncation_indicator,
        value: body_cell_value(source, row_index: row_index, column_index: column_index),
        wrap_preserve: @wrap_preserve,
        width: @width,
      )
    end

    def body_cell_value(source, row_index:, column_index:)
      if @extractor.arity == 2
        @extractor.call(source, row_index)
      else
        @extractor.call(source)
      end
    end

    def padded_width = width + total_padding

    def total_padding = @left_padding + @right_padding

    private

    def body_cell_data_required?
      @cell_data_required ||= (@styler.arity == 3 || @formatter.arity == 2)
    end
  end
end
