require 'rexml/document'
require 'fileutils'

SVN_ROOT = Pathname.new 'c:/svn/trunk' 
DEFAULT_ROBOCOPY_OPTIONS = "/XF *#{EXCLUDED_EXTENSIONS.join(' *')} /NP /COPY:DAT /A-:R "

class String
  def change_tag!(name, value = '')
    pattern = Regexp.new "\<#{name}\>(.*?)\<\/#{name}\>", Regexp::MULTILINE
    self.gsub! pattern, "\<#{name}\>#{value}\<\/#{name}\>"
  end

  def change_tag_value!(name, old_value, new_value)
    pattern = Regexp.new "\<#{name}\>#{old_value}\<\/#{name}\>", Regexp::MULTILINE
    self.gsub! pattern, "\<#{name}\>#{new_value}\<\/#{name}\>"
  end
end

module SvnProvider
  def add(path)
    exec_f "svn add --force #{path}"
  end

  def delete(path)
    exec_f "svn delete --force #{path}"
  end

  def checkout(path)
  end
end

module TfsProvider
  def add(path)
    exec_f "tf add #{path}"
  end

  def delete(path)
    exec_f "tf delete #{path}"
  end

  def checkout(path)
    exec_f "tf checkout #{path}"
  end
end

class Configuration
  class Group
    attr_reader :super_group

    def initialize(name, super_group)
      @name = name
      @switches = {}
      @references = []
      @super_group = super_group
    end

    def switches(config, *args)
      @switches[config] ||= []
      @switches[config] += args
    end

    def references(*refs)
      @references += refs
    end

    def remove_switches(config, *args)
      args.each { |arg| @switches[config].delete arg } unless @switches[config].nil?
    end

    def get_switches(config)
      all = @switches[:all].nil? ? [] : @switches[:all]
      (@switches[config].nil? ? [] : @switches[config]) + all
    end

    def get_references
      @references
    end

    def framework_path(*args, &b)
      @framework_path = (if args.length == 0
        b.call
      elsif args.length == 1
        args.first
      else 
        raise 'framework_path must be called with either a path or a block that defines a path'
      end << nil)
    end

    def resolve_framework_path(path)
      @framework_path.each do |candidate|
        raise "cannot resolve path #{path}" if candidate.nil?
        candidate_path = candidate + path
        break candidate_path if candidate_path.exist?
      end
    end
  end

  def self.define(&b)
    @master = {}
    instance_eval(&b)
  end

  def self.group(*args, &b)
    name = args.first
    super_group = args.length == 2 ? @master[args.last] : nil
    @master[name] ||= Group.new(name, super_group)
    @master[name].instance_eval(&b)
  end

  def self.get_switches(group, config)
    result = []
    current = @master[group]
    while !current.nil?
      result += current.get_switches(config)
      current = current.super_group
    end
    result
  end

  def self.get_references(group)
    result = []
    current = @master[group]
    while !current.nil?
      result += current.get_references
      current = current.super_group
    end
    result.map { |assembly| resolve_framework_path(group, assembly) }
  end

  def self.resolve_framework_path(group, path)
    @master[group].resolve_framework_path(path)
  end
end

Configuration.define do
  group(:common) {
    switches :all, 'nologo', 'noconfig', 'nowarn:1591,1701,1702', 'errorreport:prompt', 'warn:4', 'warnaserror-'
    switches :debug, 'define:"DEBUG;TRACE;SIGNED;DLR"', 'debug+', 'debug:full', 'optimize-', 'delaysign+'
    switches :release, 'define:TRACE', 'optimize+'
    references 'System.dll'
  }
  group(:desktop, :common) {
    references 'System.Configuration.dll' 
  }
  group(:silverlight, :common) {
    switches :all, 'define:SILVERLIGHT', 'nostdlib+', 'platform:AnyCPU'
    references 'mscorlib.dll'
  }
  if ENV['mono'].nil?
    group(:desktop) {
      framework_path [Pathname.new(ENV['windir'].dup) + 'Microsoft.NET/Framework/v2.0.50727', 
                      Pathname.new('c:\program files\reference assemblies\microsoft\framework\v3.5')]
    }
    group(:silverlight) {
      framework_path [Pathname.new('c:/program files/microsoft silverlight/2.0.30523.6')]
    }
  else
    group(:mono) {
      framework_path {
        libdir = IO.popen('pkg-config --variable=libdir mono').read.strip
        [Pathname.new(libdir) + 'mono' + '2.0']
      }
      switches :all, 'noconfig'
      remove_switches ['warnaserror+']
    }
  end
end

class ProjectContext
  Mapping = Struct.new(:merlin_path, :svn_path, :recurse)

  class CommandContext

    # Low-level command helpers

    def is_test?
      ENV['test'] == 'true'
    end

    def exec(cmd)
      if is_test?
        rake_output_message ">> #{cmd}"
      else
        sh cmd
      end
    end

    def exec_net(cmd)
      if ENV['mono'].nil?
        exec cmd
      else
        exec "mono #{cmd}"
      end
    end

    def exec_f(cmd)
      begin
        exec(cmd)
      rescue
      end
    end

    # context is either :source or :target. project_context is reference
    # to ProjectContext subclass class

    def initialize(context, project_context)
      if context == :source || context == :target
        @project_context = project_context
        @context = context
      else
        raise "CommandContext#initialize(context) must be :source or :target"
      end
    end

    def get_mapping(name)
      mapping = @project_context.resolve(name)
      raise "cannot find #{name} in ProjectContext" if mapping.nil?
      mapping
    end

    # There are three things to consider when getting the source directory path
    # 1) The context (source or destination) we are running in
    # 2) The name of the mapping that we want to look up the source path in
    # 3) Whether we are running in MERLIN_ROOT or SVN_ROOT

    def get_source_dir(name)
      mapping = get_mapping name
      context_path = @project_context.source
      context_path + (@project_context.is_merlin? ? mapping.merlin_path : mapping.svn_path)
    end

    # Getting the target directory path is the same as source except for the
    # reversal of logic around whether to get svn_path or merlin_path based on
    # is_merlin? status

    def get_target_dir(name)
      mapping = get_mapping name
      context_path = @project_context.target
      context_path + (@project_context.is_merlin? ? mapping.svn_path : mapping.merlin_path)
    end

    def get_relative_target_dir(name)
      mapping = get_mapping name
      @project_context.is_merlin? ? mapping.svn_path : mapping.merlin_path
    end

    def is_recursive?(name)
      get_mapping(name).recurse
    end

    # General filesystem related methods

    def copy_dir(source_dir, target_dir, options = '')
      log_filename = Pathname.new(Dir.tmpdir) + "rake_transform.log"
      exec_f "robocopy \"#{source_dir}\" \"#{target_dir}\" #{DEFAULT_ROBOCOPY_OPTIONS} /LOG+:#{log_filename} #{options}"
    end

    def copy_to_temp_dir(name, temp_dir, extras = [])
      IronRuby.source_context do
        source = get_source_dir(name)
        target = get_relative_target_dir(name)

        if is_recursive? name
          source_dirs = source.filtered_subdirs(extras)
          source_dirs.each { |dir| copy_dir dir, temp_dir + target + dir.relative_path_from(source) }
        else
          copy_dir source, temp_dir + target
        end
      end
    end

    def del(name, *paths)
      dir = name.is_a?(Symbol) ? get_source_dir(name) : name
      Dir.chdir(dir) { paths.each { |path| exec_f "del #{path}" } }
    end

    def chdir(env, &b)
      dir = env.is_a?(Symbol) ? get_source_dir(env) : env
      Dir.chdir(dir) { instance_eval(&b) }
    end

    def rd(name)
      path = name.is_a?(Symbol) ? get_source_dir(name) : name
      FileUtils.rm_rf path
    end

    def mkdir(name)
      path = name.is_a?(Symbol) ? get_source_dir(name) : name
      FileUtils.mkdir_p path
    end

    def generate_temp_dir
      layout = Pathname.new(Dir.tmpdir) + 'layout'
      del   Pathname.new(Dir.tmpdir), 'rake_transform.log'
      rd    layout
      mkdir layout
      layout
    end

    # Source transformation related methods
    
    def diff_directories(temp_dir)
      source_dirs, target_dirs = [], []

      nodes = [:root, :gppg, :dlr_core, :dlr_libs, :ironruby, :libraries, :tests, :console, :generator, :test_runner, :scanner, :yaml, :libs]
      nodes.each do |node| 
        if is_recursive? node
          source_dirs += (temp_dir + get_relative_target_dir(node)).filtered_subdirs.map { |d| d.relative_path_from(temp_dir).downcase }

          # Target directory may not exist, so we only add if we find it there
          if get_target_dir(node).directory?
            target_dirs += get_target_dir(node).filtered_subdirs.map { |d| d.relative_path_from(@project_context.target).downcase }
          end
        else
          # This is also an unusual case - since there is a 1:1 mapping by
          # definition in a non-recursive directory mapping, this will be
          # flagged only as a change candidate and not an add or a delete.
          source_dirs << get_relative_target_dir(node).downcase

          # Target directory may not exist, so we only add if we find it there
          target_dirs << get_relative_target_dir(node).downcase if get_target_dir(node).directory?
        end
      end

      added             = source_dirs - target_dirs
      removed           = target_dirs - source_dirs
      change_candidates = source_dirs & target_dirs

      return added, removed, change_candidates
    end

    def push_to_target(temp_dir)
      rake_output_message "\n#{'=' * 78}\nApplying source changes to target source repository\n\n"
      rake_output_message "Computing directory structure changes ...\n"

      added, removed, change_candidates = diff_directories(temp_dir)

      dest = @project_context.target

      rake_output_message "Adding new directories to target source control\n"
      added.each do |dir| 
        copy_dir(temp_dir + dir, dest + dir)
        add(dest + dir)
      end

      rake_output_message "Deleting directories from target source control\n"
      removed.each do |dir|
        rd dest + dir
        delete dest + dir
      end

      rake_output_message "Copying files in changed directories to target source control\n"
      change_candidates.each do |dir|
        src_file_list = (temp_dir + dir).filtered_files
        dest_file_list = (dest + dir).filtered_files

        added             = src_file_list - dest_file_list
        removed           = dest_file_list - src_file_list
        change_candidates = src_file_list & dest_file_list

        added.each do |file|
          copy temp_dir + dir + file, dest + dir + file
          add dest + dir + file
        end

        removed.each do |file|
          delete dest + dir + file
        end

        change_candidates.each do |file|
          source_file = temp_dir + dir + file
          dest_file = dest + dir + file

          if !compare_file(source_file, dest_file)
            checkout dest_file
            copy source_file, dest_file
          end
        end
      end
    end

    # Compiler-related methods

    def resgen(base_path, resource_map)
      resource_map.each_pair do |input, output| 
        exec "resgen \"#{base_path + input.dup}\" \"#{build_path + output}\"" 
      end
    end

    def configuration
      ENV['configuration'].nil? ? :debug : ENV['configuration'].to_sym
    end

    def clr
      if ENV['mono'].nil?
        ENV['clr'].nil? ? :desktop : ENV['clr'].to_sym
      else
        :mono
      end
    end

    def platform
      ENV['platform'].nil? ? :windows : ENV['platform'].to_sym
    end

    def resolve_framework_path(file)
      Configuration.resolve_framework_path(clr, file)
    end

    def build_path
      get_source_dir(:build) + "#{clr == :desktop ? configuration : "#{clr}_#{configuration}"}"
    end

    def compiler_switches
      Configuration.get_switches(clr, configuration)
    end

    def references(refs, working_dir)
      references = Configuration.get_references(clr)
      refs.each do |ref|
        references << if ref =~ /^\!/ 
          resolve_framework_path(ref[1..ref.length])
        else
          (build_path + ref).relative_path_from(working_dir)
        end
      end unless refs.nil?
      references
    end

    def get_compile_path_list
      cs_proj_files = Dir['*.csproj']
      if cs_proj_files.length == 1
        doc = REXML::Document.new(File.open(cs_proj_files.first))
        result = doc.elements.collect("/Project/ItemGroup/Compile") { |c| "\"#{c.attributes['Include']}\"" }        
        result.delete_if { |e| e =~ /Silverlight\\SilverlightVersion.cs/ }
        result
      else
        raise ArgumentError.new("Found more than one .csproj file in directory! #{cs_proj_files.join(", ")}")
      end
    end

    def compile(name, args)
      working_dir = get_source_dir(name)
      build_dir = build_path

      Dir.chdir(working_dir) do |p|
        cs_args = ["out:\"#{build_dir + args[:output]}\""]
        cs_args += references(args[:references], working_dir).map { |ref| "r:\"#{ref}\"" }
        cs_args += compiler_switches
        cs_args += args[:switches] unless args[:switches].nil?

        unless args[:resources].nil?
          resgen working_dir, args[:resources] 
          args[:resources].each_value { |res| cs_args << "resource:\"#{build_path + res}\"" }
        end

        # Hack path to RubyTestKey.snk
        cs_args << 'keyfile:' + $rakefile_path + '\RubyTestKey.snk'

        switches = ''
        cs_args.each { |opt| switches << ' /' + opt }
        exec CS_COMPILER + switches + ' ' + get_compile_path_list.join(" ")
      end
    end

    # Project transformation methods
    
    def replace_output_path(contents, old, new)
      contents.gsub! Regexp.new(Regexp.escape("<OutputPath>#{old}</OutputPath>"), Regexp::IGNORECASE), "<OutputPath>#{new}</OutputPath>"
    end
    
    def replace_doc_path(contents, old, new)
      contents.gsub! Regexp.new(Regexp.escape("<DocumentationFile>#{old}</DocumentationFile>"), Regexp::IGNORECASE), "<DocumentationFile>#{new}</DocumentationFile>"
    end
    
    def replace_key_path(contents, old, new)
      contents.gsub! Regexp.new(Regexp.escape("<AssemblyOriginatorKeyFile>#{old}</AssemblyOriginatorKeyFile>"), Regexp::IGNORECASE), "<AssemblyOriginatorKeyFile>#{new}</AssemblyOriginatorKeyFile>"
    end
    
    def replace_import_project(contents, old, new)
      contents.gsub! Regexp.new(Regexp.escape("<Import Project=\"#{old}\" />"), Regexp::IGNORECASE), "<Import Project=\"#{new}\" />"
    end

    def transform_project(name, project)
      path = get_target_dir(name) + project
      rake_output_message "Transforming: #{path}"
      contents = path.read

      # Extract the project name from .csproj filename
      project_name = /(.*)\.csproj/.match(project)[1]

      if @project_context.is_merlin?
        contents.change_tag! 'SccProjectName'
        contents.change_tag! 'SccLocalPath'
        contents.change_tag! 'SccAuxPath'
        contents.change_tag! 'SccProvider'

        if block_given?
          yield contents
        else
          replace_output_path contents, '..\..\..\Bin\Debug\\', '..\..\build\debug\\'
          replace_output_path contents, '..\..\..\Bin\Release\\', '..\..\build\release\\'
          replace_output_path contents, '..\..\..\Bin\Silverlight Debug\\', '..\..\build\silverlight debug\\'
          replace_output_path contents, '..\..\..\Bin\Silverlight Release\\', '..\..\build\silverlight release\\'
          replace_key_path    contents, '..\..\..\MSSharedLibKey.snk', '..\..\RubyTestKey.snk'
        end
      else
        contents.change_tag! 'SccProjectName', 'SAK'
        contents.change_tag! 'SccLocalPath', 'SAK'
        contents.change_tag! 'SccAuxPath', 'SAK'
        contents.change_tag! 'SccProvider', 'SAK'

        if block_given?
          yield contents
        else
          replace_output_path contents, '..\..\build\debug\\', '..\..\..\Bin\Debug\\'
          replace_output_path contents, '..\..\build\release\\', '..\..\..\Bin\Release\\'
          replace_output_path contents, '..\..\build\silverlight debug', '..\..\..\Bin\Silverlight Debug\\'
          replace_output_path contents, '..\..\build\silverlight release', '..\..\..\Bin\Silverlight Release\\'
          replace_key_path    contents, '..\..\RubyTestKey.snk', '..\..\..\MSSharedLibKey.snk'
        end
      end
      path.open('w+') { |f| f.write contents }
    end
  end

  # The Rakefile must always be found in the root directory of the source tree.
  
  # If ENV['MERLIN_ROOT'] is defined, then we know that we are running on 
  # a machine with an enlistment in the MERLIN repository. This will enable
  # features that require a source context and a destination context (such as
  # pushing to / from MERLIN). Otherwise, destination context will always be
  # nil and we will throw on an attempt to do operations that require a
  # push.
  
  private
  def self.init_context
    @rakefile_dir = Pathname.new(File.dirname(File.expand_path(__FILE__)).downcase)

    if ENV['MERLIN_ROOT'].nil?
      # Initialize the context for an external contributor who builds from
      # a non-MERLIN command prompt
      @source = @rakefile_dir
      @target = nil
    else
      # Initialize @source and @target to point to the right places based
      # on whether we are within MERLIN_ROOT or SVN_ROOT
      @merlin_root = Pathname.new(ENV['MERLIN_ROOT'].downcase) + '../../' # hack for changes in TFS layout
      @ruby_root = @merlin_root + 'merlin/main/languages/ruby'
      
      if @rakefile_dir == @ruby_root
        @source = @merlin_root
        @target = SVN_ROOT
      elsif @rakefile_dir == SVN_ROOT
        @source = @rakefile_dir
        @target = @merlin_root
      else
        raise <<-EOF
          Rakefile is at #{@rakefile_dir}. This is neither the SVN_ROOT nor 
          the MERLIN_ROOT. Possible causes of this are running from a 
          non-MERLIN command prompt (where MERLIN_ROOT environment variable
          is defined) or if the SVN_ROOT constant in the Rakefile does not
          point to where you downloaded the SVN repository for IronRuby.
        EOF
      end
    end  

    @map = {}
    @initialized = true
  end

  def self.make_pathname(path)
    elements = path.split '/'
    raise "must be an an array with at least one element: #{elements}" if elements.length < 1
    result = Pathname.new elements.first
    (1..elements.length-1).each { |i| result += elements[i] }
    result
  end

  public
  def self.map(name, args)
    init_context unless @initialized
    @map[name] = Mapping.new(make_pathname(args[:merlin]), make_pathname(args[:svn]), (args[:recurse].nil? ? true : args[:recurse]))
  end

  def self.resolve(name)
    @map[name]
  end

  def self.is_merlin?
    @merlin_root == @source
  end

  def self.source_context(&b)
    context = CommandContext.new(:source, self)
    context.extend(is_merlin? ? SvnProvider : TfsProvider)
    context.instance_eval(&b)
    context
  end

  def self.target_context(&b)
    if @target.nil?
      raise <<-EOF
      Cannot invoke commands against target_context if you are not running in 
      a MERLIN_ROOT context. External folks should never see this error as they
      should never be running commands that require moving things between
      different contexts.
      EOF
    else
      # Note that this is a bit unusual - the source control commands in the
      # target are identical to the source control commands for the source. This
      # is due to the semantics of the operation. The source is always
      # authoritative in these kinds of push scenarios, so you'll never want to
      # mutate the source repository, only the target repository.
      context = CommandContext.new(:target, self)
      context.extend(is_merlin? ? SvnProvider : TfsProvider)
      context.instance_eval(&b)
      context
    end
  end

  def self.source
    @source
  end

  def self.target
    @target
  end
end

class IronRuby < ProjectContext
  map :root, :merlin => 'merlin/main/languages/ruby', :svn => '.', :recurse => false
  map :gppg, :merlin => 'merlin/main/utilities/gppg', :svn => 'bin', :recurse => false
  map :dlr_core, :merlin => 'ndp/fx/src/core/microsoft/scripting', :svn => 'src/microsoft.scripting.core'
  map :dlr_libs, :merlin => 'merlin/main/runtime/microsoft.scripting', :svn => 'src/microsoft.scripting'
  map :ironruby, :merlin => 'merlin/main/languages/ruby/ruby', :svn => 'src/ironruby'
  map :libraries, :merlin => 'merlin/main/languages/ruby/libraries.lca_restricted', :svn => 'src/IronRuby.Libraries'
  map :yaml, :merlin => 'merlin/external/languages/ironruby/yaml/ironruby.libraries.yaml', :svn => 'src/yaml'
  map :tests, :merlin => 'merlin/main/languages/ruby/tests', :svn => 'tests/ironruby'
  map :console, :merlin => 'merlin/main/languages/ruby/console', :svn => 'utils/ironruby.console'
  map :generator, :merlin => 'merlin/main/languages/ruby/classinitgenerator', :svn => 'utils/ironruby.classinitgenerator'
  map :test_runner, :merlin => 'merlin/main/languages/ruby/ironruby.tests', :svn => 'utils/IronRuby.Tests'
  map :scanner, :merlin => 'merlin/main/languages/ruby/utils/ironruby.libraries.scanner', :svn => 'utils/ironruby.libraries.scanner'
  map :build, :merlin => 'merlin/main/bin', :svn => 'build'
  map :libs, :merlin => 'merlin/main/languages/ruby/libs', :svn => 'libs'
end

# Spec runner helpers

class MSpecRunner
  attr_accessor :files, :examples, :expectations, :failures, :errors, :summaries
  attr_reader :tempdir

  SUMMARY_PARSER = /(\d+) file[s]*, (\d+) example[s]*, (\d+) expectation[s]*, (\d+) failure[s]*, (\d+) error[s]*/

  def initialize
    @files = 0
    @examples = 0
    @expectations = 0
    @failures = 0
    @errors = 0
    @summaries = []
    @tempdir = Dir.tmpdir
  end

  def regression(ruby, klass)
    cmd = "#{ruby} #{UserEnvironment::MSPEC}/bin/mspec-ci -fm -X #{UserEnvironment::TAGS} #{UserEnvironment::RUBYSPEC}/1.8/core/#{klass} > #{tempdir}/out.txt"
    system cmd
    File.open("#{tempdir}/out.txt", 'r') do |f|
      lines = f.readlines
      lines.each do |line|
        if SUMMARY_PARSER =~ line
          m = SUMMARY_PARSER.match(line)
          @files += m[1].to_i
          @examples += m[2].to_i
          @expectations += m[3].to_i
          @failures += m[4].to_i
          @errors += m[5].to_i
          @summaries << "#{klass}: #{m[1].to_i} files, #{m[2].to_i} examples, #{m[3].to_i} expectations, #{m[4].to_i} failures, #{m[5].to_i} errors" 
        end
      end
    end
  end

  def regression_all_core(ruby)
    Dir["#{UserEnvironment::RUBYSPEC}/1.8/core/*"].each do |path|
      klass = File.basename(path)
      regression ruby, klass
    end
  end

  def why_regression(ruby, klass)
    cmd = "#{ruby} #{UserEnvironment::MSPEC}/bin/mspec-ci -fs -X #{UserEnvironment::TAGS} #{UserEnvironment::RUBYSPEC}/1.8/core/#{klass}"
    system cmd
  end

  def why_regression_all_core(ruby)
    Dir["#{UserEnvironment::RUBYSPEC}/1.8/core/*"].each do |path|
      klass = File.basename(path)
      detailed ruby, klass
    end
  end

  def test(ruby, klass)
    cmd = "#{ruby} #{UserEnvironment::MSPEC}/bin/mspec-ci -fs #{UserEnvironment::RUBYSPEC}/1.8/core/#{klass}"
    system cmd
  end

  def test_all_core(ruby)
    Dir["#{UserEnvironment::RUBYSPEC}/1.8/core/*"].each do |path|
      klass = File.basename(path)
      developer ruby, klass
    end
  end

  def baseline(ruby, klass)
    cmd = "#{ruby} #{UserEnvironment::MSPEC}/bin/mspec-tag -X #{UserEnvironment::TAGS} #{UserEnvironment::RUBYSPEC}/1.8/core/#{klass} > #{tempdir}/out.txt"
    puts cmd
    system cmd
  end

  def baseline_all_core(ruby)
    Dir["#{UserEnvironment::RUBYSPEC}/1.8/core/*"].each do |path|
      klass = File.basename(path)
      tag ruby, klass
    end
  end

  def report
    summaries.each { |s| puts s }
    puts "\nSummary:\n"
    puts "#{summaries.length} types, #{files} files, #{examples} examples, #{expectations} expectations, #{failures} failures, #{errors} errors"
  end
end

class UserEnvironment

  # Find path to named executable

  def self.find_executable(executable)
    executable.downcase!  
    result = []
    search_path = ENV['PATH'].split(';')
    search_path.each do |dir|
      path = dir.gsub '\\', '/'
      Dir[path + '/*.exe'].each do |file|
        file_path = Pathname.new(file)
        result << file_path.dirname if file_path.basename.downcase == executable
      end
    end
    result
  end

  TAGS     = "#{ENV['USERPROFILE']}\\dev\\ironruby-tags".gsub('\\', '/')
  RUBYSPEC = "#{ENV['USERPROFILE']}\\dev\\rubyspec".gsub('\\', '/')
  MSPEC    = "#{ENV['USERPROFILE']}\\dev\\mspec".gsub('\\', '/')

  def initialize
    path_to_config = ENV['USERPROFILE'] + '/.irconfig.rb'
    load path_to_config if File.exist? path_to_config

    unless defined?(UserEnvironment::MRI)
      ruby_exe_paths = UserEnvironment.find_executable 'ruby.exe'
      if ruby_exe_paths.length == 1
        UserEnvironment.const_set(:MRI, Pathname.new(ruby_exe_paths.first + '\..\\'))
      else
        raise ArgumentError.new("Found more than one version of ruby.exe on your path #{ruby_exe_paths.join(', ')}") 
      end
    end
  end
end

UE = UserEnvironment.new

