/*
* Copyright (c) CovertJaguar, 2014 http://railcraft.info
*
* This code is the property of CovertJaguar
* and may only be used with explicit written
* permission unless otherwise specified on the
* license page at http://railcraft.info/wiki/info:license.
*/
package mods.railcraft.common.carts;
import com.google.common.collect.Lists;
import mods.railcraft.api.electricity.IElectricMinecart;
import net.minecraft.entity.item.EntityMinecart;
import net.minecraft.nbt.NBTTagCompound;
import java.util.*;
/**
* @author CovertJaguar <http://www.railcraft.info>
*/
public class Train implements Iterable<EntityMinecart> {
public static final String TRAIN_HIGH = "rcTrainHigh";
public static final String TRAIN_LOW = "rcTrainLow";
private static final Map<UUID, Train> trains = new HashMap<UUID, Train>();
private final UUID uuid;
private final LinkedList<UUID> carts = new LinkedList<UUID>();
private final List<UUID> safeCarts = Collections.unmodifiableList(carts);
private final Set<UUID> lockingTracks = new HashSet<UUID>();
private TrainState trainState = TrainState.NORMAL;
public Train(EntityMinecart cart) {
uuid = UUID.randomUUID();
buildTrain(null, cart);
}
public static Train getTrain(EntityMinecart cart) {
if (cart == null)
return null;
Train train = trains.get(getTrainUUID(cart));
if (train != null && !train.containsCart(cart)) {
train.releaseTrain();
trains.remove(train.getUUID());
train = null;
}
if (train != null) {
train.validate();
if (train.isEmpty()) {
train = null;
}
}
if (train == null) {
train = new Train(cart);
trains.put(train.getUUID(), train);
}
return train;
}
private static Train getTrainUnsafe(EntityMinecart cart) {
if (cart == null)
return null;
return trains.get(getTrainUUID(cart));
}
public static UUID getTrainUUID(EntityMinecart cart) {
NBTTagCompound nbt = cart.getEntityData();
if (nbt.hasKey(TRAIN_HIGH)) {
long high = nbt.getLong(TRAIN_HIGH);
long low = nbt.getLong(TRAIN_LOW);
return new UUID(high, low);
}
return null;
}
public static void resetTrain(EntityMinecart cart) {
Train train = trains.remove(getTrainUUID(cart));
if (train != null)
train.releaseTrain();
}
public static void resetTrain(UUID uuid) {
Train train = trains.remove(uuid);
if (train != null)
train.releaseTrain();
}
public static boolean areInSameTrain(EntityMinecart cart1, EntityMinecart cart2) {
if (cart1 == null || cart2 == null)
return false;
if (cart1 == cart2)
return true;
UUID train1 = getTrainUUID(cart1);
UUID train2 = getTrainUUID(cart2);
return train1 != null && train1.equals(train2);
}
public static Train getLongestTrain(EntityMinecart cart1, EntityMinecart cart2) {
Train train1 = getTrain(cart1);
Train train2 = getTrain(cart2);
if (train1 == train2)
return train1;
if (train1.size() >= train2.size())
return train1;
return train2;
}
public static void removeTrainTag(EntityMinecart cart) {
cart.getEntityData().removeTag(TRAIN_HIGH);
cart.getEntityData().removeTag(TRAIN_LOW);
}
public static void addTrainTag(EntityMinecart cart, Train train) {
UUID trainId = train.getUUID();
cart.getEntityData().setLong(TRAIN_HIGH, trainId.getMostSignificantBits());
cart.getEntityData().setLong(TRAIN_LOW, trainId.getLeastSignificantBits());
}
public Train getTrain(UUID cartUUID) {
if (cartUUID == null)
return null;
EntityMinecart cart = LinkageManager.instance().getCartFromUUID(cartUUID);
if (cart == null)
return null;
return getTrain(cart);
}
@Override
public Iterator<EntityMinecart> iterator() {
return new Iterator<EntityMinecart>() {
private final Iterator<UUID> it = carts.iterator();
private final LinkageManager lm = LinkageManager.instance();
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public EntityMinecart next() {
return lm.getCartFromUUID(it.next());
}
@Override
public void remove() {
throw new UnsupportedOperationException("Removing not supported.");
}
};
}
public Iterable<EntityMinecart> orderedIteratable(final EntityMinecart head) {
return new Iterable<EntityMinecart>() {
@Override
public Iterator<EntityMinecart> iterator() {
return new Iterator<EntityMinecart>() {
private final LinkageManager lm = LinkageManager.instance();
private EntityMinecart last = null;
private EntityMinecart current = head;
@Override
public boolean hasNext() {
EntityMinecart next = lm.getLinkedCartA(current);
if (next != null && next != last)
return true;
next = lm.getLinkedCartB(current);
if (next != null && next != last)
return true;
return false;
}
@Override
public EntityMinecart next() {
EntityMinecart next = lm.getLinkedCartA(current);
if (next != last) {
last = current;
current = next;
return current;
}
next = lm.getLinkedCartB(current);
if (next != last) {
last = current;
current = next;
return current;
}
return null;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Removing not supported.");
}
};
}
};
}
private void buildTrain(EntityMinecart prev, EntityMinecart next) {
_addLink(prev, next);
LinkageManager lm = LinkageManager.instance();
EntityMinecart linkA = lm.getLinkedCartA(next);
EntityMinecart linkB = lm.getLinkedCartB(next);
if (linkA != null && linkA != prev && !containsCart(linkA))
buildTrain(next, linkA);
if (linkB != null && linkB != prev && !containsCart(linkB))
buildTrain(next, linkB);
}
private void dropCarts(EntityMinecart cart) {
_removeCart(cart);
LinkageManager lm = LinkageManager.instance();
EntityMinecart linkA = lm.getLinkedCartA(cart);
EntityMinecart linkB = lm.getLinkedCartB(cart);
if (linkA != null && containsCart(linkA)) dropCarts(linkA);
if (linkB != null && containsCart(linkB)) dropCarts(linkB);
}
public void validate() {
if (!isValid()) {
UUID first = null;
for (UUID id : carts) {
if (isCartValid(id)) {
first = id;
break;
}
}
releaseTrain();
if (first == null)
trains.remove(getUUID());
else
buildTrain(null, LinkageManager.instance().getCartFromUUID(first));
}
}
private boolean isValid() {
for (UUID id : carts) {
if (!isCartValid(id))
return false;
}
return true;
}
private boolean isCartValid(UUID cartId) {
EntityMinecart cart = LinkageManager.instance().getCartFromUUID(cartId);
return cart != null && uuid.equals(getTrainUUID(cart));
}
protected void releaseTrain() {
LinkageManager lm = LinkageManager.instance();
for (UUID id : carts) {
EntityMinecart cart = lm.getCartFromUUID(id);
if (cart != null) {
removeTrainTag(cart);
}
}
carts.clear();
lockingTracks.clear();
}
public UUID getUUID() {
return uuid;
}
public void removeAllLinkedCarts(EntityMinecart cart) {
UUID cartId = cart.getPersistentID();
if (carts.contains(cartId)) {
dropCarts(cart);
}
}
public void addLink(EntityMinecart cart1, EntityMinecart cart2) {
if (isTrainEnd(cart1))
buildTrain(cart1, cart2);
else if (isTrainEnd(cart2))
buildTrain(cart2, cart1);
}
private void _addLink(EntityMinecart cartBase, EntityMinecart cartNew) {
if (cartBase == null || carts.getFirst() == cartBase.getPersistentID())
carts.addFirst(cartNew.getPersistentID());
else if (carts.getLast() == cartBase.getPersistentID())
carts.addLast(cartNew.getPersistentID());
else
return;
Train train = getTrainUnsafe(cartNew);
if (train != null && train != this)
train._removeCart(cartNew);
addTrainTag(cartNew, this);
}
private boolean _removeCart(EntityMinecart cart) {
boolean removed = _removeCart(cart.getPersistentID());
if (removed && uuid.equals(getTrainUUID(cart))) {
removeTrainTag(cart);
}
return removed;
}
private boolean _removeCart(UUID cart) {
boolean removed = carts.remove(cart);
if (removed) {
if (carts.isEmpty()) {
releaseTrain();
trains.remove(getUUID());
}
}
return removed;
}
public boolean containsCart(EntityMinecart cart) {
if (cart == null)
return false;
return carts.contains(cart.getPersistentID());
}
public boolean isTrainEnd(EntityMinecart cart) {
if (cart == null)
return false;
return getEnds().contains(cart.getPersistentID());
}
public Set<UUID> getEnds() {
Set<UUID> ends = new HashSet<UUID>();
ends.add(carts.getFirst());
ends.add(carts.getLast());
return ends;
}
public EntityLocomotive getLocomotive() {
LinkageManager lm = LinkageManager.instance();
for (UUID id : getEnds()) {
EntityMinecart cart = lm.getCartFromUUID(id);
if (cart instanceof EntityLocomotive)
return (EntityLocomotive) cart;
}
return null;
}
public <T extends EntityMinecart> Collection<T> getCarts(Class<T> cartClass) {
List<T> list = Lists.newArrayList();
for (EntityMinecart cart : this) {
if (cartClass.isInstance(cart))
list.add(cartClass.cast(cart));
}
return list;
}
public List<UUID> getUUIDs() {
return safeCarts;
}
public int size() {
return carts.size();
}
public boolean isEmpty() {
return carts.isEmpty();
}
public int getNumRunningLocomotives() {
int count = 0;
for (EntityMinecart cart : this) {
if (cart instanceof EntityLocomotive && ((EntityLocomotive) cart).isRunning())
count++;
}
return count;
}
public void refreshMaxSpeed() {
setMaxSpeed(getMaxSpeed());
}
public float getMaxSpeed() {
float speed = 1.2F;
int numLocomotives = getNumRunningLocomotives();
for (EntityMinecart c : this) {
float baseSpeed = c.getMaxCartSpeedOnRail();
if (numLocomotives > 0 && !(c instanceof EntityCartEnergy) && c instanceof IElectricMinecart) {
IElectricMinecart e = (IElectricMinecart) c;
if (e.getChargeHandler().getType() != IElectricMinecart.ChargeHandler.Type.USER) {
baseSpeed = Math.min(0.2F, 0.03F + (numLocomotives - 1) * 0.075F);
}
}
speed = Math.min(speed, baseSpeed);
}
return speed;
}
public void setMaxSpeed(float trainSpeed) {
for (EntityMinecart c : this) {
c.setCurrentCartSpeedCapOnRail(trainSpeed);
}
}
public boolean isTrainLockedDown() {
return !lockingTracks.isEmpty();
}
public void addLockingTrack(UUID track) {
lockingTracks.add(track);
}
public void removeLockingTrack(UUID track) {
lockingTracks.remove(track);
}
public boolean isIdle() {
return trainState == TrainState.IDLE || isTrainLockedDown();
}
public boolean isStopped() {
return trainState == TrainState.STOPPED;
}
public void setTrainState(TrainState state) {
this.trainState = state;
}
public enum TrainState {
STOPPED,
IDLE,
NORMAL
}
}