package org.lodder.subtools.multisubdownloader.workers;
import java.util.*;
import java.util.Map.Entry;
import org.lodder.subtools.multisubdownloader.exceptions.SearchSetupException;
import org.lodder.subtools.multisubdownloader.gui.dialog.Cancelable;
import org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting.ScoreCalculator;
import org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting.SortWeight;
import org.lodder.subtools.multisubdownloader.listeners.SearchProgressListener;
import org.lodder.subtools.multisubdownloader.settings.model.Settings;
import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider;
import org.lodder.subtools.sublibrary.model.Release;
import org.lodder.subtools.sublibrary.model.Subtitle;
public class SearchManager implements Cancelable {
protected Map<SubtitleProvider, Queue<Release>> queue = new HashMap<>();
protected Map<SubtitleProvider, SearchWorker> workers = new HashMap<>();
protected Map<Release, ScoreCalculator> scoreCalculators = new HashMap<>();
protected Settings settings;
protected int progress = 0;
protected int totalJobs;
protected SearchHandler onFound;
protected String language;
private SearchProgressListener progressListener;
public SearchManager(Settings settings) {
this.settings = settings;
}
public void onFound(SearchHandler onFound) {
this.onFound = onFound;
}
public void setLanguage(String language) {
this.language = language;
}
public void addProvider(SubtitleProvider provider) {
if (this.workers.containsKey(provider)) return;
this.workers.put(provider, new SearchWorker(provider, this));
this.queue.put(provider, new LinkedList<Release>());
}
public void addRelease(Release release) {
for (Entry<SubtitleProvider, Queue<Release>> provider : this.queue.entrySet()) {
queue.get(provider.getKey()).add(release);
}
/* Create a scoreCalculator so we can score subtitles for this release */
// TODO: extract to factory
SortWeight weights = new SortWeight(release, this.settings.getSortWeights());
this.scoreCalculators.put(release, new ScoreCalculator(weights));
}
public void setProgressListener(SearchProgressListener listener) {
synchronized (this) {
this.progressListener = listener;
}
}
public void start() throws SearchSetupException {
synchronized (this) {
if (this.progressListener == null)
throw new SearchSetupException("ProgressListener cannot be null");
}
if (this.onFound == null) throw new SearchSetupException("SearchHandler cannot be null");
if (this.language == null) throw new SearchSetupException("Language cannot be null");
totalJobs = this.jobsLeft();
if (totalJobs <= 0) {
return;
}
for (Entry<SubtitleProvider, SearchWorker> worker : workers.entrySet())
worker.getValue().start();
}
public synchronized void onCompleted(SearchWorker worker) {
Release release = worker.getRelease();
List<Subtitle> subtitles = new ArrayList<>(worker.getSubtitles());
calculateProgress();
/* set the score of the found subtitles */
ScoreCalculator calculator = this.scoreCalculators.get(release);
for (Subtitle subtitle : subtitles) {
subtitle.setScore(calculator.calculate(subtitle));
}
/* Tell the progresslistener our total progress */
this.progressListener.progress(this.getProgress());
onFound.onFound(release, subtitles);
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
for (Entry<SubtitleProvider, SearchWorker> worker : workers.entrySet())
worker.getValue().interrupt();
return true;
}
public synchronized Release getNextRelease(SubtitleProvider provider) {
if (!this.hasNextRelease(provider)) {
/* Tell the progressListener this provider is finished */
this.progressListener.progress(provider, queue.get(provider).size(), null);
return null;
}
Release release = queue.get(provider).poll();
/* Tell the progressListener we are starting on a new Release */
this.progressListener.progress(provider, queue.get(provider).size(), release);
return release;
}
public boolean hasNextRelease(SubtitleProvider provider) {
return !queue.get(provider).isEmpty();
}
public String getLanguage() {
return this.language;
}
public int getProgress() {
return this.progress;
}
private int jobsLeft() {
int jobsLeft = 0;
for (Entry<SubtitleProvider, Queue<Release>> provider : this.queue.entrySet()) {
jobsLeft += provider.getValue().size();
SearchWorker worker = this.workers.get(provider.getKey());
if (worker.isAlive() && worker.isBusy()) {
jobsLeft++;
}
}
return jobsLeft;
}
private void calculateProgress() {
if (totalJobs <= 0) {
// No job, means we are completed
progress = 100;
} else {
int jobsDone = this.totalJobs - this.jobsLeft();
progress = (int) Math.floor((float) jobsDone / this.totalJobs * 100);
}
}
}