package mekanism.common.multipart;
import java.util.HashSet;
import java.util.Set;
import mekanism.api.Coord4D;
import mekanism.api.EnumColor;
import mekanism.api.Range4D;
import mekanism.api.util.CapabilityUtils;
import mekanism.common.HashList;
import mekanism.common.InventoryNetwork;
import mekanism.common.Mekanism;
import mekanism.common.base.ILogisticalTransporter;
import mekanism.common.capabilities.Capabilities;
import mekanism.common.content.transporter.TransporterManager;
import mekanism.common.content.transporter.TransporterStack;
import mekanism.common.content.transporter.TransporterStack.Path;
import mekanism.common.multipart.PartSidedPipe.ConnectionType;
import mekanism.common.network.PacketTileEntity.TileEntityMessage;
import mekanism.common.tile.TileEntityLogisticalSorter;
import mekanism.common.util.InventoryUtils;
import mekanism.common.util.MekanismUtils;
import mekanism.common.util.TransporterUtils;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
public class MultipartTransporter extends MultipartTransmitter<IInventory, InventoryNetwork> implements ILogisticalTransporter
{
public HashList<TransporterStack> transit = new HashList<>();
public EnumColor color;
public Set<TransporterStack> needsSync = new HashSet<>();
public MultipartTransporter(PartLogisticalTransporter multiPart)
{
super(multiPart);
}
public void update()
{
if(world().isRemote)
{
for(TransporterStack stack : transit)
{
if(stack != null)
{
stack.progress = Math.min(100, stack.progress+getPart().tier.speed);
}
}
}
else {
if(getTransmitterNetwork() == null)
{
return;
}
Set<TransporterStack> remove = new HashSet<TransporterStack>();
getPart().pullItems();
for(TransporterStack stack : transit)
{
if(!stack.initiatedPath)
{
if(stack.itemStack == null || !recalculate(stack, null))
{
remove.add(stack);
continue;
}
}
stack.progress += getPart().tier.speed;
if(stack.progress > 100)
{
Coord4D prevSet = null;
if(stack.hasPath())
{
int currentIndex = stack.getPath().indexOf(coord());
if(currentIndex == 0) //Necessary for transition reasons, not sure why
{
remove.add(stack);
continue;
}
Coord4D next = stack.getPath().get(currentIndex-1);
if(!stack.isFinal(this))
{
if(next != null && stack.canInsertToTransporter(stack.getNext(this).getTileEntity(world()), stack.getSide(this)))
{
ILogisticalTransporter nextTile = CapabilityUtils.getCapability(next.getTileEntity(world()), Capabilities.LOGISTICAL_TRANSPORTER_CAPABILITY, null);
nextTile.entityEntering(stack, stack.progress%100);
remove.add(stack);
continue;
}
else if(next != null)
{
prevSet = next;
}
}
else {
if(stack.pathType != Path.NONE)
{
if(next != null && next.getTileEntity(world()) instanceof IInventory)
{
needsSync.add(stack);
IInventory inventory = (IInventory)next.getTileEntity(world());
if(inventory != null)
{
ItemStack rejected = InventoryUtils.putStackInInventory(inventory, stack.itemStack, stack.getSide(this), stack.pathType == Path.HOME);
if(rejected == null)
{
TransporterManager.remove(stack);
remove.add(stack);
continue;
}
else {
needsSync.add(stack);
stack.itemStack = rejected;
prevSet = next;
}
}
}
}
}
}
if(!recalculate(stack, prevSet))
{
remove.add(stack);
continue;
}
else {
if(prevSet != null)
{
stack.progress = 0;
}
else {
stack.progress = 50;
}
}
}
else if(stack.progress == 50)
{
if(stack.isFinal(this))
{
if(stack.pathType == Path.DEST && (!checkSideForInsert(stack) || !InventoryUtils.canInsert(stack.getDest().getTileEntity(world()), stack.color, stack.itemStack, stack.getSide(this), false)))
{
if(!recalculate(stack, null))
{
remove.add(stack);
continue;
}
}
else if(stack.pathType == Path.HOME && (!checkSideForInsert(stack) || !InventoryUtils.canInsert(stack.getDest().getTileEntity(world()), stack.color, stack.itemStack, stack.getSide(this), true)))
{
if(!recalculate(stack, null))
{
remove.add(stack);
continue;
}
}
else if(stack.pathType == Path.NONE)
{
if(!recalculate(stack, null))
{
remove.add(stack);
continue;
}
}
}
else {
TileEntity next = stack.getNext(this).getTileEntity(world());
boolean recalculate = false;
if(!stack.canInsertToTransporter(next, stack.getSide(this)))
{
recalculate = true;
}
if(recalculate)
{
if(!recalculate(stack, null))
{
remove.add(stack);
continue;
}
}
}
}
}
for(TransporterStack stack : remove)
{
Mekanism.packetHandler.sendToReceivers(new TileEntityMessage(coord(), getPart().getSyncPacket(stack, true)), new Range4D(coord()));
transit.remove(stack);
MekanismUtils.saveChunk(getPart());
}
for(TransporterStack stack : needsSync)
{
if(transit.contains(stack))
{
Mekanism.packetHandler.sendToReceivers(new TileEntityMessage(coord(), getPart().getSyncPacket(stack, false)), new Range4D(coord()));
}
}
needsSync.clear();
}
}
private boolean checkSideForInsert(TransporterStack stack)
{
EnumFacing side = stack.getSide(this);
return getPart().getConnectionType(side) == ConnectionType.NORMAL || getPart().getConnectionType(side) == ConnectionType.PUSH;
}
private boolean recalculate(TransporterStack stack, Coord4D from)
{
needsSync.add(stack);
if(stack.pathType != Path.NONE)
{
if(!TransporterManager.didEmit(stack.itemStack, stack.recalculatePath(this, 0)))
{
if(!stack.calculateIdle(this))
{
TransporterUtils.drop(this, stack);
return false;
}
}
}
else {
if(!stack.calculateIdle(this))
{
TransporterUtils.drop(this, stack);
return false;
}
}
if(from != null)
{
stack.originalLocation = from;
}
return true;
}
@Override
public ItemStack insert(Coord4D original, ItemStack itemStack, EnumColor color, boolean doEmit, int min)
{
return insert_do(original, itemStack, color, doEmit, min, false);
}
private ItemStack insert_do(Coord4D original, ItemStack itemStack, EnumColor color, boolean doEmit, int min, boolean force)
{
EnumFacing from = coord().sideDifference(original).getOpposite();
TransporterStack stack = new TransporterStack();
stack.itemStack = itemStack;
stack.originalLocation = original;
stack.homeLocation = original;
stack.color = color;
if((force && !canReceiveFrom(original.getTileEntity(world()), from)) || !stack.canInsertToTransporter(this, from))
{
return itemStack;
}
ItemStack rejected = stack.recalculatePath(this, min);
if(TransporterManager.didEmit(stack.itemStack, rejected))
{
stack.itemStack = TransporterManager.getToUse(stack.itemStack, rejected);
if(doEmit)
{
transit.add(stack);
Mekanism.packetHandler.sendToReceivers(new TileEntityMessage(coord(), getPart().getSyncPacket(stack, false)), new Range4D(coord()));
MekanismUtils.saveChunk(getPart());
}
return rejected;
}
return itemStack;
}
@Override
public ItemStack insertRR(TileEntityLogisticalSorter outputter, ItemStack itemStack, EnumColor color, boolean doEmit, int min)
{
EnumFacing from = coord().sideDifference(Coord4D.get(outputter)).getOpposite();
TransporterStack stack = new TransporterStack();
stack.itemStack = itemStack;
stack.originalLocation = Coord4D.get(outputter);
stack.homeLocation = Coord4D.get(outputter);
stack.color = color;
if(!canReceiveFrom(outputter, from) || !stack.canInsertToTransporter(this, from))
{
return itemStack;
}
ItemStack rejected = stack.recalculateRRPath(outputter, this, min);
if(TransporterManager.didEmit(stack.itemStack, rejected))
{
stack.itemStack = TransporterManager.getToUse(stack.itemStack, rejected);
if(doEmit)
{
transit.add(stack);
Mekanism.packetHandler.sendToReceivers(new TileEntityMessage(coord(), getPart().getSyncPacket(stack, false)), new Range4D(coord()));
MekanismUtils.saveChunk(getPart());
}
return rejected;
}
return itemStack;
}
@Override
public void entityEntering(TransporterStack stack, int progress)
{
stack.progress = progress;
transit.add(stack);
Mekanism.packetHandler.sendToReceivers(new TileEntityMessage(coord(), getPart().getSyncPacket(stack, false)), new Range4D(coord()));
MekanismUtils.saveChunk(getPart());
}
@Override
public EnumColor getColor()
{
return color;
}
@Override
public void setColor(EnumColor c)
{
color = c;
}
@Override
public boolean canEmitTo(TileEntity tileEntity, EnumFacing side)
{
if(!getPart().canConnect(side))
{
return false;
}
return getPart().getConnectionType(side) == ConnectionType.NORMAL || getPart().getConnectionType(side) == ConnectionType.PUSH;
}
@Override
public boolean canReceiveFrom(TileEntity tileEntity, EnumFacing side)
{
if(!getPart().canConnect(side))
{
return false;
}
return getPart().getConnectionType(side) == ConnectionType.NORMAL;
}
@Override
public double getCost()
{
return getPart().getCost();
}
@Override
public boolean canConnectMutual(EnumFacing side)
{
return getPart().canConnectMutual(side);
}
@Override
public boolean canConnect(EnumFacing side)
{
return getPart().canConnect(side);
}
@Override
public PartLogisticalTransporter getPart()
{
return (PartLogisticalTransporter)containingPart;
}
}