package crazypants.enderio.conduit.redstone;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import cofh.api.tileentity.IRedstoneControl;
import com.enderio.core.client.render.BoundingBox;
import com.enderio.core.client.render.IconUtil;
import com.enderio.core.common.util.BlockCoord;
import com.enderio.core.common.util.DyeColor;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import crazypants.enderio.EnderIO;
import crazypants.enderio.Log;
import crazypants.enderio.api.redstone.IRedstoneConnectable;
import crazypants.enderio.conduit.ConduitUtil;
import crazypants.enderio.conduit.ConnectionMode;
import crazypants.enderio.conduit.IConduit;
import crazypants.enderio.conduit.RaytraceResult;
import crazypants.enderio.conduit.geom.CollidableCache.CacheKey;
import crazypants.enderio.conduit.geom.CollidableComponent;
import crazypants.enderio.conduit.geom.ConduitGeometryUtil;
import crazypants.enderio.tool.ToolUtil;
public class InsulatedRedstoneConduit extends RedstoneConduit implements IInsulatedRedstoneConduit {
static final Map<String, IIcon> ICONS = new HashMap<String, IIcon>();
@SideOnly(Side.CLIENT)
public static void initIcons() {
IconUtil.addIconProvider(new IconUtil.IIconProvider() {
@Override
public void registerIcons(IIconRegister register) {
ICONS.put(KEY_INS_CORE_OFF_ICON, register.registerIcon(KEY_INS_CORE_OFF_ICON));
ICONS.put(KEY_INS_CORE_ON_ICON, register.registerIcon(KEY_INS_CORE_ON_ICON));
ICONS.put(KEY_INS_CONDUIT_ICON, register.registerIcon(KEY_INS_CONDUIT_ICON));
}
@Override
public int getTextureType() {
return 0;
}
});
}
private static final List<Block> CONECTABLE_BLOCKS = Arrays.asList(Blocks.redstone_lamp, Blocks.lit_redstone_lamp, Blocks.redstone_torch,
Blocks.redstone_wire, Blocks.redstone_block, Blocks.dispenser, Blocks.lever, Blocks.wooden_button, Blocks.stone_button, Blocks.wooden_pressure_plate,
Blocks.stone_pressure_plate, Blocks.dropper, Blocks.daylight_detector, Blocks.command_block, Blocks.golden_rail, Blocks.trapped_chest, Blocks.piston,
Blocks.sticky_piston, Blocks.noteblock);
private static Map<Class<?>, Boolean> CONNECTABLE_CLASSES = null;
public static void addConnectableBlock(NBTTagCompound nbt) {
if(nbt == null) {
Log.warn("InsulatedRedstoneConduit: An attempt was made to register a redstone connectable using a null NBT");
return;
}
boolean connectable = true;
if(nbt.hasKey("isConnectable")) {
connectable = nbt.getBoolean("isConnectable");
}
String className = nbt.getString("className");
addConnectableInterface(className, connectable);
}
public static void addConnectableBlock(Block block) {
if(block == null) {
Log.warn("InsulatedRedstoneConduit: An attempt was made to register a redstone connectable using a null Block");
return;
}
CONECTABLE_BLOCKS.add(block);
}
public static void addConnectableInterface(String className, boolean connectable) {
try {
Class<?> clz = Class.forName(className);
addConnectableInterface(clz, connectable);
} catch (Exception e) {
Log.warn("InsulatedRedstoneConduit: An attempt was made to register " + className + " as connectable but it could not be loaded");
}
}
public static void addConnectableInterface(Class<?> clazz, boolean connectable) {
if(clazz == null) {
Log.warn("InsulatedRedstoneConduit: An attempt was made to register a null class as a connectable");
return;
}
getConnectableInterfaces().put(clazz, connectable);
}
private static Map<Class<?>, Boolean> getConnectableInterfaces() {
if(CONNECTABLE_CLASSES == null) {
CONNECTABLE_CLASSES = new HashMap<Class<?>, Boolean>();
CONNECTABLE_CLASSES.put(IRedstoneControl.class, false);
try {
Class<?> conInterface = Class.forName("powercrystals.minefactoryreloaded.api.rednet.connectivity.IRedNetConnection");
CONNECTABLE_CLASSES.put(conInterface, false);
} catch (Throwable e) {
//NO-OP
}
try {
Class<?> ccInterface = Class.forName("dan200.computercraft.shared.computer.blocks.IComputerTile");
CONNECTABLE_CLASSES.put(ccInterface, true);
} catch (Throwable e) {
//NO-OP
}
}
return CONNECTABLE_CLASSES;
}
private Map<ForgeDirection, ConnectionMode> forcedConnections = new HashMap<ForgeDirection, ConnectionMode>();
private Map<ForgeDirection, DyeColor> signalColors = new HashMap<ForgeDirection, DyeColor>();
private Map<ForgeDirection, Boolean> signalStrengths = new HashMap<ForgeDirection, Boolean>();
@Override
public boolean onBlockActivated(EntityPlayer player, RaytraceResult res, List<RaytraceResult> all) {
World world = getBundle().getEntity().getWorldObj();
if(!world.isRemote) {
DyeColor col = DyeColor.getColorFromDye(player.getCurrentEquippedItem());
if(col != null && res.component != null) {
setSignalColor(res.component.dir, col);
return true;
} else if(ToolUtil.isToolEquipped(player)) {
if(res != null && res.component != null) {
ForgeDirection connDir = res.component.dir;
ForgeDirection faceHit = ForgeDirection.getOrientation(res.movingObjectPosition.sideHit);
boolean colorHit = false;
if(all != null && containsExternalConnection(connDir)) {
for (RaytraceResult rtr : all) {
if(rtr != null && rtr.component != null && COLOR_CONTROLLER_ID.equals(rtr.component.data)) {
colorHit = true;
}
}
}
if(colorHit) {
setSignalColor(connDir, DyeColor.getNext(getSignalColor(connDir)));
return true;
} else if(connDir == ForgeDirection.UNKNOWN || connDir == faceHit) {
BlockCoord loc = getLocation().getLocation(faceHit);
Block id = world.getBlock(loc.x, loc.y, loc.z);
if(id == EnderIO.blockConduitBundle) {
IRedstoneConduit neighbour = ConduitUtil.getConduit(world, loc.x, loc.y, loc.z, IRedstoneConduit.class);
if(neighbour != null && neighbour.getConnectionMode(faceHit.getOpposite()) == ConnectionMode.DISABLED) {
neighbour.setConnectionMode(faceHit.getOpposite(), ConnectionMode.NOT_SET);
}
setConnectionMode(faceHit, ConnectionMode.NOT_SET);
return ConduitUtil.joinConduits(this, faceHit);
}
forceConnectionMode(faceHit, ConnectionMode.IN_OUT);
return true;
} else if(externalConnections.contains(connDir)) {
if(network != null) {
network.destroyNetwork();
}
externalConnectionRemoved(connDir);
forceConnectionMode(connDir, ConnectionMode.DISABLED);
return true;
} else if(containsConduitConnection(connDir)) {
BlockCoord loc = getLocation().getLocation(connDir);
IRedstoneConduit neighbour = ConduitUtil.getConduit(getBundle().getEntity().getWorldObj(), loc.x, loc.y, loc.z, IRedstoneConduit.class);
if(neighbour != null) {
if(network != null) {
network.destroyNetwork();
}
if(neighbour.getNetwork() != null) {
neighbour.getNetwork().destroyNetwork();
}
neighbour.conduitConnectionRemoved(connDir.getOpposite());
conduitConnectionRemoved(connDir);
neighbour.connectionsChanged();
connectionsChanged();
updateNetwork();
neighbour.updateNetwork();
return true;
}
}
}
}
}
return false;
}
@Override
protected void readTypeSettings(ForgeDirection dir, NBTTagCompound dataRoot) {
setSignalColor(dir, DyeColor.values()[dataRoot.getShort("signalColor")]);
setOutputStrength(dir, dataRoot.getBoolean("signalStrong"));
}
@Override
protected void writeTypeSettingsToNbt(ForgeDirection dir, NBTTagCompound dataRoot) {
dataRoot.setShort("signalColor", (short) getSignalColor(dir).ordinal());
dataRoot.setBoolean("signalStrong", isOutputStrong(dir));
}
@Override
public void forceConnectionMode(ForgeDirection dir, ConnectionMode mode) {
if(mode == ConnectionMode.IN_OUT) {
setConnectionMode(dir, mode);
forcedConnections.put(dir, mode);
onAddedToBundle();
Set<Signal> newSignals = getNetworkInputs(dir);
if(network != null) {
network.addSignals(newSignals);
network.notifyNeigborsOfSignals();
}
} else {
Set<Signal> signals = getNetworkInputs(dir);
setConnectionMode(dir, mode);
forcedConnections.put(dir, mode);
onAddedToBundle();
if(network != null) {
network.removeSignals(signals);
network.notifyNeigborsOfSignals();
}
}
}
@Override
public ConnectionMode getNextConnectionMode(ForgeDirection dir) {
if(getConnectionMode(dir) == ConnectionMode.IN_OUT) {
return ConnectionMode.DISABLED;
}
return ConnectionMode.IN_OUT;
}
@Override
public ConnectionMode getPreviousConnectionMode(ForgeDirection dir) {
if(getConnectionMode(dir) == ConnectionMode.IN_OUT) {
return ConnectionMode.DISABLED;
}
return ConnectionMode.IN_OUT;
}
@Override
public ItemStack createItem() {
return new ItemStack(EnderIO.itemRedstoneConduit, 1, 2);
}
@Override
public Class<? extends IConduit> getCollidableType() {
return IInsulatedRedstoneConduit.class;
}
@Override
public void onInputsChanged(ForgeDirection side, int[] inputValues) {
}
@Override
public void onInputChanged(ForgeDirection side, int inputValue) {
}
@Override
public DyeColor getSignalColor(ForgeDirection dir) {
DyeColor res = signalColors.get(dir);
if(res == null) {
return DyeColor.RED;
}
return res;
}
@Override
public void setSignalColor(ForgeDirection dir, DyeColor col) {
Set<Signal> toRemove = getNetworkInputs(dir);
signalColors.put(dir, col);
Set<Signal> toAdd = getNetworkInputs(dir);
if(network != null) {
network.removeSignals(toRemove);
network.addSignals(toAdd);
network.notifyNeigborsOfSignals();
}
setClientStateDirty();
}
@Override
public boolean isOutputStrong(ForgeDirection dir) {
if(signalStrengths.containsKey(dir)) {
return signalStrengths.get(dir);
}
return false;
}
@Override
public void setOutputStrength(ForgeDirection dir, boolean isStrong) {
if(isOutputStrong(dir) != isStrong) {
if(isStrong) {
signalStrengths.put(dir, isStrong);
} else {
signalStrengths.remove(dir);
}
if(network != null) {
network.notifyNeigborsOfSignals();
}
}
}
@Override
public boolean canConnectToExternal(ForgeDirection direction, boolean ignoreConnectionState) {
if(ignoreConnectionState) { //you can always force an external connection
return true;
}
if(forcedConnections.get(direction) == ConnectionMode.DISABLED) {
return false;
} else if(forcedConnections.get(direction) == ConnectionMode.IN_OUT) {
return true;
}
//Not set so figure it out
BlockCoord loc = getLocation().getLocation(direction);
World world = getBundle().getEntity().getWorldObj();
Block block = world.getBlock(loc.x, loc.y, loc.z);
TileEntity te = world.getTileEntity(loc.x, loc.y, loc.z);
if(block == null || block == EnderIO.blockConduitBundle) {
return false;
}
if(CONECTABLE_BLOCKS.contains(block)) {
return true;
}
if (block instanceof IRedstoneConnectable) {
return ((IRedstoneConnectable) block).shouldRedstoneConduitConnect(world, loc.x, loc.y, loc.z, direction);
}
if (te instanceof IRedstoneConnectable) {
return ((IRedstoneConnectable) te).shouldRedstoneConduitConnect(world, loc.x, loc.y, loc.z, direction);
}
Map<Class<?>, Boolean> connectableInterfaces = getConnectableInterfaces();
for(Class<?> connectable : connectableInterfaces.keySet()) {
if((te != null && connectable.isAssignableFrom(te.getClass())) || (connectable.isAssignableFrom(block.getClass()))) {
return true;
}
}
return false;
}
@Override
public boolean isSpecialConnection(ForgeDirection dir){
BlockCoord loc = getLocation().getLocation(dir);
Block block = getBundle().getEntity().getWorldObj().getBlock(loc.x, loc.y, loc.z);
World world = getBundle().getEntity().getWorldObj();
TileEntity te = world.getTileEntity(loc.x, loc.y, loc.z);
Map<Class<?>, Boolean> connectableInterfaces = getConnectableInterfaces();
for (Class<?> connectable : connectableInterfaces.keySet()) {
if((te != null && connectable.isAssignableFrom(te.getClass())) || (block != null && connectable.isAssignableFrom(block.getClass()))) {
return connectableInterfaces.get(connectable);
}
}
return false;
}
@Override
public int isProvidingWeakPower(ForgeDirection toDirection) {
if(getConnectionMode(toDirection.getOpposite()) != ConnectionMode.IN_OUT) {
return 0;
}
return super.isProvidingWeakPower(toDirection);
}
@Override
public int isProvidingStrongPower(ForgeDirection toDirection) {
if(isOutputStrong(toDirection.getOpposite())) {
return isProvidingWeakPower(toDirection);
} else {
return 0;
}
}
@Override
public void externalConnectionAdded(ForgeDirection fromDirection) {
super.externalConnectionAdded(fromDirection);
setConnectionMode(fromDirection, ConnectionMode.IN_OUT);
}
@Override
public void externalConnectionRemoved(ForgeDirection fromDirection) {
super.externalConnectionRemoved(fromDirection);
setConnectionMode(fromDirection, ConnectionMode.NOT_SET);
}
@Override
public Set<Signal> getNetworkOutputs(ForgeDirection side) {
if(side == null || side == ForgeDirection.UNKNOWN) {
return super.getNetworkOutputs(side);
}
ConnectionMode mode = getConnectionMode(side);
if(network == null || mode != ConnectionMode.IN_OUT) {
return Collections.emptySet();
}
Set<Signal> allSigs = network.getSignals();
if(allSigs.isEmpty()) {
return allSigs;
}
DyeColor col = getSignalColor(side);
Set<Signal> result = new HashSet<Signal>();
for (Signal signal : allSigs) {
if(signal.color == col) {
result.add(signal);
}
}
return result;
}
@Override
public ConnectionMode getConnectionMode(ForgeDirection dir) {
ConnectionMode res = conectionModes.get(dir);
if(res == null) {
return ConnectionMode.NOT_SET;
}
return res;
}
@Override
protected boolean acceptSignalsForDir(ForgeDirection dir) {
return getConnectionMode(dir) == ConnectionMode.IN_OUT && super.acceptSignalsForDir(dir);
}
@Override
public Collection<CollidableComponent> createCollidables(CacheKey key) {
Collection<CollidableComponent> baseCollidables = super.createCollidables(key);
if(key.dir == ForgeDirection.UNKNOWN) {
return baseCollidables;
}
BoundingBox bb = ConduitGeometryUtil.instance.createBoundsForConnectionController(key.dir, key.offset);
CollidableComponent cc = new CollidableComponent(IRedstoneConduit.class, bb, key.dir, COLOR_CONTROLLER_ID);
List<CollidableComponent> result = new ArrayList<CollidableComponent>();
result.addAll(baseCollidables);
result.add(cc);
return result;
}
@Override
public IIcon getTextureForState(CollidableComponent component) {
if(component.dir == ForgeDirection.UNKNOWN) {
return isActive() ? ICONS.get(KEY_INS_CORE_ON_ICON) : ICONS.get(KEY_INS_CORE_OFF_ICON);
}
if(COLOR_CONTROLLER_ID.equals(component.data)) {
return IconUtil.whiteTexture;
}
return ICONS.get(KEY_INS_CONDUIT_ICON);
}
@Override
public IIcon getTransmitionTextureForState(CollidableComponent component) {
return isActive() ? RedstoneConduit.ICONS.get(KEY_TRANSMISSION_ICON) : RedstoneConduit.ICONS.get(KEY_CONDUIT_ICON);
}
@Override
protected boolean renderStub(ForgeDirection dir) {
return false;
}
@Override
public void writeToNBT(NBTTagCompound nbtRoot) {
super.writeToNBT(nbtRoot);
if(forcedConnections.size() >= 0) {
byte[] modes = new byte[6];
int i = 0;
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
ConnectionMode mode = forcedConnections.get(dir);
if(mode != null) {
modes[i] = (byte) mode.ordinal();
} else {
modes[i] = -1;
}
i++;
}
nbtRoot.setByteArray("forcedConnections", modes);
}
if(signalColors.size() >= 0) {
byte[] modes = new byte[6];
int i = 0;
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
DyeColor col = signalColors.get(dir);
if(col != null) {
modes[i] = (byte) col.ordinal();
} else {
modes[i] = -1;
}
i++;
}
nbtRoot.setByteArray("signalColors", modes);
}
if(signalStrengths.size() >= 0) {
byte[] modes = new byte[6];
int i = 0;
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
boolean isStrong = isOutputStrong(dir);
if(isStrong) {
modes[i] = 1;
} else {
modes[i] = 0;
}
i++;
}
nbtRoot.setByteArray("signalStrengths", modes);
}
}
@Override
public void readFromNBT(NBTTagCompound nbtRoot, short nbtVersion) {
super.readFromNBT(nbtRoot, nbtVersion);
forcedConnections.clear();
byte[] modes = nbtRoot.getByteArray("forcedConnections");
if(modes != null && modes.length == 6) {
int i = 0;
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
if(modes[i] >= 0) {
forcedConnections.put(dir, ConnectionMode.values()[modes[i]]);
}
i++;
}
}
signalColors.clear();
byte[] cols = nbtRoot.getByteArray("signalColors");
if(cols != null && cols.length == 6) {
int i = 0;
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
if(cols[i] >= 0) {
signalColors.put(dir, DyeColor.values()[cols[i]]);
}
i++;
}
}
signalStrengths.clear();
byte[] strengths = nbtRoot.getByteArray("signalStrengths");
if(strengths != null && strengths.length == 6) {
int i = 0;
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
if(strengths[i] > 0) {
signalStrengths.put(dir, true);
}
i++;
}
}
}
}