package mekanism.api.transmitters;
import java.util.HashMap;
import java.util.HashSet;
import mekanism.api.Coord4D;
import mekanism.api.MekanismAPI;
import net.minecraftforge.common.util.ForgeDirection;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
import cpw.mods.fml.common.gameevent.TickEvent.ServerTickEvent;
import cpw.mods.fml.relauncher.Side;
public class TransmitterNetworkRegistry
{
private static TransmitterNetworkRegistry INSTANCE = new TransmitterNetworkRegistry();
private static boolean loaderRegistered = false;
private HashSet<DynamicNetwork> networks = Sets.newHashSet();
private HashSet<DynamicNetwork> networksToChange = Sets.newHashSet();
private HashSet<IGridTransmitter> invalidTransmitters = Sets.newHashSet();
private HashMap<Coord4D, IGridTransmitter> orphanTransmitters = Maps.newHashMap();
private Logger logger = LogManager.getLogger("MekanismTransmitters");
public static void initiate()
{
if(!loaderRegistered)
{
loaderRegistered = true;
FMLCommonHandler.instance().bus().register(INSTANCE);
}
}
public static void reset()
{
getInstance().networks.clear();
getInstance().networksToChange.clear();
getInstance().invalidTransmitters.clear();
getInstance().orphanTransmitters.clear();
}
public static void invalidateTransmitter(IGridTransmitter transmitter)
{
getInstance().invalidTransmitters.add(transmitter);
}
public static void registerOrphanTransmitter(IGridTransmitter transmitter)
{
getInstance().orphanTransmitters.put(transmitter.coord(), transmitter);
}
public static void registerChangedNetwork(DynamicNetwork network)
{
getInstance().networksToChange.add(network);
}
public static TransmitterNetworkRegistry getInstance()
{
return INSTANCE;
}
public void registerNetwork(DynamicNetwork network)
{
networks.add(network);
}
public void removeNetwork(DynamicNetwork network)
{
if(networks.contains(network))
{
networks.remove(network);
}
}
@SubscribeEvent
public void onTick(ServerTickEvent event)
{
if(event.phase == Phase.END && event.side == Side.SERVER)
{
tickEnd();
}
}
public void tickEnd()
{
removeInvalidTransmitters();
assignOrphans();
commitChanges();
for(DynamicNetwork net : networks)
{
net.tick();
}
}
public void removeInvalidTransmitters()
{
if(MekanismAPI.debug && !invalidTransmitters.isEmpty())
{
logger.debug("Dealing with " + invalidTransmitters.size() + " invalid Transmitters");
}
for(IGridTransmitter invalid : invalidTransmitters)
{
if(!invalid.isOrphan())
{
DynamicNetwork n = invalid.getTransmitterNetwork();
if(n != null)
{
n.invalidate();
}
}
}
invalidTransmitters.clear();
}
public void assignOrphans()
{
if(MekanismAPI.debug && !orphanTransmitters.isEmpty())
{
logger.debug("Dealing with " + orphanTransmitters.size() + " orphan Transmitters");
}
for(IGridTransmitter orphanTransmitter : orphanTransmitters.values())
{
DynamicNetwork network = getNetworkFromOrphan(orphanTransmitter);
if(network != null)
{
networksToChange.add(network);
network.register();
}
}
orphanTransmitters.clear();
}
public <A, N extends DynamicNetwork<A, N>> DynamicNetwork<A, N> getNetworkFromOrphan(IGridTransmitter<A, N> startOrphan)
{
if(startOrphan.isValid() && startOrphan.isOrphan())
{
OrphanPathFinder<A, N> finder = new OrphanPathFinder<A, N>(startOrphan);
finder.start();
N network;
switch(finder.networksFound.size())
{
case 0:
if(MekanismAPI.debug)
{
logger.debug("No networks found. Creating new network");
}
network = startOrphan.createEmptyNetwork();
break;
case 1:
if(MekanismAPI.debug)
{
logger.debug("Using single found network");
}
network = finder.networksFound.iterator().next();
break;
default:
if(MekanismAPI.debug)
{
logger.debug("Merging " + finder.networksFound.size() + " networks");
}
network = startOrphan.mergeNetworks(finder.networksFound);
}
network.addNewTransmitters(finder.connectedTransmitters);
return network;
}
return null;
}
public void commitChanges()
{
for(DynamicNetwork network : networksToChange)
{
network.commit();
}
networksToChange.clear();
}
@Override
public String toString()
{
return "Network Registry:\n" + networks;
}
public String[] toStrings()
{
String[] strings = new String[networks.size()];
int i = 0;
for(DynamicNetwork network : networks)
{
strings[i++] = network.toString();
}
return strings;
}
public class OrphanPathFinder<A, N extends DynamicNetwork<A, N>>
{
public IGridTransmitter<A, N> startPoint;
public HashSet<Coord4D> iterated = Sets.newHashSet();
public HashSet<IGridTransmitter<A, N>> connectedTransmitters = Sets.newHashSet();
public HashSet<N> networksFound = Sets.newHashSet();
public OrphanPathFinder(IGridTransmitter<A, N> start)
{
startPoint = start;
}
public void start()
{
iterate(startPoint.coord(), ForgeDirection.UNKNOWN);
}
public void iterate(Coord4D from, ForgeDirection fromDirection)
{
if(iterated.contains(from))
{
return;
}
iterated.add(from);
if(orphanTransmitters.containsKey(from))
{
IGridTransmitter<A, N> transmitter = orphanTransmitters.get(from);
if(transmitter.isValid() && transmitter.isOrphan())
{
connectedTransmitters.add(transmitter);
transmitter.setOrphan(false);
for(ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS)
{
if(direction != fromDirection)
{
Coord4D directionCoord = transmitter.getAdjacentConnectableTransmitterCoord(direction);
if(!(directionCoord == null || iterated.contains(directionCoord)))
{
iterate(directionCoord, direction.getOpposite());
}
}
}
}
}
else {
addNetworkToIterated(from);
}
}
public void addNetworkToIterated(Coord4D from)
{
N net = startPoint.getExternalNetwork(from);
if(net != null) networksFound.add(net);
}
}
}