module AR::Game::Gateway

  def self.give_off_entered_or_failed_on_travelled
    r = AR::Events::Resolution.new event_class: AR::Events::ARObjectTravelledEvent, as: "gateway" do |e|
      gateway = e.gateway
      ar_object = e.traveller

      destination = e.gateway.destination
      if gateway.all_blockers.empty?
        ar_object.parent.rem_inhabitant ar_object
        e.engine.create AR::Events::ARObjectEnteredInhabitableEvent, ({gateway: gateway, inhabitable: destination, enterer: ar_object})
      else
        e.engine.create AR::Events::ARObjectEntryBlockedEvent, ({gateway: gateway, blocker: gateway.all_blockers.first, inhabitable: destination, traveller: ar_object})
      end
    end

    r
  end

  def self.remove_blocker_on_blocker_removed
    r = AR::Events::Resolution.new event_class: AR::Events::BlockerRemoved, as: "gateway" do |e|
      gateway = e.gateway


      e.gateway.remove_blocker e.blocker
    end
    r
  end

  def blocker_placement_for ar_object
    p = ""
    blocker_placement_nodes.each do |placement, blocker_ids|
      p = placement and break unless blocker_ids.select{|id| id == ar_object.id}.empty?
    end
    p
  end





  def destination
    self.world.find self.destination_id
  end

  def destination=(d)
    self.destination_id = d.id
  end
  def  self.init ar_object 

    ar_object.class.attr_serializeable :blocker_placement_nodes
    ar_object.class.attr_serializeable :destination_id
    #    structure should be "placement => object_ids"
    ar_object.blocker_placement_nodes = {} 



  end

  #since this is a more abstract behavior (included in other behaviors)
  #the placements should really be namespaced by the behaviors.
  #e.g. a type implementing gateway and inhabitable will break
  #because placements for either blockers or  will overlap
  def add_blocker o, placement
    self.blocker_placement_nodes[placement] = [] if self.blocker_placement_nodes[placement].nil?
    self.blocker_placement_nodes[placement] << o.id
    o.parent = self
  end

  def rem_blocker o 
    self.blocker_placement_nodes[blocker_placement_for(o)] = [] if self.blocker_placement_nodes[blocker_placement_for(o)].nil?
    arr  = self.blocker_placement_nodes[blocker_placement_for(o)]
    arr.delete o.id
    self.blocker_placement_nodes[blocker_placement_for(o)] = arr
    puts "removed the blockage"
    o.parent = nil 
  end

  def remove_blocker o 
    rem_blocker o
  end


  def self.extended ar_object
    self.init ar_object

    ar_object.resolutions << self.extend_perception_to_blockers_on_perception_of_self_changed
    ar_object.resolutions << give_off_entered_or_failed_on_travelled
    ar_object.resolutions <<  remove_blocker_on_blocker_removed
  end 

  def self.extend_perception_to_blockers_on_perception_of_self_changed
    r = AR::Events::Resolution.new event_class: AR::Events::PerceptionAccountChangedEvent, as: "discoverable" do |e|
      perceiver = e.perceiver
      discoverable = e.discoverable
      change = e.change
      events = []

      unless e.extended
        discoverable.all_blockers.each do |ar_object|
          events.push e.engine.create(AR::Events::PerceptionAccountChangedEvent, ({extended: true, perceiver: perceiver, discoverable: ar_object, change: perception_change_extended_to_blockers(change) }))
        end
      end
      events
    end
    r
  end





  def self.perception_change_extended_to_blockers change
    change
  end

  def all_blockers
    blocker_placement_nodes.values.flatten.map{|id| self.world.find id}
  end

end

