package org.lysty.strategies;
import java.util.ArrayList;
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 AbstractVoteMatchStrategy implements PlaylistGenerator {
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),
alreadyIns);
}
if (nextSong != null) {
fillCandidates(candidates, votesMap.get(nextSong), distB
+ distF == 0 ? 1 : distB / (distB + distF),
alreadyIns);
}
totalVotes = 0;
it = candidates.entrySet().iterator();
while (it.hasNext()) {
entry = it.next();
totalVotes += entry.getValue();
}
rand = (int) (Math.random() * totalVotes);
selSong = getCandidateSong(candidates, rand);
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, int rand) {
int cnt = 0;
Iterator<Entry<Song, Integer>> it = candidates.entrySet().iterator();
Entry<Song, Integer> entry;
while (it.hasNext()) {
entry = it.next();
cnt += entry.getValue();
if (cnt >= rand)
return entry.getKey();
}
return null;
}
}