require 'rbconfig'
IR_BIN = RbConfig::CONFIG['bindir']
IR_EXE = ::File.join(IR_BIN, 'ir.exe')

def debug(*args)
  print(*args) if ENV['VERBOSE']
end

def debugln(*args)
  debug(*([args].flatten << "\n"))
end

task :default => [:test]

desc "Run IronRuby.Rack tests"
task :test do
  system "#{IR_EXE} test/ispecrb.rb -a"
end

task :gem do
  system "#{IR_EXE} -S gem build ironruby-rack.gemspec"
end

desc "Generate a ChangeLog"
task :changelog do
  File.open("ChangeLog", "w") { |out|
    `git log -z`.split("\0").map { |chunk|
      author = chunk[/Author: (.*)/, 1].strip
      date = chunk[/Date: (.*)/, 1].strip
      desc, detail = $'.strip.split("\n", 2)
      detail ||= ""
      detail.rstrip!
      out.puts "#{date} #{author}"
      out.puts " * #{desc.strip}"
      out.puts detail unless detail.empty?
      out.puts
    }
  }
end

if ENV['DLR_ROOT']
  DOTNET_VERSION = System::Environment.version.major
  build_config = IR_BIN.split('/').last
  if build_config !~ /Release|Debug/i
    build_config = "Release"
    build_config = "v2" + build_config if DOTNET_VERSION.to_i < 4
  end
  BUILD_CONFIG = build_config
  SRC_BIN = File.expand_path(File.join(ENV['DLR_ROOT'], 'bin', BUILD_CONFIG))
  DST_BIN = File.expand_path(File.join(File.dirname(__FILE__), 'bin'))
  
  desc "Run all passing Rack tests through IronRuby's unit-test runner (utr.rb)"
  task :ci do
    system "#{IR_EXE} #{ENV['DLR_ROOT']}/Languages/Ruby/Tests/Scripts/utr.rb rack"
  end

  desc "Run all Rack tests through IronRuby's unit-test runner (utr.rb)"
  task :fullracktest do
    system "#{IR_EXE} #{ENV['DLR_ROOT']}/Languages/Ruby/Tests/Scripts/utr.rb rack -all"
  end

  # CSharp compiler helper (version 3.5 and 4.0)
  # files:Array - list of files that make up a C# program
  # refs:Array - list of assembly references
  # flags:Array - list of compiler flags
  # options:Hash[String]=String - hash of compiler options
  # version:String - Version of the C# compiler to use
  def csc(files, refs = [], flags = [], options = {}, version = nil)
    version = '4.0.30319' if version.nil? || version.to_f >= 4.0
    version = '3.5' if version.to_f < 4.0
    dotnet = 'C:\Windows\Microsoft.NET\Framework'
    csc_exe = File.join(dotnet, "v#{version}", 'csc.exe')
    cmd = "#{csc_exe} #{
  	  flags.flatten.map{|i| "/#{i}"}.flatten.join(' ')
  	} #{
        options.inject([]){|r, (k,v)| r << "/#{k}#{":#{v}" if v}"}.join(' ')
  	} #{
        refs.map{|r| "/r:#{win_path(r)}"}.join(' ')
  	} #{
        win_path(files.flatten.join(' '))
  	}"
    [system(cmd), cmd]
  end
  
  def win_path(path)
    path.gsub('/', '\\')
  end
  
  def quote(str_or_array)
    val = str_or_array
    val = [str_or_array] if str_or_array.kind_of? String
    val.map{|v| "\"#{v}\""}
  end
  
  def ironruby_binaries
    %W(
      #{IR_BIN}/IronRuby.dll
      #{IR_BIN}/IronRuby.Libraries.dll
      #{IR_BIN}/IronRuby.Libraries.Yaml.dll
      #{IR_BIN}/Microsoft.Scripting.dll
      #{IR_BIN}/Microsoft.Dynamic.dll
      #{IR_BIN}/Microsoft.Scripting.Core.dll
      #{IR_BIN}/Microsoft.Scripting.Metadata.dll
    ).select{|f| ::File.exist? f}
  end
  
  def system_binaries
    asms = %W(
      mscorlib.dll
      System.dll
      System.Configuration.dll
      System.Web.dll
      System.Web.Routing.dll
    )
    asms << 'System.Core.dll' unless File.exist? "#{IR_BIN}/Microsoft.Scripting.Core.dll"
    asms.compact
  end
  
  def copy_cassini
    FileUtils.cp "#{SRC_BIN}/Cassini.exe", DST_BIN
  end

  def cassini_stopped?
    10.times do |i| 
      if `tasklist`.grep(/Cassini/).size == 0
        return true
      else 
        sleep 0.1
      end
    end
	return false
  end

  def kill_cassini
    `taskkill /F /IM Cassini.exe`
  end

  desc 'Updates the bin directory of ironruby-rack. Should only be used by ironruby-rack developers'
  task 'update-bin' => ['compile', 'copy-cassini', '32bit-cassini']
  
  desc 'Compiles IronRuby.Rack against the current IronRuby binaries'
  task 'compile', [:to] do |t, args|
    args.with_defaults(:to => DST_BIN)
  
    flags = ['nologo', 'nostdlib']
    options = {}
  
    if BUILD_CONFIG =~ /Release/
      flags << ['optimize+', 'debug-']
      options.merge! :debug => 'pdbonly'
	  remove_pdb = true
    else
      flags << ['debug+', 'optimize-']
      options.merge! :define => 'DEBUG;', :debug => 'full'
    end
  
    options[:define] ||= ''
    options[:define] << (DOTNET_VERSION.to_f < 4.0 ? 'CLR2;' : 'CLR4;')
  
    dll_path =  File.join(DST_BIN, "IronRuby.Rack.dll")
    
    files = quote(
      Dir[File.expand_path(File.dirname(__FILE__) + '/../Src') + '/*.cs'] +
      Dir[File.expand_path(File.dirname(__FILE__) + '/../Src/Properties') + '/*.cs']
    )
  
    references = quote(system_binaries + ironruby_binaries)
    options.merge! :out => quote(dll_path), :target => 'library'
  
    debugln
    debug "=> Compiling IronRuby.Rack (#{BUILD_CONFIG}) "
    csc_result, csc_cmd = csc files, references, flags, options, DOTNET_VERSION
    if csc_result
      debugln "[DONE]"
      debugln "IronRuby.Rack => #{dll_path}"
    else
      $stderr.puts "[FAILED]\n #{csc_cmd}"
    end
    debugln
  end
  
  desc 'Copies a pre-built Cassini.exe to the bin directory'
  task 'copy-cassini' do
    require 'fileutils'
    debug "=> Copying Cassini.exe "
	begin
	  copy_cassini
	rescue IOError
	  puts "Cassini.exe is already running ... killing all Cassini.exe processes"
	  kill_cassini
	  if (cassini_stopped?)
	    copy_cassini
	  else
	    $stderr.puts "Cassini.exe not quitting, please quit manually and retry"
		exit(1)
      end
	end
    debugln "[DONE]"
    debugln "#{SRC_BIN}/Cassini.exe => #{DST_BIN}/Cassini.exe"
    debugln
  end
  
  desc 'Force Cassini to only run in 32-bit (rubygems does not support 64-bit)'
  task '32bit-cassini' do
    debug "=> Stamping Cassini.exe for 32-bit execution "
    corflags = "CorFlags.exe \"#{DST_BIN.gsub '/', '\\'}/Cassini.exe\" /32Bit+ /Force /nologo"
    if system corflags
      debugln "[DONE]"
    else
      $stderr.puts "[FAILED]\n #{corflags}"
    end
    debugln
  end

  desc 'Remove IronRuby.Rack.pdb'
  task 'remove-pdb' do
    pdb = "#{DST_BIN}/IronRuby.Rack.pdb"
    FileUtils.rm pdb if File.exist? pdb
  end

  desc 'Generate a IronRuby.Rack release'
  task 'release' => ['update-bin', 'remove-pdb', 'gem']

end