#!/usr/bin/env ruby

require 'xmpp4r/framework/bot'
require 'xmpp4r/pubsub'
require 'xmpp4r/vcard'
require 'ramaze'

if ARGV.size < 3
  puts "Usage: #{$0} <HTTP port> <JID> <Password> [Status message]"
  exit
end
HTTP_PORT = ARGV.shift.to_i
JID = ARGV.shift
PASSWORD = ARGV.shift
STATUS_MESSAGE = ARGV.shift

def xml_namespaces(filename, excludes=[])
  namespaces_recursive = nil
  namespaces_recursive = lambda { |element|
    namespaces = element.namespaces
    element.each_element { |child|
      namespaces.merge!(namespaces_recursive.call(child))
    }
    namespaces
  }

  root = REXML::Document.new(File.new(filename)).root
  all = namespaces_recursive.call(root)
  res = {}
  all.each { |prefix,uri|
    res[prefix] = uri unless excludes.include? prefix
  }
  res
end

Jabber::debug = true

class VcardCache < Jabber::Vcard::Helper
  attr_reader :vcards

  def initialize(stream)
    super
    @vcards = {}
  end

  def get(jid)
    unless @vcards[jid]
      begin
        @vcards[jid] = super
      rescue Jabber::ServerError
        @vcards[jid] = :error
      end
    end

    @vcards[jid]
  end

  def get_until(jid, timeout=10)
    begin
      Timeout::timeout(timeout) {
        get(jid)
      }
    rescue Timeout::Error
      @vcards[jid] = :timeout
    end

    @vcards[jid]
  end
end

MAX_ITEMS = 50
$jid_items = []

$bot = Jabber::Framework::Bot.new(JID, PASSWORD)
class << $bot
  def accept_subscription_from?(jid)
    roster.add(jid, nil, true)
    true
  end
end
$vcards = VcardCache.new($bot.stream)
xml_namespaces('index.xsl', %w(xmlns xsl pa j p)).each { |prefix,node|
        $bot.add_pep_notification(node) do |from,item|
          from.strip!
          item.add_namespace(Jabber::PubSub::NS_PUBSUB)
          item.attributes['node'] = node
          is_duplicate = false
          $jid_items.each { |jid1,item1|
            if jid1.to_s == from.to_s and node == item1.attributes['node']
              is_duplicate = (item.to_s == item1.to_s)
              break
            end
          }
          unless is_duplicate
            $jid_items.unshift([from, item])
            $jid_items = $jid_items[0..(MAX_ITEMS-1)]
          end
        end
      }

$bot.set_presence(nil, STATUS_MESSAGE || "Busy aggregating PEP events...")

class WebController < Ramaze::Controller
  map '/'
  template_root __DIR__
  engine :XSLT

  def index
    "<?xml version='1.0' encoding='UTF-8'?>" +
      "<items xmlns:jabber='jabber:client' jabber:to='#{Jabber::JID.new(JID).strip}'>" +
      $jid_items.collect do |jid,item_orig|
        item = item_orig.deep_clone
        item.attributes['jabber:from'] = jid.to_s
        vcard = $vcards.get_until(jid)
        if vcard.kind_of? Jabber::Vcard::IqVcard
          item.attributes['jabber:from-name'] = vcard['NICKNAME'] || vcard['FN'] || jid.node
          item.attributes['jabber:has-avatar'] = (vcard['PHOTO/TYPE'] and
                                                  vcard['PHOTO/BINVAL']) ? 'true' : 'false'
        else
          item.attributes['jabber:from-name'] = jid.node
          item.attributes['jabber:has-avatar'] = 'false'
        end
        item
      end.join +
      "</items>"
  end

  def avatar(jid)
    trait :engine => :None

    vcard = $vcards.get_until(jid)
    if vcard.kind_of? Jabber::Vcard::IqVcard
      if vcard['PHOTO/TYPE'] and vcard.photo_binval
        response['Content-Type'] = vcard['PHOTO/TYPE']
        response.body = vcard.photo_binval
      else
        response['Status'] = 404
      end
    else
        response['Status'] = 404
    end

    throw :respond
  end
end

Ramaze::start(:port => HTTP_PORT)