require_relative "column_printer"
require_relative "terminal"

class Thor
  module Shell
    class TablePrinter < ColumnPrinter
      BORDER_SEPARATOR = :separator

      def initialize(stdout, options = {})
        super
        @formats = []
        @maximas = []
        @colwidth = options[:colwidth]
        @truncate = options[:truncate] == true ? Terminal.terminal_width : options[:truncate]
        @padding = 1
      end

      def print(array)
        return if array.empty?

        prepare(array)

        print_border_separator if options[:borders]

        array.each do |row|
          if options[:borders] && row == BORDER_SEPARATOR
            print_border_separator
            next
          end

          sentence = "".dup

          row.each_with_index do |column, index|
            sentence << format_cell(column, row.size, index)
          end

          sentence = truncate(sentence)
          sentence << "|" if options[:borders]
          stdout.puts indentation + sentence

        end
        print_border_separator if options[:borders]
      end

    private

      def prepare(array)
        array = array.reject{|row| row == BORDER_SEPARATOR }

        @formats << "%-#{@colwidth + 2}s".dup if @colwidth
        start = @colwidth ? 1 : 0

        colcount = array.max { |a, b| a.size <=> b.size }.size

        start.upto(colcount - 1) do |index|
          maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max

          @maximas << maxima
          @formats << if options[:borders]
             "%-#{maxima}s".dup
          elsif index == colcount - 1
            # Don't output 2 trailing spaces when printing the last column
            "%-s".dup
          else
            "%-#{maxima + 2}s".dup
          end
        end

        @formats << "%s"
      end

      def format_cell(column, row_size, index)
        maxima = @maximas[index]

        f = if column.is_a?(Numeric)
          if options[:borders]
            # With borders we handle padding separately
            "%#{maxima}s"
          elsif index == row_size - 1
            # Don't output 2 trailing spaces when printing the last column
            "%#{maxima}s"
          else
            "%#{maxima}s  "
          end
        else
          @formats[index]
        end

        cell = "".dup
        cell << "|" + " " * @padding if options[:borders]
        cell << f % column.to_s
        cell << " " * @padding if options[:borders]
        cell
      end

      def print_border_separator
        separator = @maximas.map do |maxima|
          "+" + "-" * (maxima + 2 * @padding)
        end
        stdout.puts indentation + separator.join + "+"
      end

      def truncate(string)
        return string unless @truncate
        as_unicode do
          chars = string.chars.to_a
          if chars.length <= @truncate
            chars.join
          else
            chars[0, @truncate - 3 - @indent].join + "..."
          end
        end
      end

      def indentation
        " " * @indent
      end

      if "".respond_to?(:encode)
        def as_unicode
          yield
        end
      else
        def as_unicode
          old = $KCODE # rubocop:disable Style/GlobalVars
          $KCODE = "U" # rubocop:disable Style/GlobalVars
          yield
        ensure
          $KCODE = old # rubocop:disable Style/GlobalVars
        end
      end
    end
  end
end
