package mekanism.common.content.boiler;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import mekanism.api.Coord4D;
import mekanism.common.Mekanism;
import mekanism.common.block.states.BlockStateBasic.BasicBlockType;
import mekanism.common.content.tank.SynchronizedTankData.ValveData;
import mekanism.common.multiblock.MultiblockCache;
import mekanism.common.multiblock.MultiblockManager;
import mekanism.common.multiblock.UpdateProtocol;
import mekanism.common.tile.TileEntityBoilerCasing;
import mekanism.common.tile.TileEntityBoilerValve;
import mekanism.common.tile.TileEntityPressureDisperser;
import mekanism.common.tile.TileEntitySuperheatingElement;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
public class BoilerUpdateProtocol extends UpdateProtocol<SynchronizedBoilerData>
{
public static final int WATER_PER_TANK = 16000;
public static final int STEAM_PER_TANK = 160000;
public BoilerUpdateProtocol(TileEntityBoilerCasing tileEntity)
{
super(tileEntity);
}
@Override
protected boolean isValidFrame(int x, int y, int z)
{
return BasicBlockType.get(pointer.getWorld().getBlockState(new BlockPos(x, y, z))) == BasicBlockType.BOILER_CASING;
}
@Override
protected boolean isValidInnerNode(int x, int y, int z)
{
if(super.isValidInnerNode(x, y, z))
{
return true;
}
TileEntity tile = new Coord4D(x, y, z, pointer.getWorld().provider.getDimension()).getTileEntity(pointer.getWorld());
return tile instanceof TileEntityPressureDisperser || tile instanceof TileEntitySuperheatingElement;
}
@Override
protected boolean canForm(SynchronizedBoilerData structure)
{
if(structure.volHeight >= 3)
{
Set<Coord4D> dispersers = new HashSet<Coord4D>();
Set<Coord4D> elements = new HashSet<Coord4D>();
for(Coord4D coord : innerNodes)
{
TileEntity tile = coord.getTileEntity(pointer.getWorld());
if(tile instanceof TileEntityPressureDisperser)
{
dispersers.add(coord);
}
else if(tile instanceof TileEntitySuperheatingElement)
{
structure.internalLocations.add(coord);
elements.add(coord);
}
}
int prevDispersers = dispersers.size();
//Ensure at least one disperser exists
if(dispersers.size() == 0)
{
return false;
}
//Find a single disperser contained within this multiblock
final Coord4D initDisperser = dispersers.iterator().next();
//Ensure that a full horizontal plane of dispersers exist, surrounding the found disperser
Coord4D pos = new Coord4D(structure.renderLocation.xCoord, initDisperser.yCoord, structure.renderLocation.zCoord, pointer.getWorld().provider.getDimension());
for(int x = 1; x < structure.volLength-1; x++)
{
for(int z = 1; z < structure.volWidth-1; z++)
{
Coord4D coord4D = pos.translate(x, 0, z);
TileEntity tile = coord4D.getTileEntity(pointer.getWorld());
if(!(tile instanceof TileEntityPressureDisperser))
{
return false;
}
dispersers.remove(coord4D);
}
}
//If there are more dispersers than those on the plane found, the structure is invalid
if(dispersers.size() > 0)
{
return false;
}
if(elements.size() > 0)
{
structure.superheatingElements = new NodeCounter(new NodeChecker() {
@Override
public boolean isValid(Coord4D coord)
{
return coord.getTileEntity(pointer.getWorld()) instanceof TileEntitySuperheatingElement;
}
}).calculate(elements.iterator().next());
}
if(elements.size() > structure.superheatingElements)
{
return false;
}
Coord4D initAir = null;
int totalAir = 0;
//Find the first available block in the structure for water storage (including casings)
for(int x = structure.renderLocation.xCoord; x < structure.renderLocation.xCoord+structure.volLength; x++)
{
for(int y = structure.renderLocation.yCoord; y < initDisperser.yCoord; y++)
{
for(int z = structure.renderLocation.zCoord; z < structure.renderLocation.zCoord+structure.volWidth; z++)
{
if(pointer.getWorld().isAirBlock(new BlockPos(x, y, z)) || isViableNode(x, y, z))
{
initAir = new Coord4D(x, y, z, pointer.getWorld().provider.getDimension());
totalAir++;
}
}
}
}
//Some air must exist for the structure to be valid
if(initAir == null)
{
return false;
}
//Gradle build requires these fields to be final
final Coord4D renderLocation = structure.renderLocation;
final int volLength = structure.volLength;
final int volWidth = structure.volWidth;
structure.waterVolume = new NodeCounter(new NodeChecker() {
@Override
public final boolean isValid(Coord4D coord)
{
return coord.yCoord >= renderLocation.yCoord-1 && coord.yCoord < initDisperser.yCoord &&
coord.xCoord >= renderLocation.xCoord && coord.xCoord < renderLocation.xCoord+volLength &&
coord.zCoord >= renderLocation.zCoord && coord.zCoord < renderLocation.zCoord+volWidth &&
(coord.isAirBlock(pointer.getWorld()) || isViableNode(coord.getPos()));
}
}).calculate(initAir);
//Make sure all air blocks are connected
if(totalAir > structure.waterVolume)
{
return false;
}
int steamHeight = (structure.renderLocation.yCoord+structure.volHeight-2)-initDisperser.yCoord;
structure.steamVolume = structure.volWidth*structure.volLength*steamHeight;
structure.upperRenderLocation = new Coord4D(structure.renderLocation.xCoord, initDisperser.yCoord+1, structure.renderLocation.zCoord, pointer.getWorld().provider.getDimension());
return true;
}
return false;
}
@Override
protected BoilerCache getNewCache()
{
return new BoilerCache();
}
@Override
protected SynchronizedBoilerData getNewStructure()
{
return new SynchronizedBoilerData();
}
@Override
protected MultiblockManager<SynchronizedBoilerData> getManager()
{
return Mekanism.boilerManager;
}
@Override
protected void mergeCaches(List<ItemStack> rejectedItems, MultiblockCache<SynchronizedBoilerData> cache, MultiblockCache<SynchronizedBoilerData> merge)
{
if(((BoilerCache)cache).water == null)
{
((BoilerCache)cache).water = ((BoilerCache)merge).water;
}
else if(((BoilerCache)merge).water != null && ((BoilerCache)cache).water.isFluidEqual(((BoilerCache)merge).water))
{
((BoilerCache)cache).water.amount += ((BoilerCache)merge).water.amount;
}
if(((BoilerCache)cache).steam == null)
{
((BoilerCache)cache).steam = ((BoilerCache)merge).steam;
}
else if(((BoilerCache)merge).steam != null && ((BoilerCache)cache).steam.isFluidEqual(((BoilerCache)merge).steam))
{
((BoilerCache)cache).steam.amount += ((BoilerCache)merge).steam.amount;
}
((BoilerCache)cache).temperature = Math.max(((BoilerCache)cache).temperature, ((BoilerCache)merge).temperature);
}
@Override
protected void onFormed()
{
super.onFormed();
if((structureFound).waterStored != null)
{
(structureFound).waterStored.amount = Math.min((structureFound).waterStored.amount, structureFound.waterVolume*WATER_PER_TANK);
}
if((structureFound).steamStored != null)
{
(structureFound).steamStored.amount = Math.min((structureFound).steamStored.amount, structureFound.steamVolume*STEAM_PER_TANK);
}
}
@Override
protected void onStructureCreated(SynchronizedBoilerData structure, int origX, int origY, int origZ, int xmin, int xmax, int ymin, int ymax, int zmin, int zmax)
{
for(Coord4D obj : structure.locations)
{
if(obj.getTileEntity(pointer.getWorld()) instanceof TileEntityBoilerValve)
{
ValveData data = new ValveData();
data.location = obj;
data.side = getSide(obj, origX+xmin, origX+xmax, origY+ymin, origY+ymax, origZ+zmin, origZ+zmax);
structure.valves.add(data);
}
}
}
}