package org.wikipedia.feed;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.feed.dataclient.FeedClient;
import org.wikipedia.feed.model.Card;
import org.wikipedia.feed.offline.OfflineCard;
import org.wikipedia.feed.progress.ProgressCard;
import org.wikipedia.settings.Prefs;
import org.wikipedia.util.ThrowableUtil;
import org.wikipedia.util.log.L;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public abstract class FeedCoordinatorBase {
private static final int MAX_HIDDEN_CARDS = 100;
public interface FeedUpdateListener {
void insert(Card card, int pos);
void swap(Card card, int pos);
void remove(Card card, int pos);
}
@NonNull private Context context;
@Nullable private WikiSite wiki;
@Nullable private FeedUpdateListener updateListener;
@NonNull private final List<Card> cards = new ArrayList<>();
private int currentAge;
private List<FeedClient> pendingClients = new ArrayList<>();
private FeedClient.Callback callback = new ClientRequestCallback();
private Card progressCard = new ProgressCard();
private Set<String> hiddenCards = Collections.newSetFromMap(new LinkedHashMap<String, Boolean>() {
@Override
public boolean removeEldestEntry(Map.Entry<String, Boolean> eldest) {
return size() > MAX_HIDDEN_CARDS;
}
});
public FeedCoordinatorBase(@NonNull Context context) {
this.context = context;
hiddenCards.addAll(Prefs.getHiddenCards());
}
@NonNull
public List<Card> getCards() {
return cards;
}
public void setFeedUpdateListener(@Nullable FeedUpdateListener listener) {
updateListener = listener;
}
public void reset() {
wiki = null;
currentAge = 0;
for (FeedClient client : pendingClients) {
client.cancel();
}
pendingClients.clear();
cards.clear();
}
public void more(@NonNull WikiSite wiki) {
this.wiki = wiki;
if (cards.size() == 0) {
insertCard(progressCard, 0);
}
if (cards.size() > 1) {
currentAge++;
}
buildScript(currentAge);
requestNextCard(wiki);
}
public boolean finished() {
return pendingClients.isEmpty();
}
public int getAge() {
return currentAge;
}
public int dismissCard(@NonNull Card card) {
int position = cards.indexOf(card);
addHiddenCard(card);
removeCard(card, position);
return position;
}
public void undoDismissCard(@NonNull Card card, int position) {
unHideCard(card);
insertCard(card, position);
}
void retryFromOffline(@NonNull WikiSite wiki) {
Card card = cards.get(lastIndex());
if (card instanceof OfflineCard) {
swapCard(progressCard, lastIndex());
}
currentAge--;
more(wiki);
}
protected abstract void buildScript(int age);
protected void addPendingClient(FeedClient client) {
pendingClients.add(client);
}
private int lastIndex() {
return cards.size() - 1;
}
private void requestNextCard(@NonNull WikiSite wiki) {
if (pendingClients.isEmpty()) {
removeProgressCard();
return;
}
pendingClients.remove(0).request(context, wiki, currentAge, callback);
}
private void removeProgressCard() {
int pos = cards.indexOf(progressCard);
if (pos < 0) {
return;
}
removeCard(progressCard, pos);
}
private class ClientRequestCallback implements FeedClient.Callback {
@Override public void success(@NonNull List<? extends Card> cardList) {
for (Card card : cardList) {
if (!isCardHidden(card)) {
appendCard(card);
}
}
//noinspection ConstantConditions
requestNextCard(wiki);
}
@Override public void error(@NonNull Throwable caught) {
if (ThrowableUtil.isOffline(caught)) {
appendCard(new OfflineCard());
removeProgressCard();
} else {
//noinspection ConstantConditions
requestNextCard(wiki);
L.w(caught);
}
}
}
private void appendCard(@NonNull Card card) {
int progressPos = cards.indexOf(progressCard);
insertCard(card, progressPos >= 0 ? progressPos : cards.size());
}
private void insertCard(@NonNull Card card, int position) {
cards.add(position, card);
if (updateListener != null) {
updateListener.insert(card, position);
}
}
private void swapCard(@NonNull Card card, int position) {
cards.remove(cards.get(position));
cards.add(position, card);
if (updateListener != null) {
updateListener.swap(card, position);
}
}
private void removeCard(@NonNull Card card, int position) {
cards.remove(card);
if (updateListener != null) {
updateListener.remove(card, position);
}
}
private void addHiddenCard(@NonNull Card card) {
hiddenCards.add(card.getHideKey());
Prefs.setHiddenCards(hiddenCards);
}
private boolean isCardHidden(@NonNull Card card) {
return hiddenCards.contains(card.getHideKey());
}
private void unHideCard(@NonNull Card card) {
hiddenCards.remove(card.getHideKey());
Prefs.setHiddenCards(hiddenCards);
}
}