require "colorize"
class AR::Events::Engine 
  attr_accessor :test_mode
  attr_accessor :subscriptions
  attr_accessor :event_stack
  attr_accessor :step_stack
  attr_accessor :last_event_stack
  attr_accessor :world

  def initialize world 
    @world = world 

    #stack of events to resolve
    @event_stack = []

    #stack of events to resolve after events on a tick have cleared
    @step_stack = []

    @subscriptions = []

    @last_event_stack = []
  end



  def create event_class, params = {}
    params[:engine] = self
    e = event_class.new params
    e
  end 

  def trigger event_class, params = {}
    params[:engine] = self
    e = event_class.new params
    pipe e
  end 

  def trigger_after_step event_class, params = {}
    params[:engine] = self
    e = event_class.new params
    after_step e
  end 

  def pipe e
    self.event_stack = self.event_stack + [e].flatten
  end

  def after_step e
    step_stack.push e
  end

  def add_subscription subscription
    subscriptions << subscription 
  end

  def rem_subscription subscription 
    subscriptions.delete subscription
  end

  def remove_subscription subscription 
    subscriptions.delete s
  end


  #This really should have a few tests
  def tick!
    #event_stack = [] if event_stack.nil?
    @last_event_stack = []

    until self.event_stack.empty? 
      begin
      e = self.event_stack.shift
      next unless e.class.ancestors.include? AR::Events::Event

      if e.class == AR::Events::GameOverEvent
        return false
      end

      AR.log "ENGINE: #{e.class.to_s.split("::").last} resolving for #{e.debug}"
      s_results = subscription_results e
      r_results = resolution_results e 

      unless s_results.nil? or s_results.empty?
        self.event_stack += s_results
      end

      unless r_results.nil? or r_results.empty?
        self.event_stack += r_results 
      end
      
#      AR.put_s "Player senses it? (#{e.sensed_by? world.player})".colorize :white
      if e.sensed_by? world.player
        world.player.narrate e unless e.dont_narrate
      end
      
      
      
      @last_event_stack << e
      rescue Exception => e
        AR.log "ERROR in #{self.class}: #{e} #{e.backtrace.join("\n")}"
        puts "ERROR in #{self.class}: #{e} #{e.backtrace.join("\n")}".colorize :red
      end
    end 
    e = step_stack.pop

    #make next round occur automatically
    if e.nil?
      #e = AR::Game::PlayerBreakMenu.new(world.player).present unless self.test_mode
      e = self.create AR::Events::ClockTickEvent, ({world: world}) if e.nil?
    end

    self.pipe e
    return true
  end

  def debug_event_stack
    event_stack.map{|e| e.to_json}
  end

  def debug_step_stack
    step_stack.map{|e| e.to_json}
  end

  def subscription_results e

    relevant = subscriptions.select{|s| s.is_for?(e.class) }
    results = relevant.map do |s| 
      s.call(e, s) 
    end

    results.flatten.compact
  end

  def resolution_results e
    begin
    results = e.resolve!
    [results].flatten.compact
    rescue Exception => e
      AR.log "ERROR: #{e} #{e.backtrace.join("\n")}"
    end
  end

  def game_over!
    self.trigger AR::Events::GameOverEvent
  end

  def take_a_break!
    AR.put_s "--press any key to continue--"
    AR::Input.take_key
  end

  def remove_subscriptions_for ar_object
    common = ar_object.subscriptions & self.subscriptions
    common.each{|s| self.rem_subscription s }
  end

end #class Events::Engine


