package tc.oc.commons.bukkit.util;
import java.util.Collection;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
import gnu.trove.TLongCollection;
import gnu.trove.impl.Constants;
import gnu.trove.iterator.TLongIterator;
import gnu.trove.set.TLongSet;
import gnu.trove.set.hash.TLongHashSet;
import org.bukkit.util.BlockVector;
import static tc.oc.commons.bukkit.util.BlockUtils.decodePos;
import static tc.oc.commons.bukkit.util.BlockUtils.encodePos;
/**
* Optimized implementation of a set of block locations. Coordinates are
* encoded into a single long integer with bit masking, and stored in
* a Trove primitive collection.
*/
public class BlockVectorSet implements Set<BlockVector> {
private final TLongSet set;
BlockVectorSet(TLongSet set) {
this.set = set;
}
public BlockVectorSet(int capacity) {
this(new TLongHashSet(capacity, Constants.DEFAULT_LOAD_FACTOR, BlockUtils.ENCODED_NULL_POS));
}
public BlockVectorSet(Collection<? extends BlockVector> that) {
this(that.size());
addAll(that);
}
public BlockVectorSet() {
this(Constants.DEFAULT_CAPACITY);
}
public static BlockVectorSet of(Collection<? extends BlockVector> that) {
return that instanceof BlockVectorSet ? (BlockVectorSet) that
: new BlockVectorSet(that);
}
@Override
public int size() {
return this.set.size();
}
@Override
public boolean isEmpty() {
return this.set.isEmpty();
}
@Override
public Iterator<BlockVector> iterator() {
final TLongIterator iter = this.set.iterator();
return new Iterator<BlockVector>() {
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public BlockVector next() {
return decodePos(iter.next());
}
@Override
public void remove() {
iter.remove();
}
};
}
/**
* Return an iterator that reuses a single BlockVector instance,
* mutating it for each iteration.
*/
public Iterator<BlockVector> mutableIterator() {
final TLongIterator iter = set.iterator();
return new Iterator<BlockVector>() {
final BlockVector value = new BlockVector();
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public BlockVector next() {
return decodePos(iter.next(), value);
}
};
}
public boolean contains(long encoded) {
return this.set.contains(encoded);
}
public boolean contains(int x, int y, int z) {
return this.contains(encodePos(x, y, z));
}
@Override
public boolean contains(Object o) {
return o instanceof BlockVector && this.set.contains(encodePos((BlockVector) o));
}
public boolean add(long encoded) {
return this.set.add(encoded);
}
public boolean add(int x, int y, int z) {
return this.add(encodePos(x, y, z));
}
@Override
public boolean add(BlockVector vector) {
return this.add(encodePos(vector));
}
public boolean remove(long encoded) {
return this.set.remove(encoded);
}
public boolean remove(int x, int y, int z) {
return this.remove(encodePos(x, y, z));
}
@Override
public boolean remove(Object o) {
return o instanceof BlockVector && this.remove(encodePos((BlockVector) o));
}
public boolean containsAll(long[] encoded) {
return this.set.containsAll(encoded);
}
public boolean containsAll(TLongCollection encoded) {
return this.set.containsAll(encoded);
}
@Override
public boolean containsAll(Collection<?> c) {
for(Object o : c) {
if(!this.contains(o)) return false;
}
return true;
}
public boolean addAll(long[] encoded) {
return this.set.addAll(encoded);
}
public boolean addAll(TLongCollection encoded) {
return this.set.addAll(encoded);
}
@Override
public boolean addAll(Collection<? extends BlockVector> vectors) {
for(BlockVector v : vectors) {
this.add(v);
}
return false;
}
public boolean retainAll(long[] encoded) {
return this.set.retainAll(encoded);
}
public boolean retainAll(TLongCollection encoded) {
return this.set.retainAll(encoded);
}
@Override
public boolean retainAll(Collection<?> vectors) {
return this.retainAll(BlockUtils.encodePosSet(vectors));
}
public boolean removeAll(TLongCollection encoded) {
return set.removeAll(encoded);
}
public boolean removeAll(long[] encoded) {
return set.removeAll(encoded);
}
@Override
public boolean removeAll(Collection<?> vectors) {
return this.removeAll(BlockUtils.encodePosSet(vectors));
}
@Override
public void clear() {
this.set.clear();
}
@Override
public Object[] toArray() {
throw new UnsupportedOperationException();
}
@Override
public <T> T[] toArray(T[] a) {
throw new UnsupportedOperationException();
}
public BlockVector chooseRandom(Random random) {
// The Trove set uses a sparse array, so there isn't really any
// faster way to do this, not even by messing with Trove internals.
final TLongIterator iterator = set.iterator();
long encoded = 0;
for(int n = random.nextInt(size()); n >= 0; n--) {
encoded = iterator.next();
}
return decodePos(encoded);
}
}