package teamcomm.net.logging;
import common.Log;
import data.GameControlData;
import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Deque;
import java.util.LinkedList;
import javax.swing.event.EventListenerList;
import teamcomm.data.GameState;
import teamcomm.net.SPLStandardMessagePackage;
import teamcomm.net.SPLStandardMessageReceiver;
/**
* Task handling the replaying of a log file.
*
* @author Felix Thielke
*/
class LogReplayTask implements Runnable {
public static final int PLAYBACK_TASK_DELAY = 50; // ms
private static class LoggedObject {
public final long time;
public final Object object;
public final int typeid;
public LoggedObject(final long time, final Object object) {
this.time = time;
this.object = object;
this.typeid = -1;
}
public LoggedObject(final long time, final int typeid) {
this.time = time;
this.object = null;
this.typeid = typeid;
}
}
private final EventListenerList listeners;
private final Deque<LoggedObject> prevObjects = new LinkedList<>();
private final Deque<LoggedObject> nextObjects = new LinkedList<>();
private LoggedObject curObject;
private ObjectInputStream stream;
private long currentPosition = 0;
private float playbackFactor = 0;
public LogReplayTask(final File logfile, final EventListenerList listeners) throws IOException {
this.listeners = listeners;
stream = new ObjectInputStream(new BufferedInputStream(new FileInputStream(logfile)));
next();
}
public void close() {
if (stream != null) {
try {
stream.close();
} catch (IOException ex) {
}
stream = null;
}
}
public boolean isPaused() {
synchronized (this) {
return playbackFactor == 0;
}
}
public void setPlaybackSpeed(final float factor) {
synchronized (this) {
playbackFactor = factor;
}
}
@Override
public void run() {
if (curObject != null) {
if (!isPaused()) {
final boolean forward;
synchronized (this) {
forward = playbackFactor > 0;
currentPosition += (long) ((float) PLAYBACK_TASK_DELAY * playbackFactor);
}
if (forward) {
while (currentPosition >= curObject.time) {
handleObject(curObject);
if (!next()) {
synchronized (this) {
playbackFactor = 0;
}
currentPosition = curObject.time;
break;
}
}
} else {
while (currentPosition <= curObject.time) {
handleObject(curObject);
if (!prev()) {
synchronized (this) {
playbackFactor = 0;
}
currentPosition = curObject.time;
break;
}
}
}
}
final LogReplayEvent e;
synchronized (this) {
e = new LogReplayEvent(this, currentPosition, prevObjects.isEmpty(), stream == null && nextObjects.isEmpty(), playbackFactor);
}
for (final LogReplayEventListener listener : listeners.getListeners(LogReplayEventListener.class)) {
listener.logReplayStatus(e);
}
}
}
private boolean prev() {
if (!prevObjects.isEmpty()) {
if (curObject != null) {
nextObjects.push(curObject);
}
curObject = prevObjects.pollFirst();
return true;
}
return false;
}
private boolean next() {
LoggedObject obj = nextObjects.pollFirst();
if (obj == null && stream != null) {
try {
final long time = stream.readLong();
if (stream.readBoolean()) {
obj = new LoggedObject(time, stream.readObject());
} else {
obj = new LoggedObject(time, stream.readInt());
}
} catch (EOFException e) {
try {
stream.close();
} catch (IOException ex) {
}
stream = null;
} catch (IOException | ClassNotFoundException e) {
Log.error("error while reading log file: " + e.getClass().getSimpleName() + ": " + e.getMessage());
try {
stream.close();
} catch (IOException ex) {
}
stream = null;
}
}
if (obj != null) {
if (curObject != null) {
prevObjects.push(curObject);
}
curObject = obj;
return true;
}
return false;
}
private void handleObject(final LoggedObject obj) {
if (obj.object != null) {
if (obj.object instanceof SPLStandardMessagePackage) {
SPLStandardMessageReceiver.getInstance().addToPackageQueue((SPLStandardMessagePackage) obj.object);
} else if (obj.object instanceof GameControlData) {
GameState.getInstance().updateGameData((GameControlData) obj.object);
}
} else {
switch (obj.typeid) {
case 1:
GameState.getInstance().updateGameData(null);
break;
}
}
}
}