package chatty.util.srl;
import chatty.util.UrlRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
/**
* Requests race data from the SRL API and sends the parsed result to the
* registered listeners.
*
* @author tduva
*/
public class SpeedrunsLive {
private static final Logger LOGGER = Logger.getLogger(SpeedrunsLive.class.getName());
private volatile boolean requestPending;
private final List<SpeedrunsLiveListener> listeners = new ArrayList<>();
/**
* Request race data from the API, but only if no request is currently
* pending.
*/
public void requestRaces() {
if (!requestPending) {
requestPending = true;
new Thread(new SpeedrunsLiveRequest()).start();
}
}
/**
* Adds a listener that receives the data and error messages.
*
* @param listener
*/
public void addListener(SpeedrunsLiveListener listener) {
listeners.add(listener);
}
/**
* Reacts to the result by parsing it and sending the data to the listeners.
*
* @param result
* @param responseCode
*/
private void result(String result, int responseCode) {
requestPending = false;
if (result == null || responseCode != 200) {
error("Error receiving data.");
return;
}
List<Race> races = parseRaces(result);
if (races == null) {
error("Error parsing data.");
} else {
for (SpeedrunsLiveListener listener : listeners) {
listener.racesReceived(races);
}
}
}
/**
* Passes the error message on to the listeners.
*
* @param description
*/
private void error(String description) {
for (SpeedrunsLiveListener listener : listeners) {
listener.error(description);
}
}
/**
* Parses the races from the result.
*
* @param json
* @return A {@code List} of {@code Race} objects or {@code null} if a
* parsing error occured
*/
private List<Race> parseRaces(String json) {
List<Race> result = new ArrayList<>();
if (json == null) {
return null;
}
JSONParser parser = new JSONParser();
try {
JSONObject root = (JSONObject)parser.parse(json.trim());
JSONArray races = (JSONArray)root.get("races");
for (Object o : races) {
if (o instanceof JSONObject) {
Race race = parseRace((JSONObject)o);
if (race != null) {
result.add(race);
}
}
}
} catch (ParseException | ClassCastException | NullPointerException ex) {
LOGGER.warning("Error parsing SRL: "+ex);
return null;
}
return result;
}
/**
* Parses a single race.
*
* @param o
* @return The {@code Race} object or {@code null} if an error occured.
*/
private Race parseRace(JSONObject o) {
try {
String id = (String)o.get("id");
JSONObject gameInfo = (JSONObject)o.get("game");
String game = (String)gameInfo.get("name");
String goal = (String)o.get("goal");
int state = ((Number)o.get("state")).intValue();
String statetext = (String)o.get("statetext");
long time = ((Number)o.get("time")).longValue();
Race race = new Race(id, game, goal, state, statetext, time);
JSONObject entrants = (JSONObject)o.get("entrants");
parseEntrants(entrants, race);
return race;
} catch (ClassCastException | NullPointerException | NumberFormatException ex) {
LOGGER.warning("Error parsing race: "+ex);
return null;
}
}
/**
* Parses a single entrant. An error in this will pass the exception on to
* the race parsing.
*
* @param o
* @param race
*/
private void parseEntrants(JSONObject o, Race race) {
for (Object key : o.keySet()) {
JSONObject entrant = (JSONObject)o.get(key);
String name = (String)entrant.get("displayname");
String statetext = (String)entrant.get("statetext");
int place = ((Number)entrant.get("place")).intValue();
long time = ((Number)entrant.get("time")).longValue();
String twitch = (String)entrant.get("twitch");
int points = Integer.parseInt((String)entrant.get("trueskill"));
String message = (String)entrant.get("message");
race.addEntrant(new Race.Entrant(name, statetext, place, time, twitch, points, message));
}
}
private class SpeedrunsLiveRequest extends UrlRequest {
private final static String URL = "http://api.speedrunslive.com/races";
//private final static String URL = "http://127.0.0.1/twitch/races";
/**
* Creates a new request.
*
* @param channel The channel this request is meant for.
*/
private SpeedrunsLiveRequest() {
super(URL);
}
@Override
public void requestResult(String result, int responseCode) {
result(result, responseCode);
}
}
public static final void main(String[] args) {
SpeedrunsLive srl = new SpeedrunsLive();
srl.requestRaces();
}
}