module AR::Game::Inhabitable 


  def  self.init ar_object 

    ar_object.class.attr_serializeable :inhabitant_placement_nodes
    #    structure should be "placement => object_ids"
    ar_object.inhabitant_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 inhabitants or  will overlap
  def add_inhabitant o, placement
    self.inhabitant_placement_nodes[placement] = [] if self.inhabitant_placement_nodes[placement].nil?
    self.inhabitant_placement_nodes[placement] << o.id
    o.parent = self
  end

  def rem_inhabitant o 
    self.inhabitant_placement_nodes[inhabitant_placement_for(o)] = [] if self.inhabitant_placement_nodes[inhabitant_placement_for(o)].nil?
    self.inhabitant_placement_nodes[inhabitant_placement_for(o)].delete o.id
    o.parent = nil 
  end

  def remove_inhabitant o 
    rem_inhabitant o
  end


  def self.extended ar_object
    self.init ar_object

    ar_object.resolutions << self.on_entered
    ar_object.resolutions << self.extend_perception_to_inhabitants_on_perception_of_self_changed
  end 

  def self.extend_perception_to_inhabitants_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_inhabitants.each do |ar_object|
            events.push e.engine.create(AR::Events::PerceptionAccountChangedEvent, ({extended: true, perceiver: perceiver, discoverable: ar_object, change: perception_change_extended_to_inhabitants(change) }))
        end
      end
      events
    end
    r
  end


  def self.on_entered
    r = AR::Events::Resolution.new as: "inhabitable", event_class: AR::Events::ARObjectEnteredInhabitableEvent do |e|
      enterer = e.enterer
      inhabitable = e.inhabitable
      placement = e.placement || ""
      events = []

      #add an event for each inhabitant
      inhabitable.all_inhabitants.map do |i| 
        if i.has_behavior? AR::Game::Perception
          evt = AR::Events::PerceptionAccountChangedEvent.new perceiver: i, discoverable: enterer, change: i.perception_lvl
          events.push evt 
        end
      end 

      inhabitable.add_inhabitant enterer, placement


      events.push e.engine.create(AR::Events::PerceptionAccountChangedEvent, ({perceiver: enterer, discoverable: inhabitable, change: enterer.perception_lvl })) if enterer.has_behavior? AR::Game::Perception
      events
    end
    r
  end



      


  def inhabitant_placement_for ar_object
    p = ""
    inhabitant_placement_nodes.each do |placement, inhabitant_ids|
      p = placement and break unless inhabitant_ids.select{|id| id == ar_object.id}.empty?
    end
    p
  end


  def self.perception_change_extended_to_inhabitants change
    (change / 2) + 1
  end

  def all_inhabitants
    inhabitant_placement_nodes.values.flatten.map{|id| self.world.find id}
  end

end
