package de.westnordost.streetcomplete.data.download;
import android.content.SharedPreferences;
import android.graphics.Rect;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
import javax.inject.Provider;
import de.westnordost.streetcomplete.ApplicationConstants;
import de.westnordost.streetcomplete.Prefs;
import de.westnordost.streetcomplete.data.QuestType;
import de.westnordost.streetcomplete.data.QuestTypes;
import de.westnordost.streetcomplete.data.VisibleQuestListener;
import de.westnordost.streetcomplete.data.osm.OsmElementQuestType;
import de.westnordost.streetcomplete.data.osm.download.OsmQuestDownload;
import de.westnordost.streetcomplete.data.osmnotes.OsmNoteQuest;
import de.westnordost.streetcomplete.data.osmnotes.OsmNoteQuestDao;
import de.westnordost.streetcomplete.data.osmnotes.OsmNotesDownload;
import de.westnordost.streetcomplete.data.tiles.DownloadedTilesDao;
import de.westnordost.streetcomplete.util.SlippyMapMath;
import de.westnordost.osmapi.map.data.BoundingBox;
import de.westnordost.osmapi.map.data.LatLon;
public class QuestDownload
{
private static final String TAG = "QuestDownload";
private final Provider<OsmNotesDownload> notesDownloadProvider;
private final Provider<OsmQuestDownload> questDownloadProvider;
private final QuestTypes questTypeList;
private final SharedPreferences prefs;
private final DownloadedTilesDao downloadedTilesDao;
private final OsmNoteQuestDao osmNoteQuestDb;
private Rect tiles;
private Integer maxQuestTypes;
private AtomicBoolean cancelState;
private boolean isPriority;
// listeners
private VisibleQuestListener questListener;
private QuestDownloadProgressListener progressListener;
// state
private int downloadedQuestTypes = 0;
private int totalQuestTypes;
private int visibleQuests = 0;
private boolean finished = false;
@Inject public QuestDownload(Provider<OsmNotesDownload> notesDownloadProvider,
Provider<OsmQuestDownload> questDownloadProvider,
DownloadedTilesDao downloadedTilesDao,
OsmNoteQuestDao osmNoteQuestDb,
QuestTypes questTypeList, SharedPreferences prefs)
{
this.notesDownloadProvider = notesDownloadProvider;
this.questDownloadProvider = questDownloadProvider;
this.downloadedTilesDao = downloadedTilesDao;
this.osmNoteQuestDb = osmNoteQuestDb;
this.questTypeList = questTypeList;
this.prefs = prefs;
}
public void setQuestTypeListener(VisibleQuestListener questListener)
{
this.questListener = questListener;
}
public void setProgressListener(QuestDownloadProgressListener progressListener)
{
this.progressListener = progressListener;
}
public void init(Rect tiles, Integer maxQuestTypes, boolean isPriority,
AtomicBoolean cancel)
{
this.tiles = tiles;
this.maxQuestTypes = maxQuestTypes;
this.isPriority = isPriority;
this.cancelState = cancel;
}
public void download()
{
if(cancelState.get()) return;
List<QuestType> questTypes = getQuestTypesToDownload();
if(questTypes.isEmpty())
{
finished = true;
progressListener.onNotStarted();
return;
}
totalQuestTypes = questTypes.size();
BoundingBox bbox = SlippyMapMath.asBoundingBox(tiles, ApplicationConstants.QUEST_TILE_ZOOM);
try
{
Log.i(TAG, "(" + bbox.getAsLeftBottomRightTopString() + ") Starting");
progressListener.onStarted();
Set<LatLon> notesPositions;
if(questTypes.contains(OsmNoteQuest.type))
{
notesPositions = downloadNotes();
}
else
{
notesPositions = getNotePositionsFromDb();
}
downloadQuestTypes(questTypes, notesPositions);
progressListener.onSuccess();
}
finally
{
finished = true;
progressListener.onFinished();
Log.i(TAG, "(" + bbox.getAsLeftBottomRightTopString() + ") Finished");
}
}
private List<QuestType> getQuestTypesToDownload()
{
List<QuestType> result = new ArrayList<>(questTypeList.getQuestTypesSortedByImportance());
result.add(0, OsmNoteQuest.type);
long questExpirationTime = Integer.parseInt(prefs.getString(Prefs.QUESTS_EXPIRATION_TIME_IN_MIN, "0")) * 1000 * 60;
long ignoreOlderThan = Math.max(0,System.currentTimeMillis() - questExpirationTime);
List<String> alreadyDownloadedNames = downloadedTilesDao.getQuestTypeNames(tiles, ignoreOlderThan);
if(!alreadyDownloadedNames.isEmpty())
{
Set<QuestType> alreadyDownloaded = new HashSet<>(alreadyDownloadedNames.size());
for (String questTypeName : alreadyDownloadedNames)
{
if(questTypeName.equals(OsmNoteQuest.type.getClass().getSimpleName()))
{
alreadyDownloaded.add(OsmNoteQuest.type);
}
else
{
alreadyDownloaded.add(questTypeList.forName(questTypeName));
}
}
result.removeAll(alreadyDownloaded);
Log.i(TAG, "Not downloading quest types because they are in local storage already: " +
Arrays.toString(alreadyDownloadedNames.toArray()));
}
return result;
}
private Set<LatLon> getNotePositionsFromDb()
{
BoundingBox bbox = SlippyMapMath.asBoundingBox(tiles, ApplicationConstants.QUEST_TILE_ZOOM);
List<LatLon> positionList = osmNoteQuestDb.getAllPositions(bbox);
Set<LatLon> positions = new HashSet<>(positionList.size());
for (LatLon pos : positionList)
{
positions.add(pos);
}
return positions;
}
private Set<LatLon> downloadNotes()
{
OsmNotesDownload notesDownload = notesDownloadProvider.get();
notesDownload.setQuestListener(questListener);
Long userId = prefs.getLong(Prefs.OSM_USER_ID, -1);
if(userId == -1) userId = null;
int maxNotes = 10000;
Set<LatLon> result = notesDownload.download(tiles, userId, maxNotes);
downloadedQuestTypes++;
dispatchProgress();
return result;
}
private int downloadQuestTypes(List<QuestType> questTypes, Set<LatLon> notesPositions)
{
int visibleQuests = 0;
for (QuestType questType : questTypes)
{
if (cancelState.get()) break;
if (maxQuestTypes != null && downloadedQuestTypes >= maxQuestTypes) break;
if (questType instanceof OsmElementQuestType)
{
OsmQuestDownload questDownload = questDownloadProvider.get();
questDownload.setQuestListener(questListener);
visibleQuests += questDownload.download((OsmElementQuestType) questType, tiles, notesPositions);
downloadedQuestTypes++;
dispatchProgress();
}
}
return visibleQuests;
}
public float getProgress()
{
int max = totalQuestTypes;
if(maxQuestTypes != null) max = Math.min(maxQuestTypes, totalQuestTypes);
return Math.min(1f, (float) downloadedQuestTypes / max);
}
public boolean isPriority()
{
return isPriority;
}
public boolean isFinished()
{
return finished;
}
private void dispatchProgress()
{
progressListener.onProgress(getProgress());
}
}