module Rubinius
  module Stats
    module Units
      # Time unit factors, where one second is
      # 10^9 nanoseconds (ns)
      # 10^6 microseconds (us)
      # 10^3 milliseconds (ms)

      SEC_PER_NS  = 1.0e-9
      MSEC_PER_NS = 1.0e-6
      USEC_PER_NS = 1.0e-3

      KBYTES = 1024

      def usec(ns)
        ns * USEC_PER_NS
      end

      def msec(ns)
        ns * MSEC_PER_NS
      end

      def sec(ns)
        ns * SEC_PER_NS
      end

      def auto_time(time)
        return "0" if time == 0

        elapsed = sec time
        return "%.2fs" % elapsed if elapsed > 1.0

        elapsed = msec time
        return "%.2fm" % elapsed if elapsed > 1.0

        elapsed = usec time
        return "%.2fu" % elapsed if elapsed > 1.0

        "%dn" % time
      end

      # Thanks Ruby Quiz #113
      def comma(number, digits=2)
        if number.is_a? Float
          str = "%.#{digits}f" % number
        else
          str = number.to_s
        end

        str.reverse.scan(/(?:\d*\.)?\d{1,3}-?/).join(',').reverse
      end

      def percentage(part, whole, digits=1, percent='%%')
        "%.#{digits}f#{percent}" % (part * 100.0 / whole)
      end

      def auto_bytes(bytes)
        return bytes.to_s if bytes < KBYTES

        bytes /= KBYTES.to_f
        return "%.1fK" % bytes if bytes < KBYTES

        "%.1fM" % (bytes / KBYTES)
      end
    end

    module Printer
      def columns(*columns)
        @width = columns.inject(0) { |sum, x| sum + x }

        text_column = "%%-%ds" % columns.shift
        @value_format = "#{text_column}%#{columns[1]}s\n"

        format = columns.map { |c| "%%%ds" % c }.join
        @stats_format = "#{text_column}#{format}\n"

        @header_format = "\n" << @stats_format
      end

      def headings(*headings)
        @headings = headings
      end

      def heading(leader)
        printf @header_format, leader, *@headings
        separator
      end

      def separator(limit=nil)
        width = limit || @width
        puts "-" * width
      end

      def string(metric, string)
        printf @value_format, metric, string
      end

      def value(metric, value, meth=:comma)
        printf @value_format, metric, send(meth, value)
      end

      def statistics(metric, data, meth=:comma)
        values = [data[:total], data[:max], data[:min], data[:average]]
        printf @stats_format, metric, *values.map { |d| send meth, d }
      end
    end
  end
end
