# To add Jabber notifications you must have xmpp4r gem installed.
# Configure your watches like this:
#
#   God::Contacts::Jabber.settings = { :jabber_id => 'sender@example.com',
#                                      :password  => 'secret' }
#   God.contact(:jabber) do |c|
#     c.name      = 'Tester'
#     c.jabber_id = 'receiver@example.com'
#     c.group     = 'developers'
#   end

module XMPP4R
  require 'xmpp4r'
  include Jabber
end

module God
  module Contacts
    class Jabber < Contact      
      class << self
        attr_accessor :settings, :format, :client
      end
      
      self.format = lambda do |message, priority, category, host|
        text  = "Message: #{message}\n"
        text += "Host: #{host}\n"         if host
        text += "Priority: #{priority}\n" if priority
        text += "Category: #{category}\n" if category
        return text
      end
      
      attr_accessor :jabber_id
      
      def valid?
        valid = true
      end
      
      def notify(message, time, priority, category, host)
        connect
        
        body = Jabber.format.call message, priority, category, host
        
        message = XMPP4R::Message::new self.jabber_id, body
        message.set_type :normal
        message.set_id '1'
        message.set_subject 'God'
        
        self.send!(message)
        
        self.info = "sent jabber message to #{self.jabber_id}"
      rescue => e
        puts e.message
        puts e.backtrace.join("\n")
        
        self.info = "failed to send jabber message to #{self.jabber_id}: #{e.message}"
      end
      
      def send!(msg)
        attempts = 0
        begin
          attempts += 1
          client.send(msg)
        rescue Errno::EPIPE, IOError => e
          sleep 1
          disconnect!
          reconnect!
          retry unless attempts > 3
          raise e
        rescue Errno::ECONNRESET => e
          sleep (attempts^2) * 60 + 60
          disconnect!
          reconnect!
          retry unless attempts > 3
          raise e
        end
      end
      
      def connect
        connect! unless connected?
      end
      
      def connected?
        connected = client.respond_to?(:is_connected?) && client.is_connected?
        return connected
      end
      
      def connect!
        disconnect! if connected?

        @connect_mutex ||= Mutex.new
        # don't try to connect if another thread is already connecting.
        return if @connect_mutex.locked?
        @connect_mutex.lock
        
        jabber_id = XMPP4R::JID::new "#{Jabber.settings[:jabber_id]}/God"
        jabber_client = XMPP4R::Client::new jabber_id
        jabber_client.connect Jabber.settings[:host]
        jabber_client.auth Jabber.settings[:password]
        self.client = jabber_client
        
        @connect_mutex.unlock
      end
      
      def disconnect!
        if client.respond_to?(:is_connected?) && client.is_connected?
          begin
            client.close
          rescue Errno::EPIPE, IOError => e
            self.info "Failed to disconnect: #{e}"
            nil
          end
        end
        client = nil
      end

      def client
        Jabber.client
      end

      def client=(jc)
        Jabber.client = jc
      end

    end
  end
end