require 'autotest'
require 'tempfile'

module Autotest::CucumberMixin
  def self.included(receiver)
    receiver::ALL_HOOKS << [:run_features, :ran_features]
  end
  
  attr_accessor :scenarios_to_run
  
  def initialize
    super
    reset_features
  end
  
  def run
    hook :initialize
    reset
    reset_features
    add_sigint_handler

    self.last_mtime = Time.now if $f

    loop do # ^c handler
      begin
        get_to_green
        if self.tainted then
          rerun_all_tests
          rerun_all_features if all_good
        else
          hook :all_good
        end
        wait_for_changes
        # Once tests and features are green, reset features every
        # time a file is changed to see if anything breaks.
        reset_features
      rescue Interrupt
        break if self.wants_to_quit
        reset
        reset_features
      end
    end
    hook :quit
  end
  
  def all_features_good
    scenarios_to_run == []
  end
  
  def get_to_green
    begin
      super
      run_features
      wait_for_changes unless all_features_good
    end until all_features_good
  end
  
  def rerun_all_features
    reset_features
    run_features
  end
  
  def reset_features
    self.scenarios_to_run = :all
  end
    
  def run_features
    hook :run_features
    Tempfile.open('autotest-cucumber') do |dirty_scenarios_file|
      cmd = self.make_cucumber_cmd self.scenarios_to_run, dirty_scenarios_file.path
      return if cmd.empty?
      puts cmd unless $q
      system cmd
      self.scenarios_to_run = dirty_scenarios_file.readlines.map { |l| l.chomp }
      self.tainted = true unless self.scenarios_to_run == []
    end
    hook :ran_features
  end
  
  def make_cucumber_cmd(scenarios_to_run, dirty_scenarios_filename)
    return '' if scenarios_to_run == []
    
    profiles = YAML.load_file("cucumber.yml").keys rescue []
    
    profile ||= "autotest-all" if profiles.include?("autotest-all") and scenarios_to_run == :all
    profile ||= "autotest"     if profiles.include?("autotest")
    profile ||= nil
    
    if profile
      args = ["--profile", profile]
    else
      args = %w{features --format} << (scenarios_to_run == :all ? "progress" : "pretty")
    end
    args += %w{--format autotest --out} << dirty_scenarios_filename
    args = args.join(' ')
    
    if scenarios_to_run == :all
      scenario_args = nil
    else
      scenario_args = scenarios_to_run.map { |s| "-s '#{s}'" }.join(' ')
    end
    
    return "#{cucumber} #{args} #{scenario_args}"
  end
  
  def cucumber
    File.exist?("script/cucumber") ? "script/cucumber" : "cucumber"
  end
end