package org.limewire.ui.swing.friends.chat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.limewire.friend.api.Friend;
import org.limewire.friend.api.FriendPresence;
import org.limewire.friend.api.FriendPresenceEvent;
import org.limewire.friend.api.IncomingChatListener;
import org.limewire.friend.api.MessageReader;
import org.limewire.friend.api.MessageWriter;
import org.limewire.inject.LazySingleton;
import org.limewire.listener.EventListener;
import org.limewire.listener.ListenerSupport;
import org.limewire.listener.SwingEDTEvent;
import org.limewire.ui.swing.util.SwingUtils;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import com.google.inject.Inject;
/**
* Keeps a list of friends and their presences. As friends sign on and off,
* this list is automatically updated. This also listens for incoming chat
* messages from a given presence.
*/
@LazySingleton
class ChatModel {
/** List of friends to chat with */
private final EventList<ChatFriend> chatFriends;
/** Mapping of friendId to ChatFriend */
private final Map<String, ChatFriend> idToFriendMap;
private final ListenerSupport<FriendPresenceEvent> presenceSupport;
private PresenceListener presenceEvent;
/**
* Listener for incoming chat events.
*/
private final List<IncomingListener> incomingListeners = new CopyOnWriteArrayList<IncomingListener>();
@Inject
public ChatModel(ListenerSupport<FriendPresenceEvent> presenceSupport) {
this.presenceSupport = presenceSupport;
this.chatFriends = new BasicEventList<ChatFriend>();
this.idToFriendMap = new HashMap<String, ChatFriend>();
}
/**
* Returns an EventList of chatFriends.
*/
public EventList<ChatFriend> getChatFriendList() {
return chatFriends;
}
/**
* Returns the ChatFriend associated with this friendId.
* Returns null if no ChatFriend exists for this friendId.
*/
public ChatFriend getChatFriend(String friendId) {
return idToFriendMap.get(friendId);
}
public void addIncomingListener(IncomingListener listener) {
incomingListeners.add(listener);
}
public void removeIncomingListener(IncomingListener listener) {
incomingListeners.remove(listener);
}
/**
* Registers presence listeners.
*/
public void registerListeners() {
// listen for presence sign on/off changes
if(presenceEvent == null)
presenceEvent = new PresenceListener();
presenceSupport.addListener(presenceEvent);
}
/**
* Removes presence listeners and clears the list of friends.
*/
public void unregisterListeners() {
if(presenceEvent != null)
presenceSupport.removeListener(presenceEvent);
idToFriendMap.clear();
chatFriends.clear();
}
/**
* Updates the list of ChatFriends as presences sign on and off.
*/
void handlePresenceEvent(FriendPresenceEvent event) {
final FriendPresence presence = event.getData();
final Friend friend = presence.getFriend();
ChatFriend chatFriend = idToFriendMap.get(friend.getId());
switch(event.getType()) {
case ADDED:
addFriend(chatFriend, presence);
break;
case UPDATE:
if (chatFriend != null) {
chatFriend.update();
}
break;
case REMOVED:
if (chatFriend != null) {
if (shouldRemoveFromFriendsList(chatFriend)) {
chatFriends.remove(idToFriendMap.remove(friend.getId()));
idToFriendMap.remove(chatFriend);
friend.removeChatListener();
}
chatFriend.update();
}
break;
}
}
/**
* Remove from the friends list only when:
* <pre>
* 1. The user (buddy) associated with the chatfriend is no longer signed in, AND
* 2. The chat has been closed (by clicking on the "x" on the friend in the friend's list)
* </pre>
* @param chatFriend the ChatFriend to decide whether to remove (no null check)
* @return true if chatFriend should be removed.
*/
private boolean shouldRemoveFromFriendsList(ChatFriend chatFriend) {
return (!chatFriend.isChatting()) && (!chatFriend.isSignedIn());
}
/**
* Adds a friend to the list of friends that can be chatted with. Also
* adds a listener to this friend presence that listens for incoming messages
* from this presence.
* <p>
* This listener ensures that the ChatPanel has been created prior to
* firing a ConversationEvent.
*/
private void addFriend(ChatFriend chatFriend, final FriendPresence presence) {
if(chatFriend == null) {
chatFriend = new ChatFriendImpl(presence);
chatFriends.add(chatFriend);
idToFriendMap.put(presence.getFriend().getId(), chatFriend);
}
final ChatFriend friend = chatFriend;
IncomingChatListener incomingChatListener = new IncomingChatListener() {
public MessageReader incomingChat(final MessageWriter writer) {
SwingUtils.invokeAndWait(new Runnable() {
@Override
public void run() {
MessageWriter writerWrapper = new MessageWriterImpl(friend, writer);
fireIncomingEvent(friend, writerWrapper);
}
});
return new MessageReaderImpl(friend);
}
};
presence.getFriend().setChatListenerIfNecessary(incomingChatListener);
chatFriend.update();
}
private void fireIncomingEvent(ChatFriend chatFriend, MessageWriter messageWriter) {
for(IncomingListener listener : incomingListeners) {
listener.incomingChat(chatFriend, messageWriter);
}
}
/**
* Listens for changes in friend presences.
*/
private class PresenceListener implements EventListener<FriendPresenceEvent> {
@Override
@SwingEDTEvent
public void handleEvent(FriendPresenceEvent event) {
handlePresenceEvent(event);
}
}
}