module AR::Game::AiControl

  class Desire

    attr_accessor :agent

    #the block that should run when this desire prevails
    attr_accessor :on_choose

    #allows multiple desires at once competing
    attr_accessor :priority

    #changers change the state of the desire
    attr_accessor :resolution_changers
    attr_accessor :subscription_changers

    #this is used when calling the desire for the next action.  returns an action
    attr_accessor :state

    def initialize params={}, &block
      self.agent = params[:agent] 
      self.priority = params[:priority] || 0
      self.resolution_changers = params[:resolution_changers] || []
      self.subscription_changers = params[:subscription_changers] || []
      self.state = params[:state] || []
      self.on_choose = block
    end 

    def call *args
      self.on_choose.call(*args)
    end

    def assign_to ar_object
      #could also add subscriptions to the object's engine
      ar_object.resolutions   = ar_object.resolutions + resolution_changers
      eng = ar_object.world.events_engine

      subscription_changers.map{|s| eng.add_subscription(s) }
      ar_object
    end

    def remove_from ar_object
      common = ar_object.resolutions & resolution_changers
      ar_object.resolutions = ar_object.resolutions - common

      eng = ar_object.world.events_engine
      subscription_changers.map{|s| eng.rem_subscription(s) }
      ar_object
    end
  end


  class ToDefendSelf < Desire
    def initialize agent
      c = AR::Events::ResolutionDesireChanger.new desire: self, event_class: AR::Events::HitAttempt, as: "hitee" do |e, desire|
        hitter = e.hitter
        desire.state[:assailant_id] = hitter.id
        desire.priority = 10
      end
      changers = [c]


      super priority: 0, resolution_changers: changers, state:({assailant_id: nil}), agent: agent do |agent|
        if self.assailant_id.nil?
          nil
        else
          t = AR::Game::AttackAction.new(agent) 

          #ai should be smarter about this than choosing last tech...:
          g=t.parameter("technique")
          g.argument = agent.attack_techniques.last

          g=t.parameter("target")
          g.argument = self.assailant 

          t
        end
      end

      assign_to agent
    end

    def assailant
      agent.world.find assailant_id
    end

    def assailant_id
      state[:assailant_id]
    end

  end


  class ToHurtCharacters < Desire
    def initialize agent
      c = AR::Events::ResolutionDesireChanger.new desire: self, event_class: AR::Events::CharacterEnteredBattle, as: "character" do |e, desire|
        #later can do validations
        target = e.hostile_character
        #this should really be a hostility check?

        desire.state[:target_id] = target.id if desire.state[:target].nil?
        desire.priority = 10
      end

      #add a changer which responds to character death, and lowers desire priority temporarily
      c1 = AR::Events::ResolutionDesireChanger.new desire: self, event_class: AR::Events::DiscoveryLostEvent, as: "perceiver" do |e, desire|
        #later can do validations
        AR.log("no longer a desire to hurt")
        target = e.ar_object
        desire.state[:target_id] = nil 
        desire.priority = 0
      end
      changers = [c, c1]

      super priority: 0, resolution_changers: changers, state:({target_id: nil}), agent: agent do |agent|
        if self.target_id.nil?
          nil
        else
          t = AR::Game::AttackAction.new(agent) 

          #ai should be smarter about this than choosing last tech...:
          g=t.parameter("technique")
          g.argument = agent.attack_techniques.last

          g=t.parameter("target")
          g.argument = self.target

          t
        end
      end

      assign_to agent
    end

    def target 
      @target ||= agent.world.find target_id
    end

    def target_id
      state[:target_id]
    end
  end




  #try to move to the forest, simple, static desire to move to the forest
  class ExampleMoveAroundDesire < Desire
    def initialize agent
      c = AR::Events::DesireChanger.new desire: self, event_class: AR::Events::ARObjectEnteredInhabitableEvent, as: "enterer" do |e, desire|
          #silly hardcoded just for example
          case e.inhabitable.id
          when "clearing"
            desire.state[:next_move_gateway_id] = "inn_door_leading_in"
          end
      end
      changers = [c]
      super priority: 0, changers: changers, state:({next_move_gateway_id: "path_to_clearing"}), agent: agent do |agent|
        t = AR::Game::TravelAction.new(agent) 

        g=t.parameter("gateway")
        g.argument = agent.world.find self.state[:next_move_gateway_id]

        t
      end

      assign_to agent
    end
  end

end
