/**
   * Return sorted top albums based on the average of each album rating.
   *
   * @param bHideUnmounted if true, unmounted albums are not chosen
   * @param iNbBestofAlbums nb of items to return
   * @return top albums, can be less items than required according to nb of available albums
   */
  public List<Album> getBestOfAlbums(boolean bHideUnmounted, int iNbBestofAlbums) {
    // TODO: this code does not look at "bHideUnmounted" at all, so most of this
    // method and
    // the double copying is not necessary, or?

    // create a temporary table to remove unmounted albums
    // We consider an album as mounted if a least one track is mounted
    // This hashmap contains album-> album rates
    final Map<Album, Float> cacheRate =
        new HashMap<Album, Float>(AlbumManager.getInstance().getElementCount());
    ReadOnlyIterator<Album> it = AlbumManager.getInstance().getAlbumsIterator();
    while (it.hasNext()) {
      Album album = it.next();
      cacheRate.put(album, (float) album.getRate());
    }
    // Now sort albums by rating
    List<Album> sortedAlbums = new ArrayList<Album>(cacheRate.keySet());
    Collections.sort(
        sortedAlbums,
        new Comparator<Album>() {
          public int compare(Album o1, Album o2) {
            // lowest first
            return (int) (cacheRate.get(o1) - cacheRate.get(o2));
          }
        });
    return getTopAlbums(sortedAlbums, iNbBestofAlbums);
  }
 /**
  * Get sorted list of albums associated with this item.
  *
  * @param item DOCUMENT_ME
  * @return a list of item, void list if no result
  */
 public synchronized List<Album> getAssociatedAlbums(Item item) {
   List<Album> out;
   // [Perf] If item is a track, just return its album
   if (item instanceof Track) {
     out = new ArrayList<Album>(1);
     out.add(((Track) item).getAlbum());
   } else {
     ReadOnlyIterator<Album> albums = getAlbumsIterator();
     // Use a set to avoid dups
     Set<Album> albumSet = new HashSet<Album>();
     while (albums.hasNext()) {
       Album album = albums.next();
       for (Track track : album.getTracksCache()) {
         if (item instanceof Artist && track.getArtist().equals(item)) {
           albumSet.add(album);
         } else if (item instanceof Genre && track.getGenre().equals(item)) {
           albumSet.add(album);
         }
         if (item instanceof Year && track.getYear().equals(item)) {
           albumSet.add(album);
         }
       }
     }
     out = new ArrayList<Album>(albumSet);
     Collections.sort(out);
   }
   return out;
 }
 /**
  * Gets the album by name.
  *
  * @param name DOCUMENT_ME
  * @return associated album (case insensitive) or null if no match
  */
 public Album getAlbumByName(String name) {
   Album out = null;
   for (ReadOnlyIterator<Album> it = getAlbumsIterator(); it.hasNext(); ) {
     Album album = it.next();
     if (album.getName().equalsIgnoreCase(name)) {
       out = album;
       break;
     }
   }
   return out;
 }
 /**
  * Force to refresh the album max rating, it is not done soon as it is pretty CPU consumming and
  * we don't need a track by track rating precision.
  */
 public void refreshMaxRating() {
   // create a temporary table to remove unmounted albums
   // We consider an album as mounted if a least one track is mounted
   // This hashmap contains album-> album rates
   final Map<Album, Float> cacheRate =
       new HashMap<Album, Float>(AlbumManager.getInstance().getElementCount());
   ReadOnlyIterator<Album> it = AlbumManager.getInstance().getAlbumsIterator();
   while (it.hasNext()) {
     Album album = it.next();
     cacheRate.put(album, (float) album.getRate());
   }
   // OK, now keep only the highest score
   for (Map.Entry<Album, Float> album : cacheRate.entrySet()) {
     long value = Math.round(album.getValue());
     if (value > maxRate) {
       maxRate = value;
     }
   }
 }
 /**
  * Return ordered rarely listen albums list.
  *
  * @param bHideUnmounted if true, unmounted albums are not chosen
  * @param iNb nb of items to return
  * @return top albums, can be less items than required according to nb of available albums
  */
 public List<Album> getRarelyListenAlbums(boolean bHideUnmounted, int iNb) {
   // create a temporary table to remove unmounted albums
   // We consider an album as mounted if a least one track is mounted
   // This hashmap contains album-> album hits (each track hit average)
   final Map<Album, Float> cache =
       new HashMap<Album, Float>(AlbumManager.getInstance().getElementCount());
   // This hashmap contains album-> nb of tracks already taken into account
   // for average
   Map<Album, Integer> cacheNb =
       new HashMap<Album, Integer>(AlbumManager.getInstance().getElementCount());
   ReadOnlyIterator<Track> it = TrackManager.getInstance().getTracksIterator();
   while (it.hasNext()) {
     Track track = it.next();
     if (track.getPlayeableFile(bHideUnmounted) != null) {
       float newHits = 0f;
       Integer nb = cacheNb.get(track.getAlbum());
       if (nb == null) {
         nb = 0;
       }
       Float previousRate = cache.get(track.getAlbum());
       if (previousRate == null) {
         newHits = track.getHits();
       } else {
         newHits = ((previousRate * nb) + track.getHits()) / (nb + 1);
       }
       cacheNb.put(track.getAlbum(), nb + 1);
       cache.put(track.getAlbum(), newHits);
     }
   }
   // Now sort albums by rating
   List<Album> sortedAlbums = new ArrayList<Album>(cache.keySet());
   Collections.sort(
       sortedAlbums,
       new Comparator<Album>() {
         public int compare(Album o1, Album o2) {
           // We inverse comparison as we want lowest scores
           return (int) (cache.get(o2) - cache.get(o1));
         }
       });
   return getTopAlbums(sortedAlbums, iNb);
 }
 /*
  * (non-Javadoc)
  *
  * @see org.jajuk.base.Observer#update(org.jajuk.base.Event)
  */
 public void update(JajukEvent event) {
   JajukEvents subject = event.getSubject();
   if (JajukEvents.FILE_NAME_CHANGED.equals(subject)) {
     Properties properties = event.getDetails();
     File fNew = (File) properties.get(Const.DETAIL_NEW);
     File fileOld = (File) properties.get(Const.DETAIL_OLD);
     // search references in playlists
     ReadOnlyIterator<Playlist> it = getPlaylistsIterator();
     while (it.hasNext()) {
       Playlist plf = it.next();
       if (plf.isReady()) { // check only in mounted
         // playlists, note that we can't
         // change unmounted playlists
         try {
           if (plf.getFiles().contains(fileOld)) {
             plf.replaceFile(fileOld, fNew);
           }
         } catch (Exception e) {
           Log.error(17, e);
         }
       }
     }
   }
 }
 /**
  * Return ordered list of newest albums.
  *
  * @param bHideUnmounted if true, unmounted albums are not chosen
  * @param iNb nb of items to return
  * @return newest albums
  */
 public List<Album> getNewestAlbums(boolean bHideUnmounted, int iNb) {
   // create a temporary table to remove unmounted albums
   // We consider an album as mounted if a least one track is mounted
   // This hashmap contains album-> discovery date
   final Map<Album, Date> cache =
       new HashMap<Album, Date>(AlbumManager.getInstance().getElementCount());
   ReadOnlyIterator<Track> it = TrackManager.getInstance().getTracksIterator();
   while (it.hasNext()) {
     Track track = it.next();
     if (track.getPlayeableFile(bHideUnmounted) != null) {
       cache.put(track.getAlbum(), track.getDiscoveryDate());
     }
   }
   // Now sort albums by discovery date
   List<Album> sortedAlbums = new ArrayList<Album>(cache.keySet());
   Collections.sort(
       sortedAlbums,
       new Comparator<Album>() {
         public int compare(Album o1, Album o2) {
           return cache.get(o1).compareTo(cache.get(o2));
         }
       });
   return getTopAlbums(sortedAlbums, iNb);
 }
 /** Specialize switchToOrderState, here we sort the album cache in addition. */
 public synchronized void orderCache() {
   for (ReadOnlyIterator<Album> it = getAlbumsIterator(); it.hasNext(); ) {
     Album album = it.next();
     Collections.sort(album.getTracksCache(), new TrackComparator(TrackComparatorType.ALBUM));
   }
 }