package blusunrize.immersiveengineering.common.blocks.metal;
import blusunrize.immersiveengineering.ImmersiveEngineering;
import blusunrize.immersiveengineering.api.IEProperties;
import blusunrize.immersiveengineering.api.IEProperties.PropertyBoolInverted;
import blusunrize.immersiveengineering.api.Lib;
import blusunrize.immersiveengineering.api.TargetingInfo;
import blusunrize.immersiveengineering.api.energy.wires.IImmersiveConnectable;
import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler;
import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection;
import blusunrize.immersiveengineering.api.energy.wires.TileEntityImmersiveConnectable;
import blusunrize.immersiveengineering.api.energy.wires.WireType;
import blusunrize.immersiveengineering.client.models.IOBJModelCallback;
import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.*;
import blusunrize.immersiveengineering.common.util.ChatUtils;
import blusunrize.immersiveengineering.common.util.IESounds;
import blusunrize.immersiveengineering.common.util.Utils;
import blusunrize.immersiveengineering.common.util.chickenbones.Matrix4;
import com.google.common.base.Optional;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumFacing.Axis;
import net.minecraft.util.EnumHand;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraftforge.common.model.TRSRTransformation;
import java.util.Set;
public class TileEntityBreakerSwitch extends TileEntityImmersiveConnectable implements IBlockBounds, IAdvancedDirectionalTile, IActiveState, IHammerInteraction, IPlayerInteraction, IRedstoneOutput, IOBJModelCallback<IBlockState>
{
public int rotation=0;
public EnumFacing facing=EnumFacing.NORTH;
public int wires = 0;
public boolean active=false;
public boolean inverted=false;
public BlockPos endOfLeftConnection = null;
@Override
protected boolean canTakeLV()
{
return true;
}
@Override
protected boolean canTakeMV()
{
return true;
}
@Override
protected boolean canTakeHV()
{
return false;
}
@Override
public boolean allowEnergyToPass(Connection con)
{
return active;
}
@Override
public boolean canConnectCable(WireType cableType, TargetingInfo target)
{
if(cableType!=null && !cableType.isEnergyWire())
return false;
if(cableType==WireType.STEEL&&!canTakeHV())
return false;
if(wires>=2)
return false;
return limitType==null || cableType==limitType;
}
@Override
public void connectCable(WireType cableType, TargetingInfo target, IImmersiveConnectable other)
{
if(this.limitType==null)
this.limitType = cableType;
wires++;
onConnectionChange();
}
@Override
public WireType getCableLimiter(TargetingInfo target)
{
return limitType;
}
@Override
public void removeCable(Connection connection)
{
WireType type = connection!=null?connection.cableType:null;
if(type==null)
wires = 0;
else
wires--;
if(wires<=0)
limitType=null;
onConnectionChange();
}
@Override
public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket)
{
super.writeCustomNBT(nbt, descPacket);
nbt.setInteger("facing", facing.ordinal());
nbt.setInteger("rotation", rotation);
nbt.setInteger("wires", wires);
nbt.setBoolean("active", active);
nbt.setBoolean("inverted", inverted);
}
@Override
public void readCustomNBT(NBTTagCompound nbt, boolean descPacket)
{
super.readCustomNBT(nbt, descPacket);
facing = EnumFacing.getFront(nbt.getInteger("facing"));
rotation = nbt.getInteger("rotation");
wires = nbt.getInteger("wires");
active = nbt.getBoolean("active");
inverted = nbt.getBoolean("inverted");
onConnectionChange();
}
@Override
public Vec3d getRaytraceOffset(IImmersiveConnectable link)
{
return new Vec3d(.5,.5,.5);
}
@Override
public Vec3d getConnectionOffset(Connection con)
{
Matrix4 mat = new Matrix4(facing);
mat.translate(.5, .5, 0).rotate(Math.PI/2*rotation, 0, 0, 1).translate(-.5, -.5, 0);
if (endOfLeftConnection==null)
calculateLeftConn(mat);
boolean isLeft = con.end.equals(endOfLeftConnection)||con.start.equals(endOfLeftConnection);
Vec3d ret = mat.apply(new Vec3d(isLeft?.25:.75, .5, .125));
return ret;
}
protected void calculateLeftConn(Matrix4 transform)
{
Vec3d leftVec = transform.apply(new Vec3d(-1, .5, .5)).subtract(0, .5, .5);
EnumFacing dir = EnumFacing.getFacingFromVector((float) leftVec.xCoord, (float) leftVec.yCoord, (float) leftVec.zCoord);
int maxDiff = Integer.MIN_VALUE;
Set<Connection> conns = ImmersiveNetHandler.INSTANCE.getConnections(worldObj, pos);
if (conns!=null)
for (Connection c:conns)
{
Vec3i diff = pos.equals(c.start)?c.end.subtract(pos):c.start.subtract(pos);
int val = 0;
switch (dir.getAxis())
{
case X:
val = diff.getX();
break;
case Y:
val = diff.getY();
break;
case Z:
val = diff.getZ();
}
val *= dir.getAxisDirection().getOffset();
if (val>maxDiff)
{
maxDiff = val;
endOfLeftConnection = pos==c.end?c.start:c.end;
}
}
}
@Override
public boolean hammerUseSide(EnumFacing side, EntityPlayer player, float hitX, float hitY, float hitZ)
{
if (player.isSneaking())
{
inverted = !inverted;
ChatUtils.sendServerNoSpamMessages(player, new TextComponentTranslation(Lib.CHAT_INFO+"rsSignal."+(inverted?"invertedOn":"invertedOff")));
if (this instanceof TileEntityBreakerSwitch && wires>1)
ImmersiveNetHandler.INSTANCE.resetCachedIndirectConnections();
notifyNeighbours();
}
else
{
rotation = (rotation+3)%4;
onConnectionChange();
}
return true;
}
@Override
public boolean interact(EnumFacing side, EntityPlayer player, EnumHand hand, ItemStack heldItem, float hitX, float hitY, float hitZ)
{
if (!Utils.isHammer(heldItem))
{
active = !active;
worldObj.playSound(getPos().getX(),getPos().getY(),getPos().getZ(), IESounds.direSwitch, SoundCategory.BLOCKS, 2.5F,1, true);
if (wires>1)
ImmersiveNetHandler.INSTANCE.resetCachedIndirectConnections();
worldObj.addBlockEvent(getPos(), getBlockType(), active?1:0, 0);
notifyNeighbours();
}
return true;
}
public void notifyNeighbours()
{
markDirty();
worldObj.notifyNeighborsOfStateChange(getPos(), getBlockType());
for(EnumFacing f : EnumFacing.VALUES)
worldObj.notifyNeighborsOfStateChange(getPos().offset(f), getBlockType());
}
@Override
public boolean receiveClientEvent(int id, int arg)
{
if(super.receiveClientEvent(id, arg))
return true;
this.active = id==1;
this.markContainingBlockForUpdate(null);
return true;
}
@Override
public PropertyBoolInverted getBoolProperty(Class<? extends IUsesBooleanProperty> inf)
{
return IEProperties.BOOLEANS[0];
}
@Override
public boolean getIsActive()
{
return active;
}
@Override
public EnumFacing getFacing()
{
return facing;
}
@Override
public void setFacing(EnumFacing facing)
{
this.facing = facing;
}
@Override
public int getFacingLimitation()
{
return 0;
}
@Override
public boolean mirrorFacingOnPlacement(EntityLivingBase placer)
{
return true;
}
@Override
public boolean canHammerRotate(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase entity)
{
return false;
}
@Override
public boolean canRotate(EnumFacing axis)
{
return false;
}
@Override
public float[] getBlockBounds()
{
Vec3d start = new Vec3d(.25,.1875,0);
Vec3d end = new Vec3d(.75,.8125,.5);
Matrix4 mat = new Matrix4(facing);
mat.translate(.5, .5, 0).rotate(Math.PI/2*rotation, 0, 0, 1).translate(-.5, -.5, 0);
start = mat.apply(start);
end = mat.apply(end);
return new float[]{(float) start.xCoord, (float) start.yCoord, (float) start.zCoord,
(float) end.xCoord, (float) end.yCoord, (float) end.zCoord};
}
@Override
public int getWeakRSOutput(IBlockState state, EnumFacing side)
{
return (active^inverted)?15:0;
}
@Override
public int getStrongRSOutput(IBlockState state, EnumFacing side)
{
return side.getOpposite()==facing && (active^inverted)?15:0;
}
@Override
public boolean canConnectRedstone(IBlockState state, EnumFacing side)
{
return true;
}
@Override
public TextureAtlasSprite getTextureReplacement(IBlockState object, String material)
{
return null;
}
@Override
public boolean shouldRenderGroup(IBlockState object, String group)
{
return true;
}
@Override
public Optional<TRSRTransformation> applyTransformations(IBlockState object, String group, Optional<TRSRTransformation> transform)
{
Matrix4 mat = transform.isPresent()?new Matrix4(transform.get().getMatrix()):new Matrix4();
mat = mat.translate(.5,0,.5).rotate(Math.PI/2*rotation,0,1,0).translate(-.5,0,-.5);
transform = Optional.of(new TRSRTransformation(mat.toMatrix4f()));
return transform;
}
@Override
public Matrix4 handlePerspective(IBlockState Object, TransformType cameraTransformType, Matrix4 perspective)
{
return perspective;
}
@Override
public String getCacheKey(IBlockState object)
{
return rotation+","+facing.getIndex()+","+active;
}
@Override
public void onDirectionalPlacement(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase placer)
{
EnumFacing f = EnumFacing.SOUTH;
if(side.getAxis() == Axis.Y)
{
float xFromMid = hitX - .5f;
float zFromMid = hitZ - .5f;
float max = Math.max(Math.abs(xFromMid), Math.abs(zFromMid));
if(max == Math.abs(xFromMid))
f = xFromMid < 0 ? EnumFacing.WEST : EnumFacing.EAST;
else
f = zFromMid < 0 ? EnumFacing.NORTH : EnumFacing.SOUTH;
if ((side==EnumFacing.UP&&f.getAxis()==Axis.Z)||side==EnumFacing.DOWN)
f = f.getOpposite();
}
rotation = f.getHorizontalIndex();
}
protected void onConnectionChange()
{
if (worldObj!=null&&worldObj.isRemote)
{
endOfLeftConnection = null;
ImmersiveEngineering.proxy.clearConnectionModelCache();
// reset cached connection vertices
Set<Connection> conns = ImmersiveNetHandler.INSTANCE.getConnections(worldObj, pos);
if (conns!=null)
for (Connection c:conns)
{
c.catenaryVertices = null;
worldObj.markBlockRangeForRenderUpdate(c.end, c.end);
Set<Connection> connsThere = ImmersiveNetHandler.INSTANCE.getConnections(worldObj, c.end);
if (connsThere!=null)
for (Connection c2:connsThere)
if (c2.end.equals(pos))
c2.catenaryVertices = null;
}
}
if (worldObj!=null)
markContainingBlockForUpdate(null);
}
}