appeng.parts.CableBusContainer.java Source code

Java tutorial

Introduction

Here is the source code for appeng.parts.CableBusContainer.java

Source

/*
 * This file is part of Applied Energistics 2.
 * Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
 *
 * Applied Energistics 2 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Applied Energistics 2 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Applied Energistics 2.  If not, see <http://www.gnu.org/licenses/lgpl>.
 */

package appeng.parts;

import java.io.IOException;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;

import javax.annotation.Nullable;

import io.netty.buffer.ByteBuf;

import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;

import appeng.api.AEApi;
import appeng.api.config.YesNo;
import appeng.api.exceptions.FailedConnection;
import appeng.api.implementations.parts.IPartCable;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.parts.IFacadeContainer;
import appeng.api.parts.IFacadePart;
import appeng.api.parts.IPart;
import appeng.api.parts.IPartCollisionHelper;
import appeng.api.parts.IPartHost;
import appeng.api.parts.IPartItem;
import appeng.api.parts.LayerFlags;
import appeng.api.parts.PartItemStack;
import appeng.api.parts.SelectedPart;
import appeng.api.util.AECableType;
import appeng.api.util.AEColor;
import appeng.api.util.AEPartLocation;
import appeng.api.util.DimensionalCoord;
import appeng.client.render.cablebus.CableBusRenderState;
import appeng.client.render.cablebus.CableCoreType;
import appeng.client.render.cablebus.FacadeRenderState;
import appeng.core.AELog;
import appeng.facade.FacadeContainer;
import appeng.helpers.AEMultiTile;
import appeng.me.GridConnection;
import appeng.parts.networking.PartCable;
import appeng.util.Platform;

public class CableBusContainer extends CableBusStorage implements AEMultiTile, ICableBusContainer {

    private static final ThreadLocal<Boolean> IS_LOADING = new ThreadLocal<Boolean>();
    private final EnumSet<LayerFlags> myLayerFlags = EnumSet.noneOf(LayerFlags.class);
    private YesNo hasRedstone = YesNo.UNDECIDED;
    private IPartHost tcb;
    // TODO 1.10.2-R - does somebody seriously want to make parts TESR??? Hope not.
    private boolean requiresDynamicRender = false;
    private boolean inWorld = false;

    public CableBusContainer(final IPartHost host) {
        this.tcb = host;
    }

    public static boolean isLoading() {
        final Boolean is = IS_LOADING.get();
        return is != null && is;
    }

    public void setHost(final IPartHost host) {
        this.tcb.clearContainer();
        this.tcb = host;
    }

    public void rotateLeft() {
        final IPart[] newSides = new IPart[6];

        newSides[AEPartLocation.UP.ordinal()] = this.getSide(AEPartLocation.UP);
        newSides[AEPartLocation.DOWN.ordinal()] = this.getSide(AEPartLocation.DOWN);

        newSides[AEPartLocation.EAST.ordinal()] = this.getSide(AEPartLocation.NORTH);
        newSides[AEPartLocation.SOUTH.ordinal()] = this.getSide(AEPartLocation.EAST);
        newSides[AEPartLocation.WEST.ordinal()] = this.getSide(AEPartLocation.SOUTH);
        newSides[AEPartLocation.NORTH.ordinal()] = this.getSide(AEPartLocation.WEST);

        for (final AEPartLocation dir : AEPartLocation.SIDE_LOCATIONS) {
            this.setSide(dir, newSides[dir.ordinal()]);
        }

        this.getFacadeContainer().rotateLeft();
    }

    @Override
    public IFacadeContainer getFacadeContainer() {
        return new FacadeContainer(this);
    }

    @Override
    public boolean canAddPart(ItemStack is, final AEPartLocation side) {
        if (PartPlacement.isFacade(is, side) != null) {
            return true;
        }

        if (is.getItem() instanceof IPartItem) {
            final IPartItem bi = (IPartItem) is.getItem();

            is = is.copy();
            is.stackSize = 1;

            final IPart bp = bi.createPartFromItemStack(is);
            if (bp != null) {
                if (bp instanceof IPartCable) {
                    boolean canPlace = true;
                    for (final AEPartLocation d : AEPartLocation.SIDE_LOCATIONS) {
                        if (this.getPart(d) != null
                                && !this.getPart(d).canBePlacedOn(((IPartCable) bp).supportsBuses())) {
                            canPlace = false;
                        }
                    }

                    if (!canPlace) {
                        return false;
                    }

                    return this.getPart(AEPartLocation.INTERNAL) == null;
                } else if (!(bp instanceof IPartCable) && side != AEPartLocation.INTERNAL) {
                    final IPart cable = this.getPart(AEPartLocation.INTERNAL);
                    if (cable != null && !bp.canBePlacedOn(((IPartCable) cable).supportsBuses())) {
                        return false;
                    }

                    return this.getPart(side) == null;
                }
            }
        }
        return false;
    }

    @Override
    public AEPartLocation addPart(ItemStack is, final AEPartLocation side, final @Nullable EntityPlayer player,
            final @Nullable EnumHand hand) {
        if (this.canAddPart(is, side)) {
            if (is.getItem() instanceof IPartItem) {
                final IPartItem bi = (IPartItem) is.getItem();

                is = is.copy();
                is.stackSize = 1;

                final IPart bp = bi.createPartFromItemStack(is);
                if (bp instanceof IPartCable) {
                    boolean canPlace = true;
                    for (final AEPartLocation d : AEPartLocation.SIDE_LOCATIONS) {
                        if (this.getPart(d) != null
                                && !this.getPart(d).canBePlacedOn(((IPartCable) bp).supportsBuses())) {
                            canPlace = false;
                        }
                    }

                    if (!canPlace) {
                        return null;
                    }

                    if (this.getPart(AEPartLocation.INTERNAL) != null) {
                        return null;
                    }

                    this.setCenter((IPartCable) bp);
                    bp.setPartHostInfo(AEPartLocation.INTERNAL, this, this.tcb.getTile());

                    if (player != null) {
                        bp.onPlacement(player, hand, is, side);
                    }

                    if (this.inWorld) {
                        bp.addToWorld();
                    }

                    final IGridNode cn = this.getCenter().getGridNode();
                    if (cn != null) {
                        for (final AEPartLocation ins : AEPartLocation.SIDE_LOCATIONS) {
                            final IPart sbp = this.getPart(ins);
                            if (sbp != null) {
                                final IGridNode sn = sbp.getGridNode();
                                if (sn != null) {
                                    try {
                                        new GridConnection(cn, sn, AEPartLocation.INTERNAL);
                                    } catch (final FailedConnection e) {
                                        // ekk!

                                        bp.removeFromWorld();
                                        this.setCenter(null);
                                        return null;
                                    }
                                }
                            }
                        }
                    }

                    this.updateConnections();
                    this.markForUpdate();
                    this.markForSave();
                    this.partChanged();
                    return AEPartLocation.INTERNAL;
                } else if (bp != null && !(bp instanceof IPartCable) && side != AEPartLocation.INTERNAL) {
                    final IPart cable = this.getPart(AEPartLocation.INTERNAL);
                    if (cable != null && !bp.canBePlacedOn(((IPartCable) cable).supportsBuses())) {
                        return null;
                    }

                    this.setSide(side, bp);
                    bp.setPartHostInfo(side, this, this.getTile());

                    if (player != null) {
                        bp.onPlacement(player, hand, is, side);
                    }

                    if (this.inWorld) {
                        bp.addToWorld();
                    }

                    if (this.getCenter() != null) {
                        final IGridNode cn = this.getCenter().getGridNode();
                        final IGridNode sn = bp.getGridNode();

                        if (cn != null && sn != null) {
                            try {
                                new GridConnection(cn, sn, AEPartLocation.INTERNAL);
                            } catch (final FailedConnection e) {
                                // ekk!

                                bp.removeFromWorld();
                                this.setSide(side, null);
                                return null;
                            }
                        }
                    }

                    this.updateDynamicRender();
                    this.updateConnections();
                    this.markForUpdate();
                    this.markForSave();
                    this.partChanged();
                    return side;
                }
            }
        }
        return null;
    }

    @Override
    public IPart getPart(final AEPartLocation partLocation) {
        if (partLocation == AEPartLocation.INTERNAL) {
            return this.getCenter();
        }
        return this.getSide(partLocation);
    }

    @Override
    public IPart getPart(final EnumFacing side) {
        return this.getSide(AEPartLocation.fromFacing(side));
    }

    @Override
    public void removePart(final AEPartLocation side, final boolean suppressUpdate) {
        if (side == AEPartLocation.INTERNAL) {
            if (this.getCenter() != null) {
                this.getCenter().removeFromWorld();
            }
            this.setCenter(null);
        } else {
            if (this.getSide(side) != null) {
                this.getSide(side).removeFromWorld();
            }
            this.setSide(side, null);
        }

        if (!suppressUpdate) {
            this.updateDynamicRender();
            this.updateConnections();
            this.markForUpdate();
            this.markForSave();
            this.partChanged();
        }
    }

    @Override
    public void markForUpdate() {
        this.tcb.markForUpdate();
    }

    @Override
    public DimensionalCoord getLocation() {
        return this.tcb.getLocation();
    }

    @Override
    public TileEntity getTile() {
        return this.tcb.getTile();
    }

    @Override
    public AEColor getColor() {
        if (this.getCenter() != null) {
            final IPartCable c = this.getCenter();
            return c.getCableColor();
        }
        return AEColor.TRANSPARENT;
    }

    @Override
    public void clearContainer() {
        throw new UnsupportedOperationException("Now that is silly!");
    }

    @Override
    public boolean isBlocked(final EnumFacing side) {
        return this.tcb.isBlocked(side);
    }

    @Override
    public SelectedPart selectPart(final Vec3d pos) {
        for (final AEPartLocation side : AEPartLocation.values()) {
            final IPart p = this.getPart(side);
            if (p != null) {
                final List<AxisAlignedBB> boxes = new LinkedList<AxisAlignedBB>();

                final IPartCollisionHelper bch = new BusCollisionHelper(boxes, side, null, true);
                p.getBoxes(bch);
                for (AxisAlignedBB bb : boxes) {
                    bb = bb.expand(0.002, 0.002, 0.002);
                    if (bb.isVecInside(pos)) {
                        return new SelectedPart(p, side);
                    }
                }
            }
        }

        if (AEApi.instance().partHelper().getCableRenderMode().opaqueFacades) {
            final IFacadeContainer fc = this.getFacadeContainer();
            for (final AEPartLocation side : AEPartLocation.SIDE_LOCATIONS) {
                final IFacadePart p = fc.getFacade(side);
                if (p != null) {
                    final List<AxisAlignedBB> boxes = new LinkedList<AxisAlignedBB>();

                    final IPartCollisionHelper bch = new BusCollisionHelper(boxes, side, null, true);
                    p.getBoxes(bch, null);
                    for (AxisAlignedBB bb : boxes) {
                        bb = bb.expand(0.01, 0.01, 0.01);
                        if (bb.isVecInside(pos)) {
                            return new SelectedPart(p, side);
                        }
                    }
                }
            }
        }

        return new SelectedPart();
    }

    @Override
    public void markForSave() {
        this.tcb.markForSave();
    }

    @Override
    public void partChanged() {
        if (this.getCenter() == null) {
            final List<ItemStack> facades = new LinkedList<ItemStack>();

            final IFacadeContainer fc = this.getFacadeContainer();
            for (final AEPartLocation d : AEPartLocation.SIDE_LOCATIONS) {
                final IFacadePart fp = fc.getFacade(d);
                if (fp != null) {
                    facades.add(fp.getItemStack());
                    fc.removeFacade(this.tcb, d);
                }
            }

            if (!facades.isEmpty()) {
                final TileEntity te = this.tcb.getTile();
                Platform.spawnDrops(te.getWorld(), te.getPos(), facades);
            }
        }

        this.tcb.partChanged();
    }

    @Override
    public boolean hasRedstone(final AEPartLocation side) {
        if (this.hasRedstone == YesNo.UNDECIDED) {
            this.updateRedstone();
        }

        return this.hasRedstone == YesNo.YES;
    }

    @Override
    public boolean isEmpty() {
        final IFacadeContainer fc = this.getFacadeContainer();
        for (final AEPartLocation s : AEPartLocation.values()) {
            final IPart part = this.getPart(s);
            if (part != null) {
                return false;
            }

            if (s != AEPartLocation.INTERNAL) {
                final IFacadePart fp = fc.getFacade(s);
                if (fp != null) {
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public Set<LayerFlags> getLayerFlags() {
        return this.myLayerFlags;
    }

    @Override
    public void cleanup() {
        this.tcb.cleanup();
    }

    @Override
    public void notifyNeighbors() {
        this.tcb.notifyNeighbors();
    }

    @Override
    public boolean isInWorld() {
        return this.inWorld;
    }

    private void updateRedstone() {
        final TileEntity te = this.getTile();
        this.hasRedstone = te.getWorld().isBlockIndirectlyGettingPowered(te.getPos()) != 0 ? YesNo.YES : YesNo.NO;
    }

    private void updateDynamicRender() {
        this.requiresDynamicRender = false;
        for (final AEPartLocation s : AEPartLocation.SIDE_LOCATIONS) {
            final IPart p = this.getPart(s);
            if (p != null) {
                this.setRequiresDynamicRender(this.isRequiresDynamicRender() || p.requireDynamicRender());
            }
        }
    }

    /**
     * use for FMP
     */
    public void updateConnections() {
        if (this.getCenter() != null) {
            final EnumSet<EnumFacing> sides = EnumSet.allOf(EnumFacing.class);

            for (final EnumFacing s : EnumFacing.VALUES) {
                if (this.getPart(s) != null || this.isBlocked(s)) {
                    sides.remove(s);
                }
            }

            this.getCenter().setValidSides(sides);
            final IGridNode n = this.getCenter().getGridNode();
            if (n != null) {
                n.updateState();
            }
        }
    }

    public void addToWorld() {
        if (this.inWorld) {
            return;
        }

        this.inWorld = true;
        IS_LOADING.set(true);

        final TileEntity te = this.getTile();

        // start with the center, then install the side parts into the grid.
        for (int x = 6; x >= 0; x--) {
            final AEPartLocation s = AEPartLocation.fromOrdinal(x);
            final IPart part = this.getPart(s);

            if (part != null) {
                part.setPartHostInfo(s, this, te);
                part.addToWorld();

                if (s != AEPartLocation.INTERNAL) {
                    final IGridNode sn = part.getGridNode();
                    if (sn != null) {
                        // this is a really stupid if statement, why was this
                        // here?
                        // if ( !sn.getConnections().iterator().hasNext() )

                        final IPart center = this.getPart(AEPartLocation.INTERNAL);
                        if (center != null) {
                            final IGridNode cn = center.getGridNode();
                            if (cn != null) {
                                try {
                                    AEApi.instance().createGridConnection(cn, sn);
                                } catch (final FailedConnection e) {
                                    // ekk
                                }
                            }
                        }
                    }
                }
            }
        }

        this.partChanged();

        IS_LOADING.set(false);
    }

    public void removeFromWorld() {
        if (!this.inWorld) {
            return;
        }

        this.inWorld = false;

        for (final AEPartLocation s : AEPartLocation.values()) {
            final IPart part = this.getPart(s);
            if (part != null) {
                part.removeFromWorld();
            }
        }

        this.partChanged();
    }

    @Override
    public IGridNode getGridNode(final AEPartLocation side) {
        final IPart part = this.getPart(side);
        if (part != null) {
            final IGridNode n = part.getExternalFacingNode();
            if (n != null) {
                return n;
            }
        }

        if (this.getCenter() != null) {
            return this.getCenter().getGridNode();
        }

        return null;
    }

    @Override
    public AECableType getCableConnectionType(final AEPartLocation dir) {
        final IPart part = this.getPart(dir);
        if (part instanceof IGridHost) {
            final AECableType t = ((IGridHost) part).getCableConnectionType(dir);
            if (t != null && t != AECableType.NONE) {
                return t;
            }
        }

        if (this.getCenter() != null) {
            final IPartCable c = this.getCenter();
            return c.getCableConnectionType();
        }
        return AECableType.NONE;
    }

    @Override
    public float getCableConnectionLength(AECableType cable) {
        return getPart(AEPartLocation.INTERNAL) instanceof IPartCable
                ? getPart(AEPartLocation.INTERNAL).getCableConnectionLength(cable)
                : -1;
    }

    @Override
    public void securityBreak() {
        for (final AEPartLocation d : AEPartLocation.values()) {
            final IPart p = this.getPart(d);
            if (p instanceof IGridHost) {
                ((IGridHost) p).securityBreak();
            }
        }
    }

    public Iterable<AxisAlignedBB> getSelectedBoundingBoxesFromPool(final boolean ignoreConnections,
            final boolean includeFacades, final Entity e, final boolean visual) {
        final List<AxisAlignedBB> boxes = new LinkedList<AxisAlignedBB>();

        final IFacadeContainer fc = this.getFacadeContainer();
        for (final AEPartLocation s : AEPartLocation.values()) {
            final IPartCollisionHelper bch = new BusCollisionHelper(boxes, s, e, visual);

            final IPart part = this.getPart(s);
            if (part != null) {
                if (ignoreConnections && part instanceof IPartCable) {
                    bch.addBox(6.0, 6.0, 6.0, 10.0, 10.0, 10.0);
                } else {
                    part.getBoxes(bch);
                }
            }

            if (AEApi.instance().partHelper().getCableRenderMode().opaqueFacades || !visual) {
                if (includeFacades && s != null && s != AEPartLocation.INTERNAL) {
                    final IFacadePart fp = fc.getFacade(s);
                    if (fp != null) {
                        fp.getBoxes(bch, e);
                    }
                }
            }
        }

        return boxes;
    }

    @Override
    public int isProvidingStrongPower(final EnumFacing side) {
        final IPart part = this.getPart(side);
        return part != null ? part.isProvidingStrongPower() : 0;
    }

    @Override
    public int isProvidingWeakPower(final EnumFacing side) {
        final IPart part = this.getPart(side);
        return part != null ? part.isProvidingWeakPower() : 0;
    }

    @Override
    public boolean canConnectRedstone(final EnumSet<EnumFacing> enumSet) {
        for (final EnumFacing dir : enumSet) {
            final IPart part = this.getPart(dir);
            if (part != null && part.canConnectRedstone()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void onEntityCollision(final Entity entity) {
        for (final AEPartLocation s : AEPartLocation.values()) {
            final IPart part = this.getPart(s);
            if (part != null) {
                part.onEntityCollision(entity);
            }
        }
    }

    @Override
    public boolean activate(final EntityPlayer player, final EnumHand hand, final Vec3d pos) {
        final SelectedPart p = this.selectPart(pos);
        if (p != null && p.part != null) {
            return p.part.onActivate(player, hand, pos);
        }
        return false;
    }

    @Override
    public void onNeighborChanged() {
        this.hasRedstone = YesNo.UNDECIDED;

        for (final AEPartLocation s : AEPartLocation.values()) {
            final IPart part = this.getPart(s);
            if (part != null) {
                part.onNeighborChanged();
            }
        }
    }

    @Override
    public boolean isSolidOnSide(final EnumFacing side) {
        if (side == null) {
            return false;
        }

        // facades are solid..
        final IFacadePart fp = this.getFacadeContainer().getFacade(AEPartLocation.fromFacing(side));
        if (fp != null) {
            return true;
        }

        // buses can be too.
        final IPart part = this.getPart(side);
        return part != null && part.isSolid();
    }

    @Override
    public boolean isLadder(final EntityLivingBase entity) {
        for (final AEPartLocation side : AEPartLocation.values()) {
            final IPart p = this.getPart(side);
            if (p != null) {
                if (p.isLadder(entity)) {
                    return true;
                }
            }
        }

        return false;
    }

    @Override
    public void randomDisplayTick(final World world, final BlockPos pos, final Random r) {
        for (final AEPartLocation side : AEPartLocation.values()) {
            final IPart p = this.getPart(side);
            if (p != null) {
                p.randomDisplayTick(world, pos, r);
            }
        }
    }

    @Override
    public int getLightValue() {
        int light = 0;

        for (final AEPartLocation d : AEPartLocation.values()) {
            final IPart p = this.getPart(d);
            if (p != null) {
                light = Math.max(p.getLightLevel(), light);
            }
        }

        return light;
    }

    public void writeToStream(final ByteBuf data) throws IOException {
        int sides = 0;
        for (int x = 0; x < 7; x++) {
            final IPart p = this.getPart(AEPartLocation.fromOrdinal(x));
            if (p != null) {
                sides |= (1 << x);
            }
        }

        data.writeByte((byte) sides);

        for (int x = 0; x < 7; x++) {
            final IPart p = this.getPart(AEPartLocation.fromOrdinal(x));
            if (p != null) {
                final ItemStack is = p.getItemStack(PartItemStack.NETWORK);

                data.writeShort(Item.getIdFromItem(is.getItem()));
                data.writeShort(is.getItemDamage());

                p.writeToStream(data);
            }
        }

        this.getFacadeContainer().writeToStream(data);
    }

    public boolean readFromStream(final ByteBuf data) throws IOException {
        final byte sides = data.readByte();

        boolean updateBlock = false;

        for (int x = 0; x < 7; x++) {
            AEPartLocation side = AEPartLocation.fromOrdinal(x);
            if (((sides & (1 << x)) == (1 << x))) {
                IPart p = this.getPart(side);

                final short itemID = data.readShort();
                final short dmgValue = data.readShort();

                final Item myItem = Item.getItemById(itemID);

                final ItemStack current = p != null ? p.getItemStack(PartItemStack.NETWORK) : null;
                if (current != null && current.getItem() == myItem && current.getItemDamage() == dmgValue) {
                    if (p.readFromStream(data)) {
                        updateBlock = true;
                    }
                } else {
                    this.removePart(side, false);
                    side = this.addPart(new ItemStack(myItem, 1, dmgValue), side, null, null);
                    if (side != null) {
                        p = this.getPart(side);
                        p.readFromStream(data);
                    } else {
                        throw new IllegalStateException("Invalid Stream For CableBus Container.");
                    }
                }
            } else if (this.getPart(side) != null) {
                this.removePart(side, false);
            }
        }

        if (this.getFacadeContainer().readFromStream(data)) {
            return true;
        }

        return updateBlock;
    }

    public void writeToNBT(final NBTTagCompound data) {
        data.setInteger("hasRedstone", this.hasRedstone.ordinal());

        final IFacadeContainer fc = this.getFacadeContainer();
        for (final AEPartLocation s : AEPartLocation.values()) {
            fc.writeToNBT(data);

            final IPart part = this.getPart(s);
            if (part != null) {
                final NBTTagCompound def = new NBTTagCompound();
                part.getItemStack(PartItemStack.WORLD).writeToNBT(def);

                final NBTTagCompound extra = new NBTTagCompound();
                part.writeToNBT(extra);

                data.setTag("def:" + this.getSide(part).ordinal(), def);
                data.setTag("extra:" + this.getSide(part).ordinal(), extra);
            }
        }
    }

    private AEPartLocation getSide(final IPart part) {
        if (this.getCenter() == part) {
            return AEPartLocation.INTERNAL;
        } else {
            for (final AEPartLocation side : AEPartLocation.SIDE_LOCATIONS) {
                if (this.getSide(side) == part) {
                    return side;
                }
            }
        }

        throw new IllegalStateException("Uhh Bad Part (" + part + ") on Side.");
    }

    public void readFromNBT(final NBTTagCompound data) {
        if (data.hasKey("hasRedstone")) {
            this.hasRedstone = YesNo.values()[data.getInteger("hasRedstone")];
        }

        for (int x = 0; x < 7; x++) {
            AEPartLocation side = AEPartLocation.fromOrdinal(x);

            final NBTTagCompound def = data.getCompoundTag("def:" + side.ordinal());
            final NBTTagCompound extra = data.getCompoundTag("extra:" + side.ordinal());
            if (def != null && extra != null) {
                IPart p = this.getPart(side);
                final ItemStack iss = ItemStack.loadItemStackFromNBT(def);
                if (iss == null) {
                    continue;
                }

                final ItemStack current = p == null ? null : p.getItemStack(PartItemStack.WORLD);

                if (Platform.itemComparisons().isEqualItemType(iss, current)) {
                    p.readFromNBT(extra);
                } else {
                    this.removePart(side, true);
                    side = this.addPart(iss, side, null, null);
                    if (side != null) {
                        p = this.getPart(side);
                        p.readFromNBT(extra);
                    } else {
                        AELog.warn("Invalid NBT For CableBus Container: " + iss.getItem().getClass().getName()
                                + " is not a valid part; it was ignored.");
                    }
                }
            } else {
                this.removePart(side, false);
            }
        }

        this.getFacadeContainer().readFromNBT(data);
    }

    public List getDrops(final List drops) {
        for (final AEPartLocation s : AEPartLocation.values()) {
            final IPart part = this.getPart(s);
            if (part != null) {
                drops.add(part.getItemStack(PartItemStack.BREAK));
                part.getDrops(drops, false);
            }

            if (s != AEPartLocation.INTERNAL) {
                final IFacadePart fp = this.getFacadeContainer().getFacade(s);
                if (fp != null) {
                    drops.add(fp.getItemStack());
                }
            }
        }

        return drops;
    }

    public List getNoDrops(final List drops) {
        for (final AEPartLocation s : AEPartLocation.values()) {
            final IPart part = this.getPart(s);
            if (part != null) {
                part.getDrops(drops, false);
            }
        }

        return drops;
    }

    @Override
    public boolean recolourBlock(final EnumFacing side, final AEColor colour, final EntityPlayer who) {
        final IPart cable = this.getPart(AEPartLocation.INTERNAL);
        if (cable != null) {
            final IPartCable pc = (IPartCable) cable;
            return pc.changeColor(colour, who);
        }
        return false;
    }

    public boolean isRequiresDynamicRender() {
        return this.requiresDynamicRender;
    }

    private void setRequiresDynamicRender(final boolean requiresDynamicRender) {
        this.requiresDynamicRender = requiresDynamicRender;
    }

    @Override
    public CableBusRenderState getRenderState() {
        PartCable cable = (PartCable) getCenter();

        CableBusRenderState renderState = new CableBusRenderState();

        if (cable != null) {
            renderState.setCableColor(cable.getCableColor());
            renderState.setCableType(cable.getCableConnectionType());
            renderState.setCoreType(CableCoreType.fromCableType(cable.getCableConnectionType()));

            // Check each outgoing connection for the desired characteristics
            for (EnumFacing facing : EnumFacing.values()) {
                // Is there a connection?
                if (!cable.isConnected(facing)) {
                    continue;
                }

                // If there is one, check out which type it has, but default to this cable's type
                AECableType connectionType = cable.getCableConnectionType();

                // Only use the incoming cable-type of the adjacent block, if it's not a cable bus itself
                // Dense cables however also respect the adjacent cable-type since their outgoing connection
                // point would look too big for other cable types
                BlockPos adjacentPos = this.getTile().getPos().offset(facing);
                TileEntity adjacentTe = this.getTile().getWorld().getTileEntity(adjacentPos);
                if (adjacentTe instanceof IGridHost) {
                    if (!(adjacentTe instanceof IPartHost) || cable.getCableConnectionType() == AECableType.DENSE) {
                        IGridHost gridHost = (IGridHost) adjacentTe;
                        connectionType = gridHost
                                .getCableConnectionType(AEPartLocation.fromFacing(facing.getOpposite()));
                    }
                }

                // Check if the adjacent TE is a cable bus or not
                if (adjacentTe instanceof IPartHost) {
                    renderState.getCableBusAdjacent().add(facing);
                }

                renderState.getConnectionTypes().put(facing, connectionType);
            }

            // Collect the number of channels used per side
            // We have to do this even for non-smart cables since a glass cable can display a connection as smart if the adjacent tile requires it
            for (EnumFacing facing : EnumFacing.values()) {
                int channels = cable.getChannelsOnSide(facing);
                renderState.getChannelsOnSide().put(facing, channels);
            }
        }

        // Determine attachments and facades
        for (EnumFacing facing : EnumFacing.values()) {

            FacadeRenderState facadeState = getFacadeRenderState(facing);
            if (facadeState != null) {
                renderState.getFacades().put(facing, facadeState);
            }

            IPart part = getPart(facing);

            if (part == null) {
                continue;
            }

            // This will add the part's bounding boxes to the render state, which is required for facades
            AEPartLocation loc = AEPartLocation.fromFacing(facing);
            IPartCollisionHelper bch = new BusCollisionHelper(renderState.getBoundingBoxes(), loc, null, true);
            part.getBoxes(bch);

            if (part instanceof IGridHost) {
                // Some attachments want a thicker cable than glass, account for that
                IGridHost gridHost = (IGridHost) part;
                AECableType desiredType = gridHost.getCableConnectionType(AEPartLocation.INTERNAL);
                if (renderState.getCoreType() == CableCoreType.GLASS
                        && (desiredType == AECableType.SMART || desiredType == AECableType.COVERED)) {
                    renderState.setCoreType(CableCoreType.COVERED);
                }

                int length = (int) part.getCableConnectionLength(null);
                if (length > 0 && length <= 8) {
                    renderState.getAttachmentConnections().put(facing, length);
                }
            }

            renderState.getAttachments().put(facing, part.getStaticModels());
        }

        return renderState;
    }

    private FacadeRenderState getFacadeRenderState(EnumFacing side) {
        // Store the "masqueraded" itemstack for the given side, if there is a facade
        IFacadePart facade = getFacade(side.ordinal());
        if (facade != null) {
            ItemStack textureItem = facade.getTextureItem();
            IBlockState blockState = facade.getBlockState();
            if (blockState != null && textureItem != null) {
                EnumSet<EnumFacing> openFaces = calculateFaceOpenFaces(side);
                return new FacadeRenderState(blockState, openFaces, textureItem);
            }
        }

        return null;
    }

    private EnumSet<EnumFacing> calculateFaceOpenFaces(EnumFacing side) {
        final EnumSet<EnumFacing> out = EnumSet.of(side, side.getOpposite());
        final IFacadePart facade = getFacade(side.ordinal());

        IBlockAccess blockAccess = getTile().getWorld();
        BlockPos pos = getTile().getPos();
        for (final EnumFacing it : EnumFacing.values()) {
            if (!out.contains(it) && this.hasAlphaDiff(blockAccess.getTileEntity(pos.offset(it)), side, facade)) {
                out.add(it);
            }
        }

        if (out.contains(EnumFacing.UP) && (side.getFrontOffsetX() != 0 || side.getFrontOffsetZ() != 0)) {
            final IFacadePart fp = getFacade(EnumFacing.UP.ordinal());
            if (fp != null && (fp.isTransparent() == facade.isTransparent())) {
                out.remove(EnumFacing.UP);
            }
        }

        if (out.contains(EnumFacing.DOWN) && (side.getFrontOffsetX() != 0 || side.getFrontOffsetZ() != 0)) {
            final IFacadePart fp = getFacade(EnumFacing.DOWN.ordinal());
            if (fp != null && (fp.isTransparent() == facade.isTransparent())) {
                out.remove(EnumFacing.DOWN);
            }
        }

        if (out.contains(EnumFacing.SOUTH) && (side.getFrontOffsetX() != 0)) {
            final IFacadePart fp = getFacade(EnumFacing.SOUTH.ordinal());
            if (fp != null && (fp.isTransparent() == facade.isTransparent())) {
                out.remove(EnumFacing.SOUTH);
            }
        }

        if (out.contains(EnumFacing.NORTH) && (side.getFrontOffsetX() != 0)) {
            final IFacadePart fp = getFacade(EnumFacing.NORTH.ordinal());
            if (fp != null && (fp.isTransparent() == facade.isTransparent())) {
                out.remove(EnumFacing.NORTH);
            }
        }

        return out;
    }

    private boolean hasAlphaDiff(final TileEntity tileEntity, final EnumFacing side, final IFacadePart facade) {
        if (tileEntity instanceof IPartHost) {
            final IPartHost ph = (IPartHost) tileEntity;
            final IFacadePart fp = ph.getFacadeContainer().getFacade(AEPartLocation.fromFacing(side));

            return fp == null || (fp.isTransparent() != facade.isTransparent());
        }

        return true;
    }

}