#!/usr/bin/env ruby

require 'optparse'
require 'tempfile'

lib_path = File.expand_path("../../lib", __FILE__)

$:.unshift lib_path

require 'benchmark/suite'
require 'benchmark/ips'

targets = []
extra = []
at_end = false
save = false

compare_targets = []

opt = OptionParser.new do |o|
  o.on("-t", "--target TARGET", String,
          "Use TARGET to compare against: r:ruby|r19:ruby19|x:rbx|j:jruby") do |t|
    case t
    when 'r', 'ruby'
      targets << 'ruby'
    when 'r19', 'ruby19'
      targets << 'ruby19'
    when 'x', 'rbx', 'rubinius'
      targets << 'rbx'
    when 'j', 'jruby'
      targets << 'jruby'
    else
      # + disambiguates execution vs using a file
      if t[0,1] == "+"
        targets << t[1..-1]
      elsif File.exists?(t)
        begin
          data = Marshal.load(File.read(t))
          compare_targets << [t, data]
        rescue TypeError
          targets << t
        end
      else
        targets << t
      end
    end
  end

  o.on("-p", "--profile", "Profile code while benchmarking (rbx only)") do
    extra << "-Xprofile"
  end

  o.on("-e", "--end", "Report all stats after all suitse have run") do
    at_end = true
  end

  o.on("-T OPTION", String, "Add more arguments to each target") do |t|
    extra << t
  end

  o.on("-s", "--save PATH", String, "Save the results to a file") do |t|
    save = t
  end
end

opt.parse!

if targets.empty?
  targets << "ruby"
end

if save and targets.size > 1
  STDOUT.puts "Save mode only available with one target."
  exit 1
end

opts = []

if at_end
  opts << "--quiet"
end

results = targets.map do |t|
  tf = Tempfile.new "benchmark"
  tf.close
  STDOUT.puts "=== #{t} ===" unless at_end

  args = extra + ["-I#{lib_path}", "#{lib_path}/benchmark/suite-run.rb"]

  args += opts
  args << tf.path
  args += ARGV

  cmd, *rest = t.split(/\s+/)
  args.unshift *rest

  system cmd, *args

  if $?.exitstatus != 0
    puts "Error executing: #{cmd}"
    nil
  else
    tf.open
    [t, Marshal.load(tf.read)]
  end
end

results.compact!

if save
  name, data = results.last

  File.open save, "w" do |f|
    f << Marshal.dump(data)
  end

  STDOUT.puts "[Saved results to '#{save}']"
end

if at_end
  results.each do |name, suite|
    STDOUT.puts "=== #{name} ==="
    suite.display
  end
end

results += compare_targets

if results.size > 1
  compared = Hash.new { |h,k| h[k] = [] }

  results.each do |target, suite|
    suite.reports.each do |name, reports|
      reports.each do |rep|
        compared["#{name}:#{rep.label}"] << [target, rep]
      end
    end
  end

  STDOUT.puts

  compared.each do |name, reports|
    if reports.size > 1
      STDOUT.puts "Comparing #{name}:"

      iter = false
      sorted = reports.sort do |a,b|
        if a[1].respond_to? :ips
          iter = true
          b[1].ips <=> a[1].ips
        else
          a[1].runtime <=> b[1].runtime
        end
      end

      best_name, best_report = sorted.shift


      if iter
        STDOUT.printf "%20s: %10d i/s\n", best_name, best_report.ips
      else
        STDOUT.puts "#{best_name.rjust(20)}: #{best_report.runtime}s"
      end

      sorted.each do |entry|
        name, report = entry
        if iter
          x = (best_report.ips.to_f / report.ips.to_f)
          STDOUT.printf "%20s: %10d i/s - %.2fx slower\n", name, report.ips, x
        else
          x = "%.2f" % (report.ips.to_f / best_report.ips.to_f)
          STDOUT.puts "#{name.rjust(20)}: #{report.runtime}s - #{x}x slower"
        end
      end
    end
  end
end
