package pl.edu.agh.service;
import java.util.List;
import java.util.Random;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import pl.edu.agh.assembler.SimpleLocationInfoJSONAssembler;
import pl.edu.agh.assembler.TrafficDataJSONAssembler;
import pl.edu.agh.assembler.TrafficInfoJSONAssembler;
import pl.edu.agh.jsonrpc.JSONRPCException;
import pl.edu.agh.jsonrpc.JSONRPCSkeleton;
import pl.edu.agh.jsonrpc.JSONRPCSocketClient;
import pl.edu.agh.jsonrpc.RawDataSocket;
import pl.edu.agh.logic.TrafficDataBuffer;
import pl.edu.agh.logic.TrafficDataProvider;
import pl.edu.agh.model.SimpleLocationInfo;
import pl.edu.agh.model.TrafficData;
import pl.edu.agh.model.TrafficInfo;
import android.util.Log;
public class TrafficAdHocDispatchService extends JSONRPCSkeleton implements AsyncDataListener {
public static final int MAXIMUM_MESSAGE_SIZE = 1400;
private AsyncDataReceiver asyncReceiver = null;
private final TrafficDataProvider trafficDataProvider;
private final SimpleLocationInfoJSONAssembler locationInfoAssembler = new SimpleLocationInfoJSONAssembler();
private final TrafficInfoJSONAssembler trafficInfoAssembler;
private final TrafficDataJSONAssembler trafficDataAssembler;
private final JSONRPCSocketClient rpcAdHocClient;
private final TrafficDataBuffer trafficDataBuffer = new TrafficDataBuffer();
private int allowedMessageSize = MAXIMUM_MESSAGE_SIZE;
public TrafficAdHocDispatchService(TrafficDataProvider provider, RawDataSocket socket) {
this.trafficDataProvider = provider;
this.rpcAdHocClient = new JSONRPCSocketClient(socket);
this.trafficInfoAssembler = new TrafficInfoJSONAssembler(locationInfoAssembler);
this.trafficDataAssembler = new TrafficDataJSONAssembler(trafficInfoAssembler);
}
public void setMessageSize(int size) {
this.allowedMessageSize = size;
}
public synchronized void registerReceiver(AsyncDataReceiver receiver) {
this.asyncReceiver = receiver;
}
@Override
protected JSONObject invoke(String methodName, JSONArray params)
throws Exception {
if(methodName.equals(TrafficService.ASYNC_TRAFFIC_DATA_EVENT)) {
this.responseReceived(params);
} else if(methodName.equals(TrafficService.GET_TRAFFIC_DATA_METHOD)) {
this.requestReceived(params);
}
return null;
}
private synchronized void responseReceived(JSONArray params) throws JSONException {
if(asyncReceiver != null) {
if(!asyncReceiver.isInterested()) {
trafficDataBuffer.discard();
asyncReceiver = null;
}
JSONObject object = params.getJSONObject(0);
Long requestId = params.getLong(1);
Long sender = params.getLong(2);
Integer seq = params.getInt(3);
Boolean lastChunk = params.getBoolean(4);
if(asyncReceiver.getRequestCode() == requestId.longValue()) {
TrafficData dataChunk = trafficDataAssembler.deserialize(object);
boolean completed = trafficDataBuffer.appendChunk(dataChunk, sender, seq, lastChunk);
if(completed) {
TrafficData data = trafficDataBuffer.getCollectedData().get(0);
asyncReceiver.dataReceived(data);
trafficDataBuffer.discard();
asyncReceiver = null;
}
}
}
}
private void requestReceived(JSONArray params) throws JSONException {
JSONObject serializedLocation = params.getJSONObject(0);
SimpleLocationInfo location = locationInfoAssembler.deserialize(serializedLocation);
Double radius = params.getDouble(1);
Long requestId = params.getLong(2);
TrafficData data = trafficDataProvider.getCachedTrafficDataIfAvailable(location, radius);
if(data != null) {
JSONArray infosArray = new JSONArray();
long senderId = new Random(System.currentTimeMillis()).nextLong();
boolean lastChunk = false;
int seq = 0;
List<TrafficInfo> trafficInfos = data.getTrafficInfos();
for(int i=0; i < trafficInfos.size(); i++) {
TrafficInfo info = trafficInfos.get(i);
if(i == trafficInfos.size()-1) {
lastChunk = true;
}
JSONObject infoObject = trafficInfoAssembler.serialize(info);
int objectSize = infoObject.toString().length();
int totalSize = infosArray.toString().length();
if(totalSize + objectSize > allowedMessageSize) {
JSONObject locationData = trafficDataAssembler.serialize(infosArray);
sendData(locationData, requestId, senderId, seq, lastChunk);
infosArray = new JSONArray();
seq++;
}
infosArray.put(infoObject);
if(lastChunk) {
JSONObject locationData = trafficDataAssembler.serialize(infosArray);
sendData(locationData, requestId, senderId, seq, lastChunk);
}
}
}
}
private void sendData(JSONObject locationData, Long requestId, Long senderId,
Integer seq, Boolean lastChunk) {
try {
rpcAdHocClient.call(TrafficService.ASYNC_TRAFFIC_DATA_EVENT,
locationData, requestId, senderId, seq, lastChunk);
Thread.sleep(10);
} catch (JSONRPCException e) {
Log.e("Ad Hoc Dispatch", e.getMessage(), e);
} catch (InterruptedException e) {
Log.w("Ad Hoc Dispatch", e.getMessage(), e);
}
}
}