#!/usr/bin/env crystal
require "../src/pg"

@[Link("readline")]
{% if flag?(:openbsd) %}
  @[Link("termcap")]
{% end %}
lib LibReadline
  fun readline(prompt : UInt8*) : UInt8*
  fun add_history(line : UInt8*)
end

def readline
  line = LibReadline.readline("# ")
  if line
    LibReadline.add_history(line)
    String.new(line).tap { LibC.free(line.as(Void*)) }
  else
    ""
  end
end

url = ARGV[0]? || "postgres:///"
db = DB.open(url)

loop do
  query = readline
  exit if query == "\\q"
  has_results = false
  begin
    db.query(query) do |rs|
      has_results = rs.column_count > 0
      if has_results
        # Gather rows, including a first row for the column names
        rows = [] of Array(typeof(rs.read))

        # The first row: column names
        rows << rs.column_count.times.map { |i| rs.column_name(i).as(typeof(rs.read)) }.to_a

        # The result rows
        rs.each do
          rows << rs.column_count.times.map { rs.read }.to_a
        end

        # Compute maximum sizes for each column for a nicer output
        sizes = [] of Int32
        rs.column_count.times do |i|
          # Add 2 for padding
          sizes << rows.max_of(&.[i].to_s.size.+(2))
        end

        # Print rows
        rows.each_with_index do |row, row_index|
          row.each_with_index do |value, col_index|
            print " |" if col_index > 0
            print " "
            col_size = sizes[col_index] - 2
            case value
            when Int, Float, PG::Numeric
              print value.to_s.rjust(col_size)
            else
              print value.to_s.ljust(col_size)
            end
          end

          # Write the separator ("---+---+---") after the first row
          if row_index == 0
            puts
            row.each_index do |col_index|
              print "+" if col_index > 0
              print "-" * sizes[col_index]
            end
          end

          puts
        end

        # Print numbers of rows
        count = rows.size - 1
        if count == 1
          puts "(1 row)"
        else
          puts "(#{count} rows)"
        end
      end
    end
  rescue e
    puts "ERROR: #{e.message}"
  end
  puts if has_results
end
