package crazypants.enderio.conduit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import net.minecraft.block.Block.SoundType;
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.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import com.enderio.core.common.util.BlockCoord;
import com.enderio.core.common.util.DyeColor;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import crazypants.enderio.EnderIO;
import crazypants.enderio.GuiHandler;
import crazypants.enderio.Log;
import crazypants.enderio.api.tool.IHideFacades;
import crazypants.enderio.conduit.IConduitBundle.FacadeRenderState;
import crazypants.enderio.conduit.gas.GasUtil;
import crazypants.enderio.conduit.me.MEUtil;
import crazypants.enderio.conduit.oc.OCUtil;
import crazypants.enderio.conduit.redstone.IInsulatedRedstoneConduit;
import crazypants.enderio.conduit.redstone.IRedstoneConduit;
import crazypants.enderio.conduit.redstone.Signal;
import crazypants.enderio.machine.RedstoneControlMode;
import crazypants.enderio.tool.ToolUtil;
public class ConduitUtil {
public static final Random RANDOM = new Random();
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void ensureValidNetwork(IConduit conduit) {
TileEntity te = conduit.getBundle().getEntity();
World world = te.getWorldObj();
Collection<? extends IConduit> connections = ConduitUtil.getConnectedConduits(world, te.xCoord, te.yCoord, te.zCoord, conduit.getBaseConduitType());
if(reuseNetwork(conduit, connections, world)) {
return;
}
AbstractConduitNetwork res = conduit.createNetworkForType();
res.init(conduit.getBundle(), connections, world);
return;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private static boolean reuseNetwork(IConduit con, Collection<? extends IConduit> connections, World world) {
AbstractConduitNetwork network = null;
for (IConduit conduit : connections) {
if(network == null) {
network = conduit.getNetwork();
} else if(network != conduit.getNetwork()) {
return false;
}
}
if(network == null) {
return false;
}
if(con.setNetwork(network)) {
network.addConduit(con);
network.notifyNetworkOfUpdate();
return true;
}
return false;
}
public static <T extends IConduit> void disconectConduits(T con, ForgeDirection connDir) {
con.conduitConnectionRemoved(connDir);
BlockCoord loc = con.getLocation().getLocation(connDir);
IConduit neighbour = ConduitUtil.getConduit(con.getBundle().getEntity().getWorldObj(), loc.x, loc.y, loc.z, con.getBaseConduitType());
if(neighbour != null) {
neighbour.conduitConnectionRemoved(connDir.getOpposite());
if(neighbour.getNetwork() != null) {
neighbour.getNetwork().destroyNetwork();
}
}
if(con.getNetwork() != null) { //this should have been destroyed when destroying the neighbours network but lets just make sure
con.getNetwork().destroyNetwork();
}
con.connectionsChanged();
if(neighbour != null) {
neighbour.connectionsChanged();
}
}
public static <T extends IConduit> boolean joinConduits(T con, ForgeDirection faceHit) {
BlockCoord loc = con.getLocation().getLocation(faceHit);
IConduit neighbour = ConduitUtil.getConduit(con.getBundle().getEntity().getWorldObj(), loc.x, loc.y, loc.z, con.getBaseConduitType());
if(neighbour != null && con.canConnectToConduit(faceHit, neighbour) && neighbour.canConnectToConduit(faceHit.getOpposite(), con)) {
con.conduitConnectionAdded(faceHit);
neighbour.conduitConnectionAdded(faceHit.getOpposite());
if(con.getNetwork() != null) {
con.getNetwork().destroyNetwork();
}
if(neighbour.getNetwork() != null) {
neighbour.getNetwork().destroyNetwork();
}
con.connectionsChanged();
neighbour.connectionsChanged();
return true;
}
return false;
}
public static boolean forceSkylightRecalculation(World worldObj, int xCoord, int yCoord, int zCoord) {
int height = worldObj.getHeightValue(xCoord, zCoord);
if(height <= yCoord) {
for (int i = 1; i < 12; i++) {
if(worldObj.isAirBlock(xCoord, yCoord + i, zCoord)) {
//We need to force the re-lighting of the column due to a change
//in the light reaching bellow the block from the sky. To avoid
//modifying core classes to expose this functionality I am just placing then breaking
//a block above this one to force the check
worldObj.setBlock(xCoord, yCoord + i, zCoord, Blocks.stone, 0, 3);
worldObj.setBlockToAir(xCoord, yCoord + i, zCoord);
return true;
}
}
}
return false;
}
@SideOnly(Side.CLIENT)
public static FacadeRenderState getRequiredFacadeRenderState(IConduitBundle bundle, EntityPlayer player) {
if(!bundle.hasFacade()) {
return FacadeRenderState.NONE;
}
if(isFacadeHidden(bundle, player)) {
return FacadeRenderState.WIRE_FRAME;
}
return FacadeRenderState.FULL;
}
public static boolean isSolidFacadeRendered(IConduitBundle bundle, EntityPlayer player) {
return bundle.getFacadeId() != null && !isFacadeHidden(bundle, player);
}
public static boolean isFacadeHidden(IConduitBundle bundle, EntityPlayer player) {
return bundle.getFacadeId() != null && shouldHeldItemHideFacades(player);
}
public static ConduitDisplayMode getDisplayMode(EntityPlayer player) {
player = player == null ? EnderIO.proxy.getClientPlayer() : player;
if(player == null) {
return ConduitDisplayMode.ALL;
}
ItemStack equipped = player.getCurrentEquippedItem();
if(equipped == null) {
return ConduitDisplayMode.ALL;
}
ConduitDisplayMode result = ConduitDisplayMode.getDisplayMode(equipped);
if(result == null) {
return ConduitDisplayMode.ALL;
}
return result;
}
public static boolean renderConduit(EntityPlayer player, IConduit con) {
if(player == null || con == null) {
return true;
}
return renderConduit(player, con.getBaseConduitType());
}
public static boolean renderConduit(EntityPlayer player, Class<? extends IConduit> conduitType) {
if(player == null || conduitType == null) {
return true;
}
ConduitDisplayMode mode = getDisplayMode(player);
return mode.renderConduit(conduitType);
}
public static boolean shouldHeldItemHideFacades(EntityPlayer player) {
player = player == null ? EnderIO.proxy.getClientPlayer() : player;
if(player == null) {
return false;
}
ItemStack held = player.getCurrentEquippedItem();
if(held != null && held.getItem() instanceof IHideFacades) {
return ((IHideFacades) held.getItem()).shouldHideFacades(held, player);
}
return ToolUtil.isToolEquipped(player);
}
public static boolean isConduitEquipped(EntityPlayer player) {
player = player == null ? EnderIO.proxy.getClientPlayer() : player;
if(player == null) {
return false;
}
ItemStack equipped = player.getCurrentEquippedItem();
if(equipped == null) {
return false;
}
return equipped.getItem() instanceof IConduitItem;
}
public static boolean isProbeEquipped(EntityPlayer player) {
player = player == null ? EnderIO.proxy.getClientPlayer() : player;
if(player == null) {
return false;
}
ItemStack equipped = player.getCurrentEquippedItem();
if(equipped == null) {
return false;
}
return equipped.getItem() == EnderIO.itemConduitProbe;
}
public static <T extends IConduit> T getConduit(World world, int x, int y, int z, Class<T> type) {
if(world == null || !world.blockExists(x, y, z)) {
return null;
}
TileEntity te = world.getTileEntity(x, y, z);
if(te instanceof IConduitBundle) {
IConduitBundle con = (IConduitBundle) te;
return con.getConduit(type);
}
return null;
}
public static <T extends IConduit> T getConduit(World world, TileEntity te, ForgeDirection dir, Class<T> type) {
return ConduitUtil.getConduit(world, te.xCoord + dir.offsetX, te.yCoord + dir.offsetY, te.zCoord + dir.offsetZ, type);
}
public static <T extends IConduit> Collection<T> getConnectedConduits(World world, int x, int y, int z, Class<T> type) {
TileEntity te = world.getTileEntity(x, y, z);
if(!(te instanceof IConduitBundle)) {
return Collections.emptyList();
}
List<T> result = new ArrayList<T>();
IConduitBundle root = (IConduitBundle) te;
T con = root.getConduit(type);
if(con != null) {
for (ForgeDirection dir : con.getConduitConnections()) {
T connected = getConduit(world, root.getEntity(), dir, type);
if(connected != null) {
result.add(connected);
}
}
}
return result;
}
public static void writeToNBT(IConduit conduit, NBTTagCompound conduitRoot) {
if(conduit == null) {
return;
}
NBTTagCompound conduitBody = new NBTTagCompound();
conduit.writeToNBT(conduitBody);
conduitRoot.setString("conduitType", conduit.getClass().getCanonicalName());
conduitRoot.setTag("conduit", conduitBody);
}
public static IConduit readConduitFromNBT(NBTTagCompound conduitRoot, short nbtVersion) {
String typeName = conduitRoot.getString("conduitType");
NBTTagCompound conduitBody = conduitRoot.getCompoundTag("conduit");
if(typeName == null || conduitBody == null) {
return null;
}
if ((typeName.contains("conduit.oc") && !OCUtil.isOCEnabled()) || (typeName.contains("conduit.me") && !MEUtil.isMEEnabled())
|| (typeName.contains("conduit.gas") && !GasUtil.isGasConduitEnabled())) {
return null;
}
if (nbtVersion == 0 && "crazypants.enderio.conduit.liquid.LiquidConduit".equals(typeName)) {
Log.debug("ConduitUtil.readConduitFromNBT: Converted pre 0.7.3 fluid conduit to advanced fluid conduit.");
typeName = "crazypants.enderio.conduit.liquid.AdvancedLiquidConduit";
}
IConduit result;
try {
result = (IConduit) Class.forName(typeName).newInstance();
} catch (Exception e) {
throw new RuntimeException("Could not create an instance of the conduit with name: " + typeName, e);
}
result.readFromNBT(conduitBody, nbtVersion);
return result;
}
public static boolean isRedstoneControlModeMet(IConduitBundle bundle, RedstoneControlMode mode, DyeColor col) {
if(mode == RedstoneControlMode.IGNORE) {
return true;
} else if(mode == RedstoneControlMode.NEVER) {
return false;
} else if(mode == null) {
return false;
}
int signalStrength = getInternalSignalForColor(bundle, col);
if(signalStrength < 15 && DyeColor.RED == col && bundle != null && bundle.getEntity() != null) {
TileEntity te = bundle.getEntity();
signalStrength = Math.max(signalStrength, te.getWorldObj().getStrongestIndirectPower(te.xCoord, te.yCoord, te.zCoord));
}
return RedstoneControlMode.isConditionMet(mode, signalStrength);
}
public static int getInternalSignalForColor(IConduitBundle bundle, DyeColor col) {
int signalStrength = 0;
if(bundle == null) {
return 0;
}
IRedstoneConduit rsCon = bundle.getConduit(IRedstoneConduit.class);
if(rsCon != null) {
Set<Signal> signals = rsCon.getNetworkOutputs(ForgeDirection.UNKNOWN);
for (Signal sig : signals) {
if(sig.color == col) {
if(sig.strength > signalStrength) {
signalStrength = sig.strength;
}
}
}
}
return signalStrength;
}
public static boolean isFluidValid(FluidStack fluidStack) {
if(fluidStack != null) {
String name = FluidRegistry.getFluidName(fluidStack);
if(name != null && !name.trim().isEmpty()) {
return true;
}
}
return false;
}
public static void openConduitGui(World world, int x, int y, int z, EntityPlayer player) {
TileEntity te = world.getTileEntity(x, y, z);
if(! (te instanceof TileConduitBundle) ) {
return;
}
IConduitBundle cb = (IConduitBundle) te;
Set<ForgeDirection> cons = new HashSet<ForgeDirection>();
boolean hasInsulated = false;
for (IConduit con : cb.getConduits()) {
cons.addAll(con.getExternalConnections());
if(con instanceof IInsulatedRedstoneConduit) {
hasInsulated = true;
}
}
if(cons.isEmpty() && !hasInsulated) {
return;
}
if(cons.size() == 1) {
player.openGui(EnderIO.instance, GuiHandler.GUI_ID_EXTERNAL_CONNECTION_BASE + cons.iterator().next().ordinal(), world, x, y, z);
return;
}
player.openGui(EnderIO.instance, GuiHandler.GUI_ID_EXTERNAL_CONNECTION_SELECTOR, world, x, y, z);
}
public static void playBreakSound(SoundType snd, World world, int x, int y, int z) {
if (!world.isRemote) {
world.playSoundEffect(x + 0.5, y + 0.5, z + 0.5, snd.getBreakSound(), (snd.getVolume() + 1.0F) / 2.0F, snd.getPitch() * 0.8F);
} else {
playClientBreakSound(snd);
}
}
private static void playClientBreakSound(SoundType snd) {
FMLClientHandler.instance().getClientPlayerEntity().playSound(snd.getBreakSound(), (snd.getVolume() + 1.0F) / 2.0F, snd.getPitch() * 0.8F);
}
public static void playHitSound(SoundType snd, World world, int x, int y, int z) {
if (!world.isRemote) {
world.playSoundEffect(x + 0.5, y + 0.5, z + 0.5, snd.getStepResourcePath(), (snd.getVolume() + 1.0F) / 2.0F, snd.getPitch() * 0.8F);
} else {
playClientHitSound(snd);
}
}
private static void playClientHitSound(SoundType snd) {
FMLClientHandler.instance().getClientPlayerEntity().playSound(snd.getStepResourcePath(), (snd.getVolume() + 1.0F) / 8.0F, snd.getPitch() * 0.5F);
}
public static void playStepSound(SoundType snd, World world, int x, int y, int z) {
if (!world.isRemote) {
world.playSoundEffect(x + 0.5, y + 0.5, z + 0.5, snd.getStepResourcePath(), (snd.getVolume() + 1.0F) / 2.0F, snd.getPitch() * 0.8F);
} else {
playClientStepSound(snd);
}
}
private static void playClientStepSound(SoundType snd) {
FMLClientHandler.instance().getClientPlayerEntity().playSound(snd.getStepResourcePath(), (snd.getVolume() + 1.0F) / 8.0F, snd.getPitch());
}
public static void playPlaceSound(SoundType snd, World world, int x, int y, int z) {
if (!world.isRemote) {
world.playSoundEffect(x + 0.5, y + 0.5, z + 0.5, snd.func_150496_b(), (snd.getVolume() + 1.0F) / 2.0F, snd.getPitch() * 0.8F);
} else {
playClientPlaceSound(snd);
}
}
private static void playClientPlaceSound(SoundType snd) {
FMLClientHandler.instance().getClientPlayerEntity().playSound(snd.func_150496_b(), (snd.getVolume() + 1.0F) / 8.0F, snd.getPitch());
}
}