package pneumaticCraft.common.tileentity;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.AxisAlignedBB;
import org.lwjgl.opengl.GL11;
import pneumaticCraft.client.render.RenderProgressingLine;
import pneumaticCraft.common.block.Blockss;
import pneumaticCraft.common.item.ItemMachineUpgrade;
import pneumaticCraft.common.item.ItemNetworkComponents;
import pneumaticCraft.common.network.NetworkHandler;
import pneumaticCraft.common.network.PacketRenderRangeLines;
import pneumaticCraft.lib.Log;
import pneumaticCraft.lib.TileEntityConstants;
import com.mojang.authlib.GameProfile;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class TileEntitySecurityStation extends TileEntityBase implements ISidedInventory, IGUITextFieldSensitive,
IRangeLineShower, IRedstoneControl{
private ItemStack[] inventory;
private final int INVENTORY_SIZE = 39;
public static final int UPGRADE_SLOT_START = 35;
public static final int UPGRADE_SLOT_END = 38;
public final List<GameProfile> hackedUsers = new ArrayList<GameProfile>(); //Stores all the users that have hacked this Security Station.
public final List<GameProfile> sharedUsers = new ArrayList<GameProfile>(); //Stores all the users that have been allowed by the stationOwner.
private int rebootTimer;//When the player decides to reset the station, this variable will hold the remaining reboot time.
public String textFieldText = "";
private int securityRange;
private int oldSecurityRange; //range used by the range line renderer, to figure out if the range has been changed.
private List<RenderProgressingLine> rangeLines;
private int rangeLinesTimer = 0;
public int redstoneMode;
public boolean oldRedstoneStatus;
private boolean validNetwork;
public TileEntitySecurityStation(){
inventory = new ItemStack[INVENTORY_SIZE];
setUpgradeSlots(new int[]{UPGRADE_SLOT_START, 36, 37, UPGRADE_SLOT_END});
}
@Override
public void updateEntity(){
if(rebootTimer > 0) {
rebootTimer--;
if(!worldObj.isRemote) {
if(rebootTimer == 0) {
hackedUsers.clear();
}
if(rebootTimer % 60 == 0) {
sendDescriptionPacket();
}
}
}
if(worldObj.isRemote && rangeLines != null) {
if(rangeLinesTimer > 0) {
rangeLinesTimer--;
for(RenderProgressingLine line : rangeLines) {
if(line.getProgress() > 0.005F || worldObj.rand.nextInt(60) == 0) {
line.incProgress(0.01F);
}
}
} else {
Iterator<RenderProgressingLine> iterator = rangeLines.iterator();
while(iterator.hasNext()) {
RenderProgressingLine line = iterator.next();
if(line.getProgress() > 0.005F) {
line.incProgress(0.01F);
}
if(worldObj.rand.nextInt(10) == 0) {
iterator.remove();
}
}
}
}
if(/* !worldObj.isRemote && */oldRedstoneStatus != shouldEmitRedstone()) {
oldRedstoneStatus = shouldEmitRedstone();
updateNeighbours();
}
securityRange = Math.min(2 + getUpgrades(ItemMachineUpgrade.UPGRADE_RANGE, getUpgradeSlots()), TileEntityConstants.SECURITY_STATION_MAX_RANGE);
super.updateEntity();
}
public void rebootStation(){
rebootTimer = TileEntityConstants.SECURITY_STATION_REBOOT_TIME;
}
public int getRebootTime(){
return rebootTimer;
}
/**
* Will initiate the wireframe rendering. When invoked on the server, it sends a packet to every client to render the box.
*/
@Override
public void showRangeLines(){
if(worldObj.isRemote) {
rangeLinesTimer = 200;
} else {
NetworkHandler.sendToAllAround(new PacketRenderRangeLines(this), worldObj, TileEntityConstants.PACKET_UPDATE_DISTANCE + getSecurityRange());
}
}
@SideOnly(Side.CLIENT)
public void renderRangeLines(){
if(oldSecurityRange != getSecurityRange() || oldSecurityRange == 0) {
showRangeLines();
oldSecurityRange = getSecurityRange();
rangeLines = new ArrayList<RenderProgressingLine>();
double renderSecurityRange = securityRange + 0.5D;
for(int i = 0; i < securityRange * 16 + 8; i++) {
//Add the vertical lines of the walls
rangeLines.add(new RenderProgressingLine(-renderSecurityRange + i / 8D, -renderSecurityRange + 1, -renderSecurityRange, -renderSecurityRange + i / 8D, renderSecurityRange + 1, -renderSecurityRange));
rangeLines.add(new RenderProgressingLine(renderSecurityRange - i / 8D, -renderSecurityRange + 1, renderSecurityRange, renderSecurityRange - i / 8D, renderSecurityRange + 1, renderSecurityRange));
rangeLines.add(new RenderProgressingLine(-renderSecurityRange, -renderSecurityRange + 1, renderSecurityRange - i / 8D, -renderSecurityRange, renderSecurityRange + 1, renderSecurityRange - i / 8D));
rangeLines.add(new RenderProgressingLine(renderSecurityRange, -renderSecurityRange + 1, -renderSecurityRange + i / 8D, renderSecurityRange, renderSecurityRange + 1, -renderSecurityRange + i / 8D));
//Add the horizontal lines of the walls
rangeLines.add(new RenderProgressingLine(-renderSecurityRange, -renderSecurityRange + i / 8D + 1, -renderSecurityRange, -renderSecurityRange, -renderSecurityRange + i / 8D + 1, renderSecurityRange));
rangeLines.add(new RenderProgressingLine(renderSecurityRange, -renderSecurityRange + i / 8D + 1, -renderSecurityRange, renderSecurityRange, -renderSecurityRange + i / 8D + 1, renderSecurityRange));
rangeLines.add(new RenderProgressingLine(-renderSecurityRange, renderSecurityRange - i / 8D + 1, -renderSecurityRange, renderSecurityRange, renderSecurityRange - i / 8D + 1, -renderSecurityRange));
rangeLines.add(new RenderProgressingLine(-renderSecurityRange, -renderSecurityRange + i / 8D + 1, renderSecurityRange, renderSecurityRange, -renderSecurityRange + i / 8D + 1, renderSecurityRange));
//Add the roof and floor
rangeLines.add(new RenderProgressingLine(renderSecurityRange - i / 8D, -renderSecurityRange + 1, -renderSecurityRange, renderSecurityRange - i / 8D, -renderSecurityRange + 1, renderSecurityRange));
rangeLines.add(new RenderProgressingLine(renderSecurityRange - i / 8D, renderSecurityRange + 1, -renderSecurityRange, renderSecurityRange - i / 8D, renderSecurityRange + 1, renderSecurityRange));
rangeLines.add(new RenderProgressingLine(-renderSecurityRange, -renderSecurityRange + 1, -renderSecurityRange + i / 8D, renderSecurityRange, -renderSecurityRange + 1, -renderSecurityRange + i / 8D));
rangeLines.add(new RenderProgressingLine(-renderSecurityRange, renderSecurityRange + 1, -renderSecurityRange + i / 8D, renderSecurityRange, renderSecurityRange + 1, -renderSecurityRange + i / 8D));
}
}
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glColor4d(1, 0, 0, 0.2D);
GL11.glLineWidth(1.0F);
for(RenderProgressingLine line : rangeLines) {
line.render();
}
GL11.glColor4d(1, 1, 1, 1);
GL11.glDisable(GL11.GL_BLEND);
GL11.glEnable(GL11.GL_TEXTURE_2D);
}
@Override
public void handleGUIButtonPress(int buttonID, EntityPlayer player){
if(buttonID == 0) {
redstoneMode++;
if(redstoneMode > 2) redstoneMode = 0;
updateNeighbours();
} else if(buttonID == 2) {
rebootStation();
} else if(buttonID > 2 && buttonID - 3 < sharedUsers.size()) {
sharedUsers.remove(buttonID - 3);
}
sendDescriptionPacket();
}
public void addSharedUser(GameProfile user){
for(GameProfile sharedUser : sharedUsers) {
if(gameProfileEquals(sharedUser, user)) return;
}
sharedUsers.add(user);
}
public void addHacker(GameProfile user){
for(GameProfile hackedUser : hackedUsers) {
if(gameProfileEquals(hackedUser, user)) {
return;
}
}
hackedUsers.add(user);
}
private boolean gameProfileEquals(GameProfile profile1, GameProfile profile2){
return profile1.getId() != null && profile2.getId() != null ? profile1.getId().equals(profile2.getId()) : profile1.getName().equals(profile2.getName());
}
public boolean shouldEmitRedstone(){
switch(redstoneMode){
case 0:
return false;
case 1:
return isHacked();
case 2:
return getRebootTime() <= 0;
}
return false;
}
public boolean isHacked(){
return hackedUsers.size() > 0;
}
@Override
@SideOnly(Side.CLIENT)
public AxisAlignedBB getRenderBoundingBox(){
if(rangeLines == null || rangeLines.size() == 0) return super.getRenderBoundingBox();
int range = getSecurityRange();
return AxisAlignedBB.getBoundingBox(xCoord - range, yCoord - range, zCoord - range, xCoord + 1 + range, yCoord + 1 + range, zCoord + 1 + range);
}
public int getSecurityRange(){
return securityRange;
}
/**
* Returns the number of slots in the inventory.
*/
@Override
public int getSizeInventory(){
return inventory.length;
}
/**
* Returns the stack in slot i
*/
@Override
public ItemStack getStackInSlot(int slot){
return inventory[slot];
}
@Override
public ItemStack decrStackSize(int slot, int amount){
ItemStack itemStack = getStackInSlot(slot);
if(itemStack != null) {
if(itemStack.stackSize <= amount) {
setInventorySlotContents(slot, null);
} else {
itemStack = itemStack.splitStack(amount);
if(itemStack.stackSize == 0) {
setInventorySlotContents(slot, null);
}
}
}
return itemStack;
}
@Override
public ItemStack getStackInSlotOnClosing(int slot){
ItemStack itemStack = getStackInSlot(slot);
if(itemStack != null) {
setInventorySlotContents(slot, null);
}
return itemStack;
}
@Override
public void setInventorySlotContents(int slot, ItemStack itemStack){
inventory[slot] = itemStack;
if(itemStack != null && itemStack.stackSize > getInventoryStackLimit()) {
itemStack.stackSize = getInventoryStackLimit();
}
checkForNetworkValidity();
}
@Override
public String getInventoryName(){
return Blockss.securityStation.getUnlocalizedName();
}
@Override
public int getInventoryStackLimit(){
return 64;
}
@Override
public void readFromNBT(NBTTagCompound tag){
super.readFromNBT(tag);
redstoneMode = tag.getInteger("redstoneMode");
rebootTimer = tag.getInteger("startupTimer");
// Read in the ItemStacks in the inventory from NBT
NBTTagList tagList = tag.getTagList("Items", 10);
inventory = new ItemStack[getSizeInventory()];
for(int i = 0; i < tagList.tagCount(); ++i) {
NBTTagCompound tagCompound = tagList.getCompoundTagAt(i);
byte slot = tagCompound.getByte("Slot");
if(slot >= 0 && slot < inventory.length) {
inventory[slot] = ItemStack.loadItemStackFromNBT(tagCompound);
}
}
sharedUsers.clear();
NBTTagList sharedList = tag.getTagList("SharedUsers", 10);
for(int i = 0; i < sharedList.tagCount(); ++i) {
NBTTagCompound tagCompound = sharedList.getCompoundTagAt(i);
sharedUsers.add(new GameProfile(tagCompound.hasKey("uuid") ? UUID.fromString(tagCompound.getString("uuid")) : null, tagCompound.getString("name")));
}
hackedUsers.clear();
NBTTagList hackedList = tag.getTagList("HackedUsers", 10);
for(int i = 0; i < hackedList.tagCount(); ++i) {
NBTTagCompound tagCompound = hackedList.getCompoundTagAt(i);
hackedUsers.add(new GameProfile(tagCompound.hasKey("uuid") ? UUID.fromString(tagCompound.getString("uuid")) : null, tagCompound.getString("name")));
}
checkForNetworkValidity();
}
@Override
public void writeToNBT(NBTTagCompound tag){
super.writeToNBT(tag);
tag.setInteger("redstoneMode", redstoneMode);
tag.setInteger("startupTimer", rebootTimer);
// Write the ItemStacks in the inventory to NBT
NBTTagList tagList = new NBTTagList();
for(int currentIndex = 0; currentIndex < inventory.length; ++currentIndex) {
if(inventory[currentIndex] != null) {
NBTTagCompound tagCompound = new NBTTagCompound();
tagCompound.setByte("Slot", (byte)currentIndex);
inventory[currentIndex].writeToNBT(tagCompound);
tagList.appendTag(tagCompound);
}
}
tag.setTag("Items", tagList);
NBTTagList sharedList = new NBTTagList();
for(int i = 0; i < sharedUsers.size(); i++) {
NBTTagCompound tagCompound = new NBTTagCompound();
tagCompound.setString("name", sharedUsers.get(i).getName());
if(sharedUsers.get(i).getId() != null) tagCompound.setString("uuid", sharedUsers.get(i).getId().toString());
sharedList.appendTag(tagCompound);
}
tag.setTag("SharedUsers", sharedList);
NBTTagList hackedList = new NBTTagList();
for(int i = 0; i < hackedUsers.size(); i++) {
NBTTagCompound tagCompound = new NBTTagCompound();
tagCompound.setString("name", hackedUsers.get(i).getName());
if(hackedUsers.get(i).getId() != null) tagCompound.setString("uuid", hackedUsers.get(i).getId().toString());
hackedList.appendTag(tagCompound);
}
tag.setTag("HackedUsers", hackedList);
}
@Override
public boolean isItemValidForSlot(int i, ItemStack itemstack){
return false;
}
@Override
// upgrades in bottom, fuel in the rest.
public int[] getAccessibleSlotsFromSide(int var1){
return new int[0];
}
@Override
public boolean canInsertItem(int i, ItemStack itemstack, int j){
return false;
}
@Override
public boolean canExtractItem(int i, ItemStack itemstack, int j){
return false;
}
@Override
public void setText(int textFieldID, String text){
textFieldText = text;
}
@Override
public String getText(int textFieldID){
return textFieldText;
}
/**
* Returns true if the given player is allowed to interact with the covered area of this Security Station.
* @param player
* @return
*/
public boolean doesAllowPlayer(EntityPlayer player){
return rebootTimer > 0 || isPlayerOnWhiteList(player) || hasPlayerHacked(player);
}
public boolean isPlayerOnWhiteList(EntityPlayer player){
for(int i = 0; i < sharedUsers.size(); i++) {
GameProfile user = sharedUsers.get(i);
if(gameProfileEquals(user, player.getGameProfile())) {
if(user.getId() == null && player.getGameProfile().getId() != null) {
sharedUsers.set(i, player.getGameProfile());
Log.info("Legacy conversion: Security Station shared username '" + player.getCommandSenderName() + "' is now using UUID '" + player.getGameProfile().getId() + "'.");
}
return true;
}
}
return false;
}
public boolean hasPlayerHacked(EntityPlayer player){
for(int i = 0; i < hackedUsers.size(); i++) {
GameProfile user = hackedUsers.get(i);
if(gameProfileEquals(user, player.getGameProfile())) {
if(user.getId() == null && player.getGameProfile().getId() != null) {
hackedUsers.set(i, player.getGameProfile());
Log.info("Legacy conversion: Security Station hacked username '" + player.getCommandSenderName() + "' is now using UUID '" + player.getGameProfile().getId() + "'.");
}
return true;
}
}
return false;
}
/**
* Returns true if the given slots are connected in the network. For this to be true both slots need to have a network component stored as well.
* @param firstSlot
* @param secondSlot
* @return
*/
public boolean connects(int firstSlot, int secondSlot){
if(firstSlot < 0 || secondSlot < 0 || firstSlot >= 35 || secondSlot >= 35 || firstSlot == secondSlot || getStackInSlot(firstSlot) == null || getStackInSlot(secondSlot) == null) return false;
for(int column = -1; column <= 1; column++) {
for(int row = -1; row <= 1; row++) {
if(firstSlot + row * 5 + column == secondSlot) {
if(firstSlot % 5 > 0 && firstSlot % 5 < 4 || secondSlot % 5 > 0 && secondSlot % 5 < 4 || secondSlot % 5 == firstSlot % 5) return true;
}
}
}
return false;
}
public boolean hasValidNetwork(){
return validNetwork;
}
public enum EnumNetworkValidityProblem{
NONE, NO_SUBROUTINE, NO_IO_PORT, NO_REGISTRY, TOO_MANY_SUBROUTINES, TOO_MANY_IO_PORTS, TOO_MANY_REGISTRIES, NO_CONNECTION_SUB_AND_IO_PORT, NO_CONNECTION_IO_PORT_AND_REGISTRY
}
/**
* Method used to update the check of the validity of the network.
* @return optional problem enum
*/
public EnumNetworkValidityProblem checkForNetworkValidity(){
validNetwork = false;
int ioPortSlot = -1;
int registrySlot = -1;
int subroutineSlot = -1;
for(int i = 0; i < 35; i++) {
if(getStackInSlot(i) != null) {
switch(getStackInSlot(i).getItemDamage()){
case ItemNetworkComponents.DIAGNOSTIC_SUBROUTINE:
if(subroutineSlot != -1) return EnumNetworkValidityProblem.TOO_MANY_SUBROUTINES;//only one subroutine per network
subroutineSlot = i;
break;
case ItemNetworkComponents.NETWORK_IO_PORT:
if(ioPortSlot != -1) return EnumNetworkValidityProblem.TOO_MANY_IO_PORTS;//only one subroutine per network
ioPortSlot = i;
break;
case ItemNetworkComponents.NETWORK_REGISTRY:
if(registrySlot != -1) return EnumNetworkValidityProblem.TOO_MANY_REGISTRIES;//only one subroutine per network
registrySlot = i;
break;
}
}
}
if(subroutineSlot == -1) return EnumNetworkValidityProblem.NO_SUBROUTINE;
if(ioPortSlot == -1) return EnumNetworkValidityProblem.NO_IO_PORT;
if(registrySlot == -1) return EnumNetworkValidityProblem.NO_REGISTRY;
if(!traceComponent(subroutineSlot, ioPortSlot, new boolean[35])) return EnumNetworkValidityProblem.NO_CONNECTION_SUB_AND_IO_PORT;//check if there's a valid route between the subroutine/ioPort
if(!traceComponent(ioPortSlot, registrySlot, new boolean[35])) return EnumNetworkValidityProblem.NO_CONNECTION_IO_PORT_AND_REGISTRY; // and ioPort/registry.
validNetwork = true;
return EnumNetworkValidityProblem.NONE;
}
private boolean traceComponent(int startSlot, int targetSlot, boolean[] slotsDone){
for(int i = 0; i < 35; i++) {
if(!slotsDone[i] && connects(startSlot, i)) {
if(i == targetSlot) return true;
slotsDone[i] = true;
if(traceComponent(i, targetSlot, slotsDone)) return true;
}
}
return false;
}
public int getDetectionChance(){
return Math.min(100, 20 + 20 * getUpgrades(ItemMachineUpgrade.UPGRADE_ENTITY_TRACKER, getUpgradeSlots()));
}
public int getSecurityLevel(){
return 1 + getUpgrades(ItemMachineUpgrade.UPGRADE_SECURITY, getUpgradeSlots());
}
@Override
public boolean hasCustomInventoryName(){
return false;
}
@Override
public boolean isUseableByPlayer(EntityPlayer var1){
return isGuiUseableByPlayer(var1);
}
@Override
public boolean isGuiUseableByPlayer(EntityPlayer par1EntityPlayer){
return worldObj.getTileEntity(xCoord, yCoord, zCoord) == this;
}
@Override
public void openInventory(){}
@Override
public void closeInventory(){}
@Override
public int getRedstoneMode(){
return redstoneMode;
}
}