# Unashamedly appropriated from Rails

class CodeStatistics
  def initialize(*pairs)
    @pairs      = pairs
    @statistics = calculate_statistics
    @total      = calculate_total if pairs.length > 1
  end

  def to_s
    print_header
    @statistics.each{ |k, v| print_line(k, v) }
    print_splitter
  
    if @total
      print_line('Total', @total)
      print_splitter
      print_code_to_test
    end
  end

  private
  def calculate_statistics
    @pairs.inject({}) do |stats, pair|
      stats[pair.first] = calculate_directory_statistics(pair.last); stats
    end
  end

  def get_file_statistics(fn, stats)
    f = File.open(fn)
    while line = f.gets
      stats[:lines]     += 1
      stats[:classes]   += 1 if line =~ /class [A-Z]/ || line =~ /context/
      stats[:methods]   += 1 if line =~ /def [a-z]/ || line =~ /specify/
      stats[:codelines] += 1 unless line =~ /^\s*$/ || line =~ /^\s*#/
    end
  end
  
  def get_directory_statistics(dir, stats)
    Dir.foreach(dir) do |fn|
      next if fn =~ /^\./
      fn = File.join(dir, fn)
      if File.directory?(fn)
        get_directory_statistics fn, stats
      else
        next unless fn =~ /.*rb/
        get_file_statistics fn, stats
      end
    end
    stats
  end

  def calculate_directory_statistics(directory, pattern = /.*rb/)
    stats = { :lines => 0, :codelines => 0, :classes => 0, :methods => 0 }
    get_directory_statistics directory, stats
    stats
  end

  def calculate_total
    total = { :lines => 0, :codelines => 0, :classes => 0, :methods => 0 }
    @statistics.each_value { |pair| pair.each { |k, v| total[k] += v } }
    total
  end

  def print_header
    print_splitter
    puts '| Name          | Lines |   LOC | Classes | Methods | M/C | LOC/M |'
    print_splitter
  end

  def print_splitter
    puts '+---------------+-------+-------+---------+---------+-----+-------+'
  end

  def print_line(name, statistics)
    m_over_c   = (statistics[:methods] / statistics[:classes])   rescue m_over_c = 0
    loc_over_m = (statistics[:codelines] / statistics[:methods]) - 2 rescue loc_over_m = 0

    puts "| #{name.ljust(13)} " +
         "| #{statistics[:lines].to_s.rjust(5)} " +
         "| #{statistics[:codelines].to_s.rjust(5)} " +
         "| #{statistics[:classes].to_s.rjust(7)} " +
         "| #{statistics[:methods].to_s.rjust(7)} " +
         "| #{m_over_c.to_s.rjust(3)} " +
         "| #{loc_over_m.to_s.rjust(5)} |"
  end

  def print_code_to_test
    c_loc = 0
    t_loc = 0
    @statistics.each do |n, s|
      if n =~ /spec/i
        t_loc += s[:codelines]
      else
        c_loc += s[:codelines]
      end
    end
    ratio = (((t_loc.to_f / c_loc)*10).round.to_f/10).to_s[0,4]
    puts "  Code LOC: #{c_loc}     Spec LOC: #{t_loc}     Code to Spec Ratio: 1:#{ratio}"
  end
end
