#import "JubAvatarManager.h"
#import "JubChatClient.h"

#define JUB_NS_PUBSUB @"http://jabber.org/protocol/pubsub"
#define JUB_NS_PUBSUB_EVENT @"http://jabber.org/protocol/pubsub#event"
#define JUB_NS_AVATAR_DATA @"urn:xmpp:avatar:data"
#define JUB_NS_AVATAR_METADATA @"urn:xmpp:avatar:metadata"

@implementation JubAvatarManager
@synthesize delegate = _delegate;

- initWithClient: (JubChatClient*)client
{
	self = [super init];

	@try {
		_client = client;
		[_client.discoEntity
		    addFeature: JUB_NS_AVATAR_METADATA @"+notify"];
		[_client.connection addDelegate: self];

		// Determine cache path
		OFDictionary *env = [OFApplication environment];
		OFString * xdgCache = env[@"XDG_CACHE_HOME"];

		cachePath = [OFMutableString new];
		if (xdgCache == nil) {
			OFString *home = env[@"HOME"];
			if (home == nil)
				[cachePath appendString: @"/tmp"];
			else
				[cachePath appendString: home];
			[cachePath appendString: @"/.cache"];
		} else
			[cachePath appendString: xdgCache];

		[cachePath appendString: @"/jubjub"];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	// TODO: Remove feature
	[cachePath release];
	[_client.connection removeDelegate: self];
	[super dealloc];
}

- (void)connection: (XMPPConnection*)connection
      didReceiveMessage: (XMPPMessage*)message
{
	OFXMLElement *event = [message elementForName: @"event"
					    namespace: JUB_NS_PUBSUB_EVENT];
	if (event == nil)
		return;

	OFXMLElement *items = [event elementForName: @"items"
					  namespace: JUB_NS_PUBSUB_EVENT];
	if (items == nil ||
	    ![[[items attributeForName: @"node"] stringValue]
	      isEqual: JUB_NS_AVATAR_METADATA])
		return;

	OFXMLElement *item = [items elementForName: @"item"
					 namespace: JUB_NS_PUBSUB_EVENT];
	if (item == nil)
		return;

	OFString *ID = [[item attributeForName: @"id"] stringValue];
	if (ID == nil)
		return;

	OFXMLElement *metadata = [item elementForName: @"metadata"
					    namespace: JUB_NS_AVATAR_METADATA];
	if (metadata == nil)
		return;

	OFXMLElement *info = [metadata elementForName: @"info"
					    namespace: JUB_NS_AVATAR_METADATA];
	if (info == nil)
		return;

	OFString *avatarPath =
	    [cachePath stringByAppendingFormat: @"/%@.png", ID];
	if ([OFFile fileExistsAtPath: avatarPath]) {
		OFString *contactJID = [message.from bareJID];
		XMPPContact *contact =
		    [_client.contactManager.contacts objectForKey: contactJID];
		if (contact == nil) // Avatar for unknown contact
			return;
		[_delegate contact: contact
		      didSetAvatar: avatarPath];
		return;
	}

	XMPPIQ *queryIQ =
	    [XMPPIQ IQWithType: @"get"
			    ID: [_client.connection generateStanzaID]];
	queryIQ.to = message.from;

	OFXMLElement *pubsub = [OFXMLElement elementWithName: @"pubsub"
						   namespace: JUB_NS_PUBSUB];
	[queryIQ addChild: pubsub];

	OFXMLElement *queryItems =
	    [OFXMLElement elementWithName: @"items"
				namespace: JUB_NS_PUBSUB];
	[queryItems addAttributeWithName: @"node"
			     stringValue: JUB_NS_AVATAR_DATA];
	[pubsub addChild: queryItems];

	OFXMLElement *queryItem =
	    [OFXMLElement elementWithName: @"item"
				namespace: JUB_NS_PUBSUB];
	[queryItem addAttributeWithName: @"id"
			    stringValue: ID];
	[queryItems addChild: queryItem];

	[_client.connection sendIQ: queryIQ
		    callbackTarget: self
			  selector: @selector(Jub_connection:
					    receivedAvatarIQ:)];
}

- (void)Jub_connection: (XMPPConnection*)connection
      receivedAvatarIQ: (XMPPIQ*)IQ
{
	OFXMLElement *pubsub = [IQ elementForName: @"pubsub"
					namespace: JUB_NS_PUBSUB];
	if (pubsub == nil)
		return;

	OFXMLElement *items = [pubsub elementForName: @"items"
					   namespace: JUB_NS_PUBSUB];
	if (items == nil ||
	    ![[[items attributeForName: @"node"] stringValue]
	      isEqual: JUB_NS_AVATAR_DATA])
		return;

	OFXMLElement *item = [items elementForName: @"item"
					 namespace: JUB_NS_PUBSUB];
	if (item == nil)
		return;

	OFString *ID = [[item attributeForName: @"id"] stringValue];
	if (ID == nil)
		return;

	OFXMLElement *data = [item elementForName: @"data"
					namespace: JUB_NS_AVATAR_DATA];
	if (data == nil)
		return;

	OFDataArray *avatar =
	    [OFDataArray dataArrayWithBase64EncodedString: [data stringValue]];

	if (![OFFile directoryExistsAtPath: cachePath])
		[OFFile createDirectoryAtPath: cachePath
				createParents: true];

	OFString *filename =
	    [cachePath stringByAppendingFormat: @"/%@.png", ID];
	[avatar writeToFile: filename];

	OFString *contactJID = [IQ.from bareJID];

	XMPPContact *contact =
	    [_client.contactManager.contacts objectForKey: contactJID];
	if (contact == nil) // Avatar for unknown contact
		return;

	[_delegate contact: contact
	      didSetAvatar: filename];
}
@end
