package org.royaldev.thehumanity.cards.cardcast; import com.google.common.base.Joiner; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.JSONArray; import org.json.JSONObject; import org.royaldev.thehumanity.cards.BaseCard; import org.royaldev.thehumanity.cards.packs.CAHCardPack; import org.royaldev.thehumanity.cards.packs.CardcastCardPack; import org.royaldev.thehumanity.cards.types.BlackCard; import org.royaldev.thehumanity.cards.types.WhiteCard; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import static khttp.KHttp.get; /** * Class the creates CardPacks based on Cardcast IDs. */ public class CardcastFetcher { private static final String INFO_URL = "https://api.cardcastgame.com/v1/decks/%s"; private static final String CARDS_URL = CardcastFetcher.INFO_URL + "/cards"; // Cache these, in case someone tries to download them shortly after private static final LoadingCache<String, CardcastCardPack> cache = CacheBuilder.newBuilder() .expireAfterWrite(12L, TimeUnit.HOURS) .build(new CacheLoader<String, CardcastCardPack>() { @Override public CardcastCardPack load(@NotNull final String key) throws Exception { return new CardcastFetcher(key).buildCardPack(); } }); private final String id; private String name; private String description; private String author; /** * Constructs a new fetcher for the given Cardcast ID. * * @param id ID of the Cardcast pack */ public CardcastFetcher(@NotNull final String id) { Preconditions.checkNotNull(id, "id was null"); this.id = id.toUpperCase(); // Standardize uppercase for cache purposes this.getInfo(); } public static void invalidateCacheFor(final String... cardcastIDs) { CardcastFetcher.cache.invalidateAll(Arrays.asList(cardcastIDs)); } /** * Builds a CardPack for storage in the cache. * * @return CardPack, never null * @throws Exception If an error contacting Cardcast occurs */ @NotNull private CardcastCardPack buildCardPack() throws Exception { final JSONObject root = get(String.format(CardcastFetcher.CARDS_URL, this.id)).getJsonObject(); final CardcastCardPack cp = new CardcastCardPack(this.name, this.id); cp.setDescription(this.description); cp.setAuthor(this.author); this.addCards(cp, this.getWhiteCards(cp, root.getJSONArray("responses"))); this.addCards(cp, this.getBlackCards(cp, root.getJSONArray("calls"))); return cp; } /** * Gets the text of a black card, given its parts. * * @param parts Parts of the black card * @return Complete text */ @NotNull private String getBlackCardText(@NotNull final JSONArray parts) { Preconditions.checkNotNull(parts, "parts was null"); final List<String> listParts = new ArrayList<>(); for (int i = 0; i < parts.length(); i++) { listParts.add(parts.getString(i)); } return Joiner.on('_').join(listParts); } /** * Adds the list of cards to the CardPack. * * @param cp CardPack to add to * @param cards Cards to add */ public void addCards(final CAHCardPack cp, final List<BaseCard> cards) { cards.forEach(cp::addCard); } /** * Gets all the black cards of the Cardcast pack. * * @param cp CardPack for black cards * @param calls Cardcast black cards * @return All converted black cards */ @NotNull public List<BaseCard> getBlackCards(@NotNull final CAHCardPack cp, @NotNull final JSONArray calls) { Preconditions.checkNotNull(cp, "cp was null"); Preconditions.checkNotNull(calls, "calls was null"); final List<BaseCard> blackCards = new ArrayList<>(); for (int i = 0; i < calls.length(); i++) { final JSONObject call = calls.getJSONObject(i); blackCards.add(new BlackCard(cp, this.getBlackCardText(call.getJSONArray("text")))); } return blackCards; } /** * Gets the converted CardPack. If there was an error contacting Cardcast, this returns null. * * @return CardPack or null */ @Nullable public CAHCardPack getCardPack() { try { return CardcastFetcher.cache.get(this.id); } catch (final ExecutionException ex) { return null; } } /** * Sets the information about this CardPack from Cardcast's API. */ public void getInfo() { final JSONObject info; try { info = get(String.format(CardcastFetcher.INFO_URL, this.id)).getJsonObject(); } catch (final Exception ex) { ex.printStackTrace(); return; } this.name = info.optString("name", this.id); this.description = info.optString("description"); final JSONObject authorInfo = info.optJSONObject("author"); if (authorInfo != null) { this.author = authorInfo.optString("username"); } } /** * Gets all the white cards of the Cardcast pack. * * @param cp CardPack for white cards * @param responses Cardcast white cards * @return All converted white cards */ @NotNull public List<BaseCard> getWhiteCards(@NotNull final CAHCardPack cp, @NotNull final JSONArray responses) { Preconditions.checkNotNull(cp, "cp was null"); Preconditions.checkNotNull(responses, "responses was null"); final List<BaseCard> whiteCards = new ArrayList<>(); for (int i = 0; i < responses.length(); i++) { final JSONObject response = responses.getJSONObject(i); whiteCards.add(new WhiteCard(cp, response.getJSONArray("text").getString(0).replaceAll("\\.$", ""))); } return whiteCards; } @Override public String toString() { return MoreObjects.toStringHelper(this) .omitNullValues() .add("id", this.id) .add("name", this.name) .add("description", this.description) .add("author", this.author) .toString(); } }