require 'abbrev'
begin
  require 'ftools'
rescue LoadError
  #ignore, not needed under ruby 1.9
end

class BaseFinder

  attr_accessor :bin_dir
  attr_reader :command

  DEFAULT_BIN_DIR = 'script'

  def self.find_and_execute(command, opts = {})
    bin_dir = opts[:bin_dir] ||= nil
    r_or_s = opts[:prefix]
    command = command.split(' ') if command.is_a?(String)
    unless running_command_for_current_rails_version?(r_or_s)
      puts "You are attempting to run a command for a version of rails you are not currently running. Please verify rails version" 
      puts "Current rails version is #{`rails -v`}"
      exit
    end
    finder = new(command, bin_dir).execute_command
  end

  def self.running_command_for_current_rails_version?(command)
    if command =~ /r/i or command =~ /s/i
      right_version = command =~ /r/i ? Regexp.new(/rails\s(3\.)/i) : Regexp.new(/rails\s(2\.)/i)

      return !right_version.match(`rails -v`).nil?
    end

    #unknown command. Assuming that this is the right call and they know what they are doing
    return true
  end

  def initialize(command, bin_dir = nil)
    @command = command
    self.bin_dir = bin_dir || DEFAULT_BIN_DIR
  end

  def execute_command
    raise NotImplementedError.new("You must implement execute_command for subclasses of BaseFinder!")
  end

  protected
  def cmd_not_found
    raise NotImplementedError.new("You must implement cmd_not_found for subclasses of BaseFinder!")
  end

  #Prefix and string join utilities
  def unique_prefixes(possibles)
    all_prefixes = possibles.abbrev
    all_prefixes.keys.sort.inject({ }) do |prefixes, abbrev|
      prefixes[all_prefixes[abbrev]] ||= abbrev
      prefixes
    end
  end

  def commands_to_command_string(commands)
    commands.collect {|x| "'#{x}'"}.join(' ')
  end

  #Command utilities
  def bin_dir_not_found
    puts "No #{bin_dir} dir found"
  end

  def too_many_cmds_found(possibles)
    exec_name = File.basename($0)
    puts "'#{exec_name} #{command.first}' was ambiguous. Try:"
    unique_prefixes(possibles).each do |cmd, prefix|
      puts "\t'#{exec_name} #{prefix}' for '#{yield(cmd)}'"
    end
  end

  def find_command_in_dir(dir)
    cmd = command.first

    Dir.chdir(dir) do
      if cmd and File.executable?(cmd)
        possibles = [cmd]
      else
        possibles = Dir.glob("#{cmd}*").select {|f| File.executable?(f)}
      end

      if possibles.size == 0
        nil
      elsif possibles.size == 1
        File.expand_path(possibles.first)
      else
        possibles
      end
    end
  end

  #Bin Utilities
  def find_bin_dir(starting_dir = '.', last_dir = nil)
    starting_dir = File.expand_path(starting_dir)

    Dir.chdir(starting_dir) do
      if starting_dir == last_dir
        nil
      elsif bin_dir_exists_in_pwd?
        File.join(starting_dir, bin_dir)
      else
        find_bin_dir('..', starting_dir)
      end
    end
  end

  def bin_dir_exists_in_pwd?
    File.exists?(bin_dir) and
      File.directory?(bin_dir) and
      File.readable?(bin_dir)
  end

  def cmd_exec(cmd)
    cmd.strip!
    puts "--> calling '#{cmd}'"
    exec cmd
  end
  
  def execute_command_if_singleton(commands)
    commands = commands.respond_to?(:to_a) ? commands.to_a : [commands] 
    if commands.empty?
      cmd_not_found
    elsif commands.size > 1
      too_many_cmds_found(commands)
    else
      command.shift
      cmd_exec "#{commands.first} #{commands_to_command_string(command)}"
    end
  end
end