package org.torrent.internal.transfer;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger;
import org.merapi.helper.handlers.BarUpdateRequestHandler;
import org.merapi.helper.messages.BarUpdateRespondMessage;
import org.torrent.internal.data.BTPart;
import org.torrent.internal.data.TorrentMetaInfo;
import org.torrent.internal.data.TorrentMetaInfo.Piece;
import org.torrent.internal.peer.connection.BTConnection;
import org.torrent.internal.service.BasicContentStateService;
import org.torrent.internal.service.BasicEventDispatcherService;
import org.torrent.internal.service.ContentState;
import org.torrent.internal.service.ContentStateListener;
import org.torrent.internal.service.ContentStateService;
import org.torrent.internal.service.EventDispatcherService;
import org.torrent.internal.service.event.ContentStateEvent;
import org.torrent.internal.util.Bits;
import org.torrent.internal.util.Partitions;
import org.torrent.internal.util.Range;
import org.torrent.internal.util.SparseArray;
import org.torrent.internal.util.Time;
import org.torrent.internal.util.Validator;
public class ContentWatcher extends BTSessionListenerAdapter {
private static final Logger LOG = Logger.getLogger(ContentWatcher.class
.getName());
private SparseArray<ContentState> content;
private Bits bitField;
private Collection<ContentStateListener> contentListeners = new CopyOnWriteArrayList<ContentStateListener>();
private final TorrentMetaInfo contentInfo;
private EventDispatcherService disp = new BasicEventDispatcherService();
public ContentWatcher(TorrentMetaInfo contentInfo) {
Validator.nonNull(contentInfo);
this.contentInfo = contentInfo;
content = new Partitions<ContentState>(Range.getRangeByLength(0,
contentInfo.getLength()), ContentState.UNKOWN);
bitField = new Bits(contentInfo.getPiecesCount());
}
private synchronized boolean hasRequired(Piece p) {
Validator.nonNull(p);
return content.findFirst(p.getRange(), ContentState.REQUIRED) != null;
}
private synchronized boolean isAvailable(int pieceIndex) {
Range range = contentInfo.getPiece(pieceIndex).getRange();
return range.equals(content.findFirst(range, ContentState.AVAILABLE));
}
private synchronized boolean isChecked(int pieceIndex) {
return content.getRange().equals(
content.findFirst(contentInfo.getPiece(pieceIndex).getRange(),
ContentState.VALIDATED));
}
public synchronized boolean isCompleted() {
return content.getRange().equals(
content.findFirst(ContentState.VALIDATED));
}
public synchronized boolean isRequired(BTPart pi) {
Time time = new Time();
try {
return content.findFirst(contentInfo.getAbsoluteRange(pi),
ContentState.REQUIRED) != null;
} finally {
if (time.delta() > 100) {
LOG.info("isRequired() time taken: " + time.delta());
}
}
}
/**
* Called to mark a part available. If all parts of a piece are available,
* an event will be dispatched.
*
* @param pi
*/
public synchronized void markAvailable(BTPart pi) {
LOG.finest("Marking available: " + pi.getIndex() + " "
+ pi.getRange().getStart() + " " + pi.getRange().getEnd());
content.put(contentInfo.getAbsoluteRange(pi), ContentState.AVAILABLE);
bitField.set(pi.getIndex(), false);
if (isAvailable(pi.getIndex())) {
fireAvailablePiece(contentInfo.getPiece(pi.getIndex()));
}
}
public synchronized void markChecked(Piece piece) {
LOG.finest("Marking checked: " + piece);
content.put(piece.getRange(), ContentState.VALIDATED);
bitField.set(piece.getIndex(), true);
fireCheckedPiece(piece);
}
public synchronized void markRequired(Piece piece) {
LOG.finest("Marking required: " + piece);
content.put(piece.getRange(), ContentState.REQUIRED);
// if (piece.getIndex() < 100) {
// return;
// }
// else {
bitField.set(piece.getIndex(), false);
fireRequiredRange(piece);
// }
}
@Override
public void removedConnection(BTConnection con) {
}
private void fireAvailablePiece(Piece piece) {
LOG.info("index available: " + piece.getIndex());
// fireEvent(new ContentStateEvent((ContentStateService) this, piece,
// ContentState.AVAILABLE));
// funzt
// fireEvent(new ContentStateEvent(new BasicContentStateService(this.disp,
// this.contentInfo.getPiecesCount()), piece,
// ContentState.AVAILABLE));
fireEvent(new ContentStateEvent(new BasicContentStateService(this.disp,
piece.getContentInfo().getPiecesCount()), piece,
ContentState.AVAILABLE));
//out to test message overflow
BarUpdateRequestHandler.sendUpdateBarData(
BarUpdateRespondMessage.UPDATE_BAR_DATA,
piece.getContentInfo().getInfoHash().asHexString(),
piece.getIndex(), piece.getRange().getLength(), 0);
// BarUpdateRequestHandler.getInstance().sendUpdateBarData(BarUpdateRespondMessage.UPDATE_BAR_DATA,
// "fireAvailablePiece, ContentWatcher", piece.getIndex(),
// piece.getRange().getLength());
}
private void fireCheckedPiece(Piece piece) {
LOG.info("index checked: " + piece.getIndex());
// fireEvent(new ContentStateEvent((ContentStateService) this, piece,
// ContentState.VALIDATED));
System.out.println(this.disp);
// funzt
// fireEvent(new ContentStateEvent(new BasicContentStateService(this.disp,
// this.contentInfo.getPiecesCount()), piece,
// ContentState.VALIDATED));
fireEvent(new ContentStateEvent(new BasicContentStateService(this.disp,
piece.getContentInfo().getPiecesCount()), piece,
ContentState.VALIDATED));
//out to test message overflow
// BarUpdateRequestHandler.sendUpdateBarData(
// BarUpdateRespondMessage.UPDATE_BAR_DATA,
// piece.getContentInfo().getInfoHash().asHexString(),
// piece.getIndex(), piece.getRange().getStart());
}
private void fireRequiredRange(Piece piece) {
LOG.info("index required: " + piece.getIndex());
// fireEvent(new ContentStateEvent((ContentStateService) this, piece,
// ContentState.REQUIRED));
// if (piece.getIndex() > 100) {
fireEvent(new ContentStateEvent(new BasicContentStateService(this.disp,
piece.getContentInfo().getPiecesCount()), piece,
ContentState.REQUIRED));
}
// else {
// fireEvent(new ContentStateEvent(new BasicContentStateService(this.disp,
// piece.g), piece,
// ContentState.UNKOWN));
//
// }
// return;
// BarUpdateRequestHandler.getInstance().sendUpdateBarData(BarUpdateRespondMessage.UPDATE_BAR_DATA,
// piece.getContentInfo().getInfoHash().asHexString(), piece.getIndex(),
// 1);
// }
private void fireEvent(final ContentStateEvent contentEvent) {
LOG.info("fireEvent: " + contentEvent.getState());
disp.invokeLater(new Runnable() {
@Override
public void run() {
LOG.info("ContentListeners: " + contentListeners.size());
for (ContentStateListener l : contentListeners) {
// l.stateChanged(contentEvent);
// l.requiresPiece(contentEvent);
// switch (contentEvent.getState()) {
// case REQUIRED: {
// l.requiresPiece(contentEvent);
// break;
// }
// case AVAILABLE: {
// // l.receivedPiece(contentEvent);
// l.stateChanged(contentEvent);
// break;
// }
// case UNKOWN: {
// break;
// }
// case VALIDATED: {
// // l.verifiedPiece(contentEvent);
// // l.stateChanged(contentEvent);
// break;
// }
//
// }
// working
// if (contentEvent.getState() == ContentState.REQUIRED) {
// l.requiresPiece(contentEvent);
// break;
// } else if (contentEvent.getState() == ContentState.AVAILABLE) {
// l.stateChanged(contentEvent);
//// l.verifiedPiece(contentEvent); //no up changed
// break;
// } else if (contentEvent.getState() == ContentState.UNKOWN) {
// break;
// } else if (contentEvent.getState() == ContentState.VALIDATED) {
//
// break;
// }
// l.stateChanged(contentEvent);
// if (contentEvent.getState() == ContentState.REQUIRED) {
// l.requiresPiece(contentEvent);
// break;
// } else if (contentEvent.getState() == ContentState.AVAILABLE) {
// l.receivedPiece(contentEvent);
// l.stateChanged(contentEvent);
// break;
// } else if (contentEvent.getState() == ContentState.UNKOWN) {
// break;
// } else if (contentEvent.getState() == ContentState.VALIDATED) {
// l.verifiedPiece(contentEvent);
// l.stateChanged(contentEvent);
// break;
// }
// l.requiresPiece(contentEvent);
// l.stateChanged(contentEvent);
if (contentEvent.getState() == ContentState.REQUIRED) {
l.requiresPiece(contentEvent);
l.stateChanged(contentEvent);
break;
} else if (contentEvent.getState() == ContentState.AVAILABLE) {
l.receivedPiece(contentEvent);
l.stateChanged(contentEvent);
break;
} else if (contentEvent.getState() == ContentState.UNKOWN) {
break;
} else if (contentEvent.getState() == ContentState.VALIDATED) {
l.verifiedPiece(contentEvent);
l.stateChanged(contentEvent);
break;
}
}
}
});
}
public Bits getBits() {
return bitField.unmodifableBits();
}
@Override
public String toString() {
return "ContentWatcher " + content;
}
@Override
public void addContentStateListener(ContentStateListener listener) {
Validator.nonNull(listener);
contentListeners.add(listener);
for (ContentStateListener list : contentListeners) {
LOG.info("ContentStateListener added");
LOG.info(list.toString());
}
}
@Override
public void removeContentStateListener(ContentStateListener listener) {
Validator.nonNull(listener);
contentListeners.remove(listener);
}
@Override
public void setAvailable(final BTPart part) {
if (disp.isEventDispatchThread()) {
markAvailable(part);
} else {
disp.invokeLater(new Runnable() {
@Override
public void run() {
markAvailable(part);
}
});
}
}
@Override
public void setRequired(final Piece piece) {
if (disp.isEventDispatchThread()) {
markRequired(piece);
} else {
disp.invokeLater(new Runnable() {
@Override
public void run() {
markRequired(piece);
}
});
}
}
@Override
public void setValidated(final Piece piece) {
if (disp.isEventDispatchThread()) {
markChecked(piece);
} else {
disp.invokeLater(new Runnable() {
@Override
public void run() {
markChecked(piece);
}
});
}
}
@Override
public boolean isAvailable(BTPart part) {
Range range = contentInfo.getAbsoluteRange(part);
return range.equals(content.findFirst(range, ContentState.AVAILABLE));
}
@Override
public boolean isRequired(Piece piece) {
return hasRequired(piece);
}
@Override
public boolean isValidated(Piece piece) {
return isChecked(piece.getIndex());
}
}