module Blather
class Stanza

  # # Iq Stanza
  #
  # [RFC 3920 Section 9.2.3 - IQ Semantics](http://xmpp.org/rfcs/rfc3920.html#rfc.section.9.2.3)
  #
  # Info/Query, or IQ, is a request-response mechanism, similar in some ways
  # to HTTP. The semantics of IQ enable an entity to make a request of, and
  # receive a response from, another entity. The data content of the request
  # and response is defined by the namespace declaration of a direct child
  # element of the IQ element, and the interaction is tracked by the
  # requesting entity through use of the 'id' attribute. Thus, IQ interactions
  # follow a common pattern of structured data exchange such as get/result or
  # set/result (although an error may be returned in reply to a request if
  # appropriate).
  #
  # ## "ID" Attribute
  #
  # Iq Stanzas require the ID attribute be set. Blather will handle this
  # automatically when a new Iq is created.
  #
  # ## "Type" Attribute
  #
  # * `:get` -- The stanza is a request for information or requirements.
  #
  # * `:set` -- The stanza provides required data, sets new values, or
  #   replaces existing values.
  #
  # * `:result` -- The stanza is a response to a successful get or set request.
  #
  # * `:error` -- An error has occurred regarding processing or delivery of a
  #   previously-sent get or set (see Stanza Errors).
  #
  # Blather provides a helper for each possible type:
  #
  #     Iq#get?
  #     Iq#set?
  #     Iq#result?
  #     Iq#error?
  #
  # Blather treats the `type` attribute like a normal ruby object attribute
  # providing a getter and setter. The default `type` is `get`.
  #
  #     iq = Iq.new
  #     iq.type               # => :get
  #     iq.get?               # => true
  #     iq.type = :set
  #     iq.set?               # => true
  #     iq.get?               # => false
  #
  #     iq.type = :invalid    # => RuntimeError
  #
  # @handler :iq
  class Iq < Stanza
    # @private
    VALID_TYPES = [:get, :set, :result, :error].freeze

    register :iq

    # @private
    def self.import(node)
      klass = nil
      node.children.detect do |e|
        ns = e.namespace ? e.namespace.href : nil
        klass = class_from_registration(e.element_name, ns)
      end

      if klass && klass != self
        klass.import(node)
      else
        new(node[:type]).inherit(node)
      end
    end

    # Create a new Iq
    #
    # @param [Symbol, nil] type the type of stanza (:get, :set, :result, :error)
    # @param [Blather::JID, String, nil] jid the JID of the inteded recipient
    # @param [#to_s] id the stanza's ID. Leaving this nil will set the ID to
    #                the next unique number
    def self.new(type = nil, to = nil, id = nil)
      node = super :iq
      node.type = type || :get
      node.to = to
      node.id = id || self.next_id
      node
    end

    # Check if the IQ is of type :get
    #
    # @return [true, false]
    def get?
      self.type == :get
    end

    # Check if the IQ is of type :set
    #
    # @return [true, false]
    def set?
      self.type == :set
    end

    # Check if the IQ is of type :result
    #
    # @return [true, false]
    def result?
      self.type == :result
    end

    # Check if the IQ is of type :error
    #
    # @return [true, false]
    def error?
      self.type == :error
    end

    # Ensures type is :get, :set, :result or :error
    #
    # @param [#to_sym] type the Iq type. Must be one of VALID_TYPES
    def type=(type)
      if type && !VALID_TYPES.include?(type.to_sym)
        raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}"
      end
      super
    end

    # Overrides the parent method to ensure the reply is of type :result and that
    # all children are removed.
    #
    # @param [Hash] opts options to pass to reply!
    # @option opts [Boolean] :remove_children Wether or not to remove child nodes when replying
    #
    # @return [self]
    def reply!(opts = {})
      opts = {:remove_children => true}.merge opts
      super
      self.type = :result
      self
    end
  end

end
end
