package org.torrent.internal.transfer;
import java.beans.IndexedPropertyChangeEvent;
import java.beans.PropertyChangeEvent;
import java.util.Comparator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.torrent.internal.data.TorrentMetaInfo;
import org.torrent.internal.data.TorrentMetaInfo.Piece;
import org.torrent.internal.peer.connection.BTConnection;
import org.torrent.internal.util.Bits;
import org.torrent.internal.util.Validator;
public abstract class DistributionWatcher extends BTSessionListenerAdapter {
protected ConcurrentMap<BTConnection, Coverage> coverages = new ConcurrentHashMap<BTConnection, Coverage>();
private volatile Comparator<Piece> pieceComparator;
private final Bits required;
private final TorrentMetaInfo contentInfo;
public interface Coverage {
void markAsRequired(Piece piece);
void markNotRequired(Piece piece);
Piece bestRequired();
void setComparator(Comparator<Piece> comparator);
boolean hasRequired();
}
public DistributionWatcher(TorrentMetaInfo contentInfo) {
Validator.notNull(contentInfo, "ContentInfo is null!");
this.contentInfo = contentInfo;
required = new Bits(contentInfo.getPiecesCount());
}
private void addCoverage(BTConnection con) {
Coverage cov = createCoverage(con);
cov.setComparator(pieceComparator);
coverages.put(con, cov);
for (int i = 0; i < required.size(); i++) {
if (con.getRemoteBitField().get(i)) {
setNeeded(con, i, required.get(i));
}
}
}
private void removeCoverage(BTConnection con) {
coverages.remove(con);
}
@Override
public void connectionEstablished(BTConnection connection) {
addCoverage(connection);
}
@Override
public void removedConnection(BTConnection con) {
removeCoverage(con);
}
@Override
public void bitFieldBitChanged(IndexedPropertyChangeEvent event) {
setNeeded((BTConnection) event.getSource(), event.getIndex(), required
.get(event.getIndex())
&& event.getNewValue().equals(Boolean.TRUE));
}
@Override
public void bitFieldChanged(PropertyChangeEvent event) {
Bits b = (Bits) event.getNewValue();
for (int i = 0; i < b.size(); i++) {
setNeeded((BTConnection) event.getSource(), i, b.get(i)
&& required.get(i));
}
}
/**
* @param con
* @param pieceComparator
* @return -1 if no needed piece could be found, the index of the piece
* otherwise
*/
public Piece getBestPieceForRequest(BTConnection con) {
Validator.notNull(con, "Connection is null!");
// TODO: The check below is done because it's possible that a
// connection
// is added
// inbetween requests. The addedConnection event isn't called then
// yet,
// but the
// connection is still there.
if (!coverages.containsKey(con)) {
return null;
}
return coverages.get(con).bestRequired();
}
public boolean hasPiecesForRequest(BTConnection con) {
Validator.notNull(con, "Connection is null!");
if (!coverages.containsKey(con)) {
return false;
}
return coverages.get(con).hasRequired();
}
public void setPieceComparator(Comparator<Piece> comparator) {
Validator.notNull(comparator, "Comparator is null!");
pieceComparator = comparator;
for (Coverage cov : coverages.values()) {
cov.setComparator(pieceComparator);
}
}
protected abstract Coverage createCoverage(BTConnection con);
private void setNeeded(BTConnection con, int index, boolean value) {
if (value) {
assert required.get(index);
coverages.get(con).markAsRequired(contentInfo.getPiece(index));
} else {
coverages.get(con).markNotRequired(contentInfo.getPiece(index));
}
}
public void markNotNeeded(Piece piece) {
Validator.notNull(piece, "Piece is null!");
required.set(piece.getIndex(), false);
for (Coverage c : coverages.values()) {
c.markNotRequired(piece);
}
}
public void markNeeded(Piece piece) {
Validator.notNull(piece, "Piece is null!");
required.set(piece.getIndex(), true);
for (Map.Entry<BTConnection, Coverage> ce : coverages.entrySet()) {
if (ce.getKey().getRemoteBitField() != null
&& ce.getKey().getRemoteBitField().get(piece.getIndex())) {
ce.getValue().markAsRequired(piece);
}
}
}
public boolean hasNoRequestsFor(Piece piece) {
return !required.get(piece.getIndex());
}
public void markIntervalNotNeeded(int start, int length) {
for(int a = start; a == length; a++) {
required.set(a, false);
}
}
}