require 'rubygems'
require 'rake'
require "rake/rdoctask"
require "rake/testtask"
require "spec/rake/spectask"
require "fileutils"

# Load code annotation support library
require File.expand_path("../tools/annotation_extract", __FILE__)

include FileUtils

desc "Run the specs."
task :default => :specs

task :merb => [:clean, :rdoc, :package]


##############################################################################
# Documentation
##############################################################################
task :doc => [:rdoc]
namespace :doc do

  Rake::RDocTask.new do |rdoc|
    files = ["README", "LICENSE", "CHANGELOG", "lib/**/*.rb"]
    rdoc.rdoc_files.add(files)
    rdoc.main = "README"
    rdoc.title = "Merb Docs"
    rdoc.rdoc_dir = "doc/rdoc"
    rdoc.options << "--line-numbers"
  end

  desc "run webgen"
  task :webgen do
    sh %{cd doc/site; webgen}
  end

end

##############################################################################
# rSpec & rcov
##############################################################################
desc "Run :specs, :rcov"
task :aok => [:specs, :rcov]

def setup_specs(name, spec_cmd='spec', run_opts = "-c")
  except = []
  except += Dir["spec/**/memcache*_spec.rb"] if ENV['MEMCACHED'] == 'no'

  public_globs = Dir["#{Dir.pwd}/spec/public/**/*_spec.rb"].reject{|file| file.include?('/gems/')}
  public_globs_10 = 
    Dir["#{Dir.pwd}/spec10/public/**/*_spec.rb"].reject{|file| file.include?('/gems/')}

  private_globs = Dir["#{Dir.pwd}/spec/private/**/*_spec.rb"]

  desc "Run all specs (#{name})"
  task "specs:#{name}" do
    require "lib/merb-core/test/run_specs"
    globs = public_globs + private_globs
    run_specs(globs, spec_cmd, ENV['RSPEC_OPTS'] || run_opts, except)
  end
  
  desc "Run 1.0 frozen specs"
  task "specs:oneoh" do
    require "lib/merb-core/test/run_specs"
    globs = public_globs_10
    run_specs(globs, spec_cmd, ENV['RSPEC_OPTS'] || run_opts, except)
  end
  
  desc "Run private specs (#{name})"
  task "specs:#{name}:private" do
    require "lib/merb-core/test/run_specs"
    run_specs(private_globs, spec_cmd, ENV['RSPEC_OPTS'] || run_opts)
  end

  desc "Run public specs (#{name})"
  task "specs:#{name}:public" do
    require "lib/merb-core/test/run_specs"
    run_specs(public_globs, spec_cmd, ENV['RSPEC_OPTS'] || run_opts)
  end
  
  # With profiling formatter
  desc "Run all specs (#{name}) with profiling formatter"
  task "specs:#{name}_profiled" do
    require "lib/merb-core/test/run_specs"
    run_specs("spec/**/*_spec.rb", spec_cmd, "-c -f o")
  end

  desc "Run private specs (#{name}) with profiling formatter"
  task "specs:#{name}_profiled:private" do
    require "lib/merb-core/test/run_specs"
    run_specs("spec/private/**/*_spec.rb", spec_cmd, "-c -f o")
  end

  desc "Run public specs (#{name}) with profiling formatter"
  task "specs:#{name}_profiled:public" do
    require "lib/merb-core/test/run_specs"
    run_specs("spec/public/**/*_spec.rb", spec_cmd, "-c -f o")
  end  
end

setup_specs("mri", "spec")
setup_specs("jruby", "jruby -S spec")

task "specs:core_ext" do
  require "lib/merb-core/test/run_specs"
  run_specs("spec/public/core_ext/*_spec.rb", "spec", "-c -f o")
end

task "spec"           => ["specs:mri"]
task "specs"          => ["specs:mri"]
task "specs:private"  => ["specs:mri:private"]
task "specs:public"   => ["specs:mri:public"]

desc "Run coverage suite"
task :rcov do
  require 'fileutils'
  FileUtils.rm_rf("coverage") if File.directory?("coverage")
  FileUtils.mkdir("coverage")
  path = File.expand_path(Dir.pwd)
  files = Dir["spec/**/*_spec.rb"]
  files.each do |spec|
    puts "Getting coverage for #{File.expand_path(spec)}"
    command = %{rcov #{File.expand_path(spec)} --aggregate #{path}/coverage/data.data --exclude ".*" --include-file "lib/merb-core(?!\/vendor)"}
    command += " --no-html" unless spec == files.last
    `#{command} 2>&1`
  end
end

desc "Run a specific spec with TASK=xxxx"
Spec::Rake::SpecTask.new("spec") do |t|
  t.spec_opts = ["--colour"]
  t.libs = ["lib", "server/lib" ]
  t.spec_files = (ENV["TASK"] || '').split(',').map do |task|
    "spec/**/#{task}_spec.rb"
  end
end

desc "Run all specs output html"
Spec::Rake::SpecTask.new("specs_html") do |t|
  t.spec_opts = ["--format", "html"]
  t.libs = ["lib", "server/lib" ]
  t.spec_files = Dir["spec/**/*_spec.rb"].sort
end

##############################################################################
# CODE STATISTICS
##############################################################################

STATS_DIRECTORIES = [
  ['Code', 'lib/'],
  ['Unit tests', 'spec']
].collect { |name, dir| [ name, "./#{dir}" ] }.
  select  { |name, dir| File.directory?(dir) }

desc "Report code statistics (KLOCs, etc) from the application"
task :stats do
  require File.expand_path("../tools/code_statistics", __FILE__)
  # require "extra/stats"
  verbose = true
  CodeStatistics.new(*STATS_DIRECTORIES).to_s
end

##############################################################################
# SYNTAX CHECKING
##############################################################################

task :check_syntax do
  `find . -name "*.rb" |xargs -n1 ruby -c |grep -v "Syntax OK"`
  puts "* Done"
end


# Run specific tests or test files. Searches nested spec directories as well.
#
# Based on a technique popularized by Geoffrey Grosenbach
rule "" do |t|
  spec_cmd = (RUBY_PLATFORM =~ /java/) ? "jruby -S spec" : "spec"
  # spec:spec_file:spec_name
  if /spec:(.*)$/.match(t.name)
    arguments = t.name.split(':')

    file_name = arguments[1]
    spec_name = arguments[2..-1]

    spec_filename = "#{file_name}_spec.rb"
    specs = Dir["spec/**/#{spec_filename}"]

    if path = specs.detect { |f| spec_filename == File.basename(f) }
      run_file_name = path
    else
      puts "No specs found for #{t.name.inspect}"
      exit
    end

    example = " -e '#{spec_name}'" unless spec_name.empty?

    sh "#{spec_cmd} #{run_file_name} --colour #{example}"
  end
end

##############################################################################
# Flog
##############################################################################

namespace :flog do
  task :worst_methods do
    require "flog"
    flogger = Flog.new
    flogger.flog_files Dir["lib/**/*.rb"]
    totals = flogger.totals.sort_by {|k,v| v}.reverse[0..10]
    totals.each do |meth, total|
      puts "%50s: %s" % [meth, total]
    end
  end
  
  task :total do
    require "flog"
    flogger = Flog.new
    flogger.flog_files Dir["lib/**/*.rb"]
    puts "Total: #{flogger.total}"
  end
  
  task :per_method do
    require "flog"
    flogger = Flog.new
    flogger.flog_files Dir["lib/**/*.rb"]
    methods = flogger.totals.reject { |k,v| k =~ /\#none$/ }.sort_by { |k,v| v }
    puts "Total Flog:    #{flogger.total}"
    puts "Total Methods: #{flogger.totals.size}"
    puts "Flog / Method: #{flogger.total / methods.size}"
  end
end

namespace :tools do
  namespace :tags do
    desc "Generates Emacs tags using Exuberant Ctags."
    task :emacs do
      sh "ctags -e --Ruby-kinds=-f -o TAGS -R lib"
    end
  end
end
