require "terminal-table"
require "csv"
require "json"

module Output
  class Base

    MAX_CELL_WIDTH = 80

    attr_reader :options

    def initialize(data_to_output, options={})
      @data, @options = data_to_output, options
    end

    def output(preferred_options)
      @options = @options.merge(preferred_options)
      case options[:format]
      when CommonOptions::TABLE_FORMAT
        table
      when CommonOptions::JSON_FORMAT
        json
      when CommonOptions::CSV_FORMAT
        csv
      end
    end

    def json
      JSON.pretty_generate(@data)
    end

    private

    def output_type
      @data.kind_of?(Array) ? :list : :show
    end

    def outputting_list?
      output_type == :list
    end

    def with_num?
      @options[:with_num] || outputting_list?
    end

    def create_table headers, rows, title=nil, with_num=true, separator=false
      return nil if headers.nil? or rows.nil?
      shrink_cells_if_width_exceeded(rows)
      if with_num
        headers.unshift(I18n.t("output.table_header.number"))
        rows.each_with_index {|row, i| row.unshift(i + 1)}
      end
      Terminal::Table.new do |t|
        titles = ["#{I18n.t("output.table_header.api_version")}: #{self.options[:api]}",
                   "#{title}"
                 ]
        t.title = titles.join( "\n" )
        t.headings = headers
        t.add_row rows[0] if rows[0]
        (rows[1..-1] || []).each do |r|
          t.add_separator if separator
          t.add_row r
        end
      end
    end

    def create_csv headers, rows, with_num=true, separator=":"
      if with_num
        headers.unshift(I18n.t("output.table_header.number"))
        rows.each_with_index {|row, i| row.unshift(i + 1)}
      end
      c = CSV.new("", {col_sep: separator, headers: true})
      c << headers
      rows.each do |r|
        row = r.map do |cell|
          if cell.is_a?(Array)
            cell.join(',')
          else
            cell.to_s
          end
        end
        c << row
      end
      c.string
    end

    def headers_and_rows(records, fields_to_output)
      headers = fields_to_output.map { |field| I18n.t("output.table_header.#{field}") }
      rows = records.map do |record|
        fields_to_output.map { |field| record[field] }
      end

      [headers, rows]
    end

    def headers_and_rows_for_array(records, field)
      headers = [ I18n.t("output.table_header.#{field}") ]
      rows = records.map{|record| [ record ] }
      [headers, rows]
    end

    def time_to_s value
      return "null" if value.nil?
      Time.at(value).to_s
    end

    private

    def shrink_cells_if_width_exceeded(rows)
      rows.each do |row|
        row.each_with_index do |cell, i|
          if cell.is_a?(String)
            row[i] = split_to_parts_of_size(cell, MAX_CELL_WIDTH)
          elsif cell.is_a?(Array)
            row[i] = cell.join("\n")
          end
        end
      end
    end

    def split_to_parts_of_size(string, size)
      (string || '').chars.each_slice(size).map(&:join).join("\n")
    end

  end
end
