package crazypants.enderio.conduit.redstone;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import com.enderio.core.common.util.BlockCoord;
import com.google.common.collect.Sets;
import crazypants.enderio.EnderIO;
import crazypants.enderio.conduit.AbstractConduitNetwork;
import crazypants.enderio.conduit.IConduitBundle;
public class RedstoneConduitNetwork extends AbstractConduitNetwork<IRedstoneConduit, IRedstoneConduit> {
private final Set<Signal> signals = new HashSet<Signal>();
boolean updatingNetwork = false;
private boolean networkEnabled = true;
public RedstoneConduitNetwork() {
super(IRedstoneConduit.class, IRedstoneConduit.class);
}
@Override
public void init(IConduitBundle tile, Collection<IRedstoneConduit> connections, World world) {
super.init(tile, connections, world);
updatingNetwork = true;
notifyNeigborsOfSignals();
updatingNetwork = false;
}
@Override
public void destroyNetwork() {
updatingNetwork = true;
for (IRedstoneConduit con : conduits) {
con.setActive(false);
}
// Notify neighbours that all signals have been lost
List<Signal> copy = new ArrayList<Signal>(signals);
signals.clear();
for (Signal s : copy) {
notifyNeigborsOfSignalUpdate(s);
}
updatingNetwork = false;
super.destroyNetwork();
}
@Override
public void addConduit(IRedstoneConduit con) {
updatingNetwork = true;
super.addConduit(con);
Set<Signal> newInputs = con.getNetworkInputs();
signals.addAll(newInputs);
// Notify existing nodes of new signals
for (Signal signal : newInputs) {
notifyNeigborsOfSignalUpdate(signal);
}
// and new nodes neighbours of all signals
for (Signal signal : signals) {
notifyConduitNeighbours(con, signal);
}
updatingNetwork = false;
}
public Set<Signal> getSignals() {
if (networkEnabled) {
return signals;
} else {
return Collections.emptySet();
}
}
// Need to disable the network when determining the strength of external
// signals
// to avoid feed back looops
void setNetworkEnabled(boolean enabled) {
networkEnabled = enabled;
}
public boolean isNetworkEnabled() {
return networkEnabled;
}
public void addSignals(Set<Signal> newSignals) {
for (Signal signal : newSignals) {
addSignal(signal);
}
}
public void addSignal(Signal signal) {
updatingNetwork = true;
signals.add(signal);
notifyNetworkOfUpdate();
notifyNeigborsOfSignalUpdate(signal);
updatingNetwork = false;
}
public void removeSignals(Set<Signal> remove) {
for (Signal signal : remove) {
removeSignal(signal);
}
}
public void removeSignal(Signal signal) {
updatingNetwork = true;
signals.remove(signal);
notifyNetworkOfUpdate();
notifyNeigborsOfSignalUpdate(signal);
updatingNetwork = false;
}
public void replaceSignal(Signal oldSig, Signal newSig) {
updatingNetwork = true;
signals.remove(oldSig);
signals.add(newSig);
notifyNetworkOfUpdate();
notifyNeigborsOfSignalUpdate(newSig);
updatingNetwork = false;
}
@Override
public void notifyNetworkOfUpdate() {
for (IRedstoneConduit con : conduits) {
con.setActive(false);
for (Signal s : getSignals()) {
if (s.strength > 0) {
con.setActive(true);
break;
}
}
}
super.notifyNetworkOfUpdate();
}
@Override
public String toString() {
return "RedstoneConduitNetwork [signals=" + signalsString() + ", conduits=" + conduitsString() + "]";
}
private String conduitsString() {
StringBuilder sb = new StringBuilder();
for (IRedstoneConduit con : conduits) {
TileEntity te = con.getBundle().getEntity();
sb.append("<").append(te.xCoord).append(",").append(te.yCoord).append(",").append(te.zCoord).append(">");
}
return sb.toString();
}
String signalsString() {
StringBuilder sb = new StringBuilder();
for (Signal s : signals) {
sb.append("<");
sb.append(s);
sb.append(">");
}
return sb.toString();
}
public void notifyNeigborsOfSignals() {
for (Signal signal : signals) {
notifyNeigborsOfSignalUpdate(signal);
}
}
public void notifyNeigborsOfSignalUpdate(Signal signal) {
ArrayList<IRedstoneConduit> conduitsCopy = new ArrayList<IRedstoneConduit>(conduits);
for (IRedstoneConduit con : conduitsCopy) {
notifyConduitNeighbours(con, signal);
}
}
private void notifyConduitNeighbours(IRedstoneConduit con, Signal signal) {
if (con.getBundle() == null) {
System.out.println("RedstoneConduitNetwork.notifyNeigborsOfSignalUpdate: NULL BUNDLE!!!!");
return;
}
TileEntity te = con.getBundle().getEntity();
World worldObj = te.getWorldObj();
BlockCoord bc1 = new BlockCoord(te);
if (!worldObj.blockExists(te.xCoord, te.yCoord, te.zCoord)) {
return;
}
// Done manually to avoid orphaning chunks
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
BlockCoord bc2 = bc1.getLocation(dir);
if (worldObj.blockExists(bc2.x, bc2.y, bc2.z)) {
worldObj.notifyBlockOfNeighborChange(bc2.x, bc2.y, bc2.z, EnderIO.blockConduitBundle);
if (signal != null && bc2.getBlock(worldObj).isNormalCube()) {
for (ForgeDirection dir2 : ForgeDirection.VALID_DIRECTIONS) {
BlockCoord bc3 = bc2.getLocation(dir2);
if (!bc3.equals(bc1) && worldObj.blockExists(bc3.x, bc3.y, bc3.z)) {
worldObj.notifyBlockOfNeighborChange(bc3.x, bc3.y, bc3.z, EnderIO.blockConduitBundle);
}
}
}
}
}
}
/**
* This is a bit of a hack...avoids the network searching for inputs from
* unloaded chunks by only filtering out the invalid signals from the unloaded
* chunk.
*
* @param conduits
* @param oldSignals
*/
public void afterChunkUnload(List<IRedstoneConduit> conduits, Set<Signal> oldSignals) {
World world = null;
for (IRedstoneConduit c : conduits) {
if (world == null) {
world = c.getBundle().getWorld();
}
BlockCoord loc = c.getLocation();
if (world.blockExists(loc.x, loc.y, loc.z)) {
this.conduits.add(c);
c.setNetwork(this);
}
}
Set<Signal> valid = Sets.newHashSet();
for (Signal s : oldSignals) {
if (world.blockExists(s.x, s.y, s.z)) {
valid.add(s);
}
}
this.addSignals(valid);
}
}