package org.limewire.core.impl.search.browse;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import org.limewire.core.api.library.FriendLibrary;
import org.limewire.core.api.library.LibraryState;
import org.limewire.core.api.library.RemoteLibraryManager;
import org.limewire.core.api.search.SearchListener;
import org.limewire.core.api.search.SearchResult;
import org.limewire.core.api.search.browse.BrowseStatus;
import org.limewire.core.api.search.browse.BrowseStatusListener;
import org.limewire.core.api.search.browse.BrowseStatus.BrowseState;
import org.limewire.friend.api.Friend;
import org.limewire.io.GUID;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
class FriendSingleBrowseSearch extends AbstractBrowseSearch {
private final Friend friend;
private final RemoteLibraryManager remoteLibraryManager;
private final ExecutorService executorService;
private final ListEventListener<FriendLibrary> friendLibraryListEventListener = new FriendLibraryListEventListener();
private final FriendLibraryModelListener friendLibraryModelListener = new FriendLibraryModelListener();
private final PropertyChangeListener libraryPropertyChangeListener = new LibraryPropertyChangeListener();
private final AtomicReference<FriendLibrary> currentLibrary = new AtomicReference<FriendLibrary>();
/**
* @param friend the person to be browsed - can not be anonymous or null
*/
public FriendSingleBrowseSearch(RemoteLibraryManager remoteLibraryManager, Friend friend, ExecutorService executorService) {
assert(friend != null && !friend.isAnonymous());
this.friend = friend;
this.remoteLibraryManager = remoteLibraryManager;
this.executorService = executorService;
}
@Override
public void start() {
executorService.execute(new Runnable() {
public void run() {
for (SearchListener listener : searchListeners) {
listener.searchStarted(FriendSingleBrowseSearch.this);
}
installListener();
startFriendBrowse();
}
});
}
@Override
public void stop() {
for (SearchListener listener : searchListeners) {
listener.searchStopped(FriendSingleBrowseSearch.this);
}
removeListener();
}
private void startFriendBrowse() {
FriendLibrary library = remoteLibraryManager.getFriendLibrary(friend);
if (library == null) {
// Failed!
fireBrowseStatusChanged(BrowseState.OFFLINE, friend);
for (SearchListener listener : searchListeners) {
listener.searchStopped(FriendSingleBrowseSearch.this);
}
} else {
setLibrary(library);
if(library.getState() == LibraryState.LOADING){
library.addPropertyChangeListener(libraryPropertyChangeListener);
} else {
loadLibrary();
}
}
}
/**Loads a snapshot of the available files, alerts BrowseStatusListeners that we have loaded,
* and SearchListeners that the search has stopped.*/
private void loadLibrary(){
List<SearchResult> remoteFileItems = new ArrayList<SearchResult>();
remoteLibraryManager.getFriendLibrary(friend).getModel().getReadWriteLock().readLock().lock();
try {
remoteFileItems.addAll(remoteLibraryManager.getFriendLibrary(friend).getModel());
} finally {
remoteLibraryManager.getFriendLibrary(friend).getModel().getReadWriteLock().readLock().unlock();
}
// add all files
for (SearchListener listener : searchListeners) {
listener.handleSearchResults(this, remoteFileItems);
}
fireBrowseStatusChanged(BrowseState.LOADED);
for (SearchListener listener : searchListeners) {
listener.searchStopped(FriendSingleBrowseSearch.this);
}
}
/**Adds friendLibraryListEventListener to the FriendLibraryList*/
private void installListener(){
remoteLibraryManager.getFriendLibraryList().addListEventListener(friendLibraryListEventListener);
}
/**Removes friendLibraryListEventListener from the FriendLibraryList.
* Removes libraryPropertyChangeLister from the friend library if necessary.*/
private void removeListener(){
remoteLibraryManager.getFriendLibraryList().removeListEventListener(friendLibraryListEventListener);
if (currentLibrary.get() != null) {
currentLibrary.get().removePropertyChangeListener(libraryPropertyChangeListener);
currentLibrary.get().getModel().removeListEventListener(friendLibraryModelListener);
currentLibrary.set(null);
}
}
private synchronized void setLibrary(FriendLibrary newLibrary){
FriendLibrary oldLibrary = currentLibrary.get();
if(newLibrary == oldLibrary){
return;
}
if(oldLibrary != null){
oldLibrary.getModel().removeListEventListener(friendLibraryModelListener);
}
//Add a property change listener to the new library and keep a reference to the library so we can remove the listener later.
newLibrary.getModel().addListEventListener(friendLibraryModelListener);
newLibrary.addPropertyChangeListener(libraryPropertyChangeListener);
currentLibrary.set(newLibrary);
if(currentLibrary.get().getState() == LibraryState.LOADED){
fireBrowseStatusChanged(BrowseState.UPDATED);
}
}
private void fireBrowseStatusChanged(BrowseState state, Friend... friends){
BrowseStatus status = new BrowseStatus(FriendSingleBrowseSearch.this, state, friends);
for (BrowseStatusListener listener : browseStatusListeners) {
listener.statusChanged(status);
}
}
private class FriendLibraryListEventListener implements ListEventListener<FriendLibrary> {
@Override
public void listChanged(ListEvent listChanges) {
while (listChanges.next()) {
if (listChanges.getType() == ListEvent.INSERT) {
FriendLibrary newLibrary = (FriendLibrary) listChanges.getSourceList().get(listChanges.getIndex());
if (newLibrary.getFriend().getId().equals(friend.getId())) {//There is a new library for our friend!
setLibrary(remoteLibraryManager.getFriendLibrary(friend));
}
} else if (listChanges.getType() == ListEvent.DELETE && remoteLibraryManager.getFriendLibrary(friend) == null){
//our friend has logged off
currentLibrary.set(null);
fireBrowseStatusChanged(BrowseState.OFFLINE, friend);
}
}
}
}
private class FriendLibraryModelListener implements ListEventListener<SearchResult>{
@Override
public void listChanged(ListEvent<SearchResult> listChanges) {
fireBrowseStatusChanged(BrowseState.UPDATED);
}
}
private class LibraryPropertyChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
LibraryState state = (LibraryState)evt.getNewValue();
if (state != LibraryState.LOADING) {
// The list has changed - tell the listeners
if (state == LibraryState.LOADED) {
fireBrowseStatusChanged(BrowseState.UPDATED);
} else {
fireBrowseStatusChanged(BrowseState.FAILED, friend);
}
}
}
}
@Override
public void repeat() {
stop();
start();
}
@Override
public CopyOnWriteArrayList<SearchListener> getListenerList() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getQuery() {
// TODO Auto-generated method stub
return null;
}
@Override
public GUID getQueryGuid() {
// TODO Auto-generated method stub
return null;
}
}