package org.lysty.strategies; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.lysty.core.PlaylistGenerator; import org.lysty.dao.Song; import org.lysty.dao.SongSelectionProfile; import org.lysty.db.DBHandler; public abstract class AbstractVoteMatchPriorityStrategy implements PlaylistGenerator { private static final int PREFERENCE_RATIO = 70; protected Map<String, String> attributes; @Override public List<Song> getPlaylist(SongSelectionProfile profile, StrategyConfiguration config, boolean isCircular, boolean mustIncludeSeeds, List<Song> blacklist) { attributes = config.getAttributes(); readAttributes(attributes); Map<Song, Map<Song, Integer>> votesMap = getVotesMap( profile.getRelPosMap(), blacklist); List<Song> playlist = profile.getPartialPlaylist(); List<Song> fPlaylist = new ArrayList<Song>(); Song prevSong = null; Song nextSong = null; Song firstSong = null; Song cSong; int distF = playlist.size(); int distB = 0; for (int i = 0; i < playlist.size(); i++) { cSong = playlist.get(i); if (cSong != null) { nextSong = cSong; firstSong = cSong; distF = i; break; } } int totalVotes; int rand; Song selSong; Map<Song, Integer> candidates = new HashMap<Song, Integer>(); Iterator<Entry<Song, Integer>> it; Entry<Song, Integer> entry; List<Song> alreadyIns = new ArrayList<Song>(); for (int i = 0; i < playlist.size(); i++) { distB++; if (distF > 0) distF--; cSong = playlist.get(i); if (cSong != null) { prevSong = playlist.get(i); distB = 0; distF = 0; nextSong = null; for (int j = i + 1; j < playlist.size(); j++) { if (playlist.get(j) != null) { nextSong = playlist.get(j); distF = j - i; break; } } if (nextSong == null && isCircular) { for (int j = 0; j <= i; j++) { if (firstSong.equals(playlist.get(j))) { nextSong = playlist.get(j); distF = j + playlist.size() - i; break; } } } if (mustIncludeSeeds) { fPlaylist.add(cSong); continue; } } if (cSong == null || !mustIncludeSeeds) { // has to fill with a song for this position candidates = new HashMap<Song, Integer>(); if (prevSong != null) { fillCandidates(candidates, votesMap.get(prevSong), distB + distF == 0 ? 1 : (distF / (distB + distF))+1, alreadyIns); } if (nextSong != null) { fillCandidates(candidates, votesMap.get(nextSong), distB + distF == 0 ? 1 : (distB / (distB + distF))+1, alreadyIns); } totalVotes = 0; it = candidates.entrySet().iterator(); while (it.hasNext()) { entry = it.next(); totalVotes += entry.getValue(); } selSong = getCandidateSong(candidates); alreadyIns.add(selSong); fPlaylist.add(selSong); } } return fPlaylist; } protected abstract void readAttributes(Map<String, String> attributes); private void fillCandidates(Map<Song, Integer> candidates, Map<Song, Integer> votedSongs, float weight, List<Song> alreadyIns) { if (votedSongs == null) { votedSongs = new HashMap<Song, Integer>(); } Iterator<Entry<Song, Integer>> it = votedSongs.entrySet().iterator(); Entry<Song, Integer> entry; Integer cVotes; while (it.hasNext()) { entry = it.next(); if (alreadyIns.contains(entry.getKey())) continue; cVotes = candidates.get(entry.getKey()); cVotes = cVotes == null ? 0 : cVotes; cVotes += (int) Math.ceil(entry.getValue() * weight); candidates.put(entry.getKey(), cVotes); } } private Map<Song, Map<Song, Integer>> getVotesMap( Map<Song, Integer> relPosMap, List<Song> blacklist) { Song song; List<Song> allSongs = DBHandler.getInstance().getSongs(null); Iterator<Song> it = relPosMap.keySet().iterator(); while (it.hasNext()) { song = it.next(); allSongs.remove(song); } if (blacklist != null) { for (Song s : blacklist) { allSongs.remove(s); } } Map<Song, Map<Song, Integer>> votesMap = new HashMap<Song, Map<Song, Integer>>(); Map<Song, Integer> listOfVotes; it = relPosMap.keySet().iterator(); int votes; while (it.hasNext()) { song = it.next(); listOfVotes = new HashMap<Song, Integer>(); for (Song c : allSongs) { votes = getVotes(song, c); if (votes > 0) { listOfVotes.put(c, votes); } } votesMap.put(song, listOfVotes); } return votesMap; } protected abstract int getVotes(Song song, Song candidate); private Song getCandidateSong(Map<Song, Integer> candidates) { List<VotedSong> sortedList = new ArrayList<VotedSong>(); Iterator<Entry<Song, Integer>> it = candidates.entrySet().iterator(); Entry<Song, Integer> entry; while (it.hasNext()) { entry = it.next(); sortedList.add(new VotedSong(entry.getKey(), entry.getValue())); } Collections.sort(sortedList); int rand = (int) Math.random() * 100; boolean preferTop = rand <= PREFERENCE_RATIO ? true : false; int endIndex = sortedList.size(); if (preferTop) { endIndex = endIndex * (100 - PREFERENCE_RATIO) / 100; } int cnt = 0; for (int i = 0; i < endIndex; i++) { cnt += sortedList.get(i).votes; } rand = (int) (Math.random() * cnt); int ttl = 0; for (int i = 0; i < endIndex; i++) { ttl += sortedList.get(i).votes; if (ttl >= rand) { return sortedList.get(i).song; } } if(sortedList.isEmpty()) return null; return sortedList.get(Math.max(0, endIndex - 1)).song; } class VotedSong implements Comparable { public Song song; public Integer votes; public VotedSong(Song song, Integer votes) { this.song = song; this.votes = votes; } @Override public int compareTo(Object other) { if (other instanceof VotedSong) { return this.votes.compareTo(((VotedSong) other).votes) * -1; } return 0; } } }