package crazypants.enderio.machine.solar;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.Packet;
import net.minecraft.network.play.server.S35PacketUpdateTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.MathHelper;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import cofh.api.energy.EnergyStorage;
import com.enderio.core.common.util.BlockCoord;
import crazypants.enderio.TileEntityEio;
import crazypants.enderio.config.Config;
import crazypants.enderio.power.IInternalPowerProvider;
import crazypants.enderio.power.IPowerInterface;
import crazypants.enderio.power.PowerHandlerUtil;
import crazypants.enderio.waila.IWailaNBTProvider;
public class TileEntitySolarPanel extends TileEntityEio implements IInternalPowerProvider, IWailaNBTProvider {
private final List<Receptor> receptors = new ArrayList<Receptor>();
private ListIterator<Receptor> receptorIterator = receptors.listIterator();
private boolean receptorsDirty = true;
private int lastCollectionValue = -1;
private static final int CHECK_INTERVAL = 100;
EnergyStorage destroyedNetworkBuffer = null;
protected SolarPanelNetwork network = new SolarPanelNetwork();
public void onNeighborBlockChange() {
receptorsDirty = true;
}
// RF Power
@Override
public int extractEnergy(ForgeDirection from, int maxExtract, boolean simulate) {
return 0;
}
@Override
public boolean canConnectEnergy(ForgeDirection from) {
return from == ForgeDirection.DOWN;
}
@Override
public int getEnergyStored(ForgeDirection from) {
return getEnergyStored();
}
@Override
public int getMaxEnergyStored(ForgeDirection from) {
return getMaxEnergyStored();
}
@Override
public int getMaxEnergyRecieved(ForgeDirection dir) {
return 0;
}
@Override
public int getEnergyStored() {
return network.getEnergyStored();
}
@Override
public int getMaxEnergyStored() {
return network.getMaxEnergyStored();
}
@Override
public void setEnergyStored(int stored) {
network.setEnergyStored(stored);
}
@Override
public void doUpdate() {
if (worldObj.isRemote) {
return;
}
collectEnergy();
transmitEnergy();
if (network.isValid()) {
if (destroyedNetworkBuffer != null) {
network.addBuffer(destroyedNetworkBuffer);
destroyedNetworkBuffer = null;
}
network.onUpdate(this);
}
if (!network.isValid() || (shouldDoWorkThisTick(20, 1) && network.addToNetwork(this))) {
findNetwork();
}
}
@Override
public void invalidate() {
network.removeFromNetwork(this);
super.invalidate();
}
private void findNetwork() {
for (ForgeDirection dir : SolarPanelNetwork.VALID_CONS) {
TileEntity te = new BlockCoord(this).getLocation(dir).getTileEntity(worldObj);
if(te != null && te instanceof TileEntitySolarPanel && ((TileEntitySolarPanel) te).canConnect(this)) {
SolarPanelNetwork network = ((TileEntitySolarPanel) te).network;
if(network != null) {
network.addToNetwork(this);
}
}
}
if(!network.isValid()) {
network = new SolarPanelNetwork(this);
}
}
private boolean canConnect(TileEntitySolarPanel other) {
return getBlockMetadata() == other.getBlockMetadata();
}
private void collectEnergy() {
if(canSeeSun()) {
if(lastCollectionValue == -1 || shouldDoWorkThisTick(CHECK_INTERVAL)) {
float fromSun = calculateLightRatio();
lastCollectionValue = Math.round(getEnergyPerTick() * fromSun);
}
if(lastCollectionValue > 0) {
network.setEnergyStored(Math.min(lastCollectionValue + network.getEnergyStored(), network.getMaxEnergyStored()));
}
}
}
private int getEnergyPerTick() {
int meta = getBlockMetadata();
if(meta == 0) {
return Config.maxPhotovoltaicOutputRF;
}
return Config.maxPhotovoltaicAdvancedOutputRF;
}
float calculateLightRatio() {
return calculateLightRatio(worldObj, xCoord, yCoord, zCoord);
}
boolean canSeeSun() {
return worldObj.canBlockSeeTheSky(xCoord, yCoord, zCoord);
}
public static float calculateLightRatio(World world, int x, int y, int z) {
int lightValue = world.getSavedLightValue(EnumSkyBlock.Sky, x, y, z) - world.skylightSubtracted;
float sunAngle = world.getCelestialAngleRadians(1.0F);
if(sunAngle < (float) Math.PI) {
sunAngle += (0.0F - sunAngle) * 0.2F;
} else {
sunAngle += (((float) Math.PI * 2F) - sunAngle) * 0.2F;
}
lightValue = Math.round(lightValue * MathHelper.cos(sunAngle));
lightValue = MathHelper.clamp_int(lightValue, 0, 15);
return lightValue / 15f;
}
private boolean transmitEnergy() {
int canTransmit = Math.min(getEnergyStored(), network.getMaxEnergyExtracted());
int transmitted = 0;
checkReceptors();
if(!receptors.isEmpty() && !receptorIterator.hasNext()) {
receptorIterator = receptors.listIterator();
}
int appliedCount = 0;
int numReceptors = receptors.size();
while (receptorIterator.hasNext() && canTransmit > 0 && appliedCount < numReceptors) {
Receptor receptor = receptorIterator.next();
IPowerInterface pp = receptor.receptor;
if(pp != null && pp.getMinEnergyReceived(receptor.fromDir.getOpposite()) <= canTransmit) {
int used = pp.recieveEnergy(receptor.fromDir.getOpposite(), canTransmit);
transmitted += used;
canTransmit -= used;
}
if(canTransmit <= 0) {
break;
}
if(!receptors.isEmpty() && !receptorIterator.hasNext()) {
receptorIterator = receptors.listIterator();
}
appliedCount++;
}
setEnergyStored(getEnergyStored() - transmitted);
return transmitted > 0;
}
private void checkReceptors() {
if(!receptorsDirty) {
return;
}
receptors.clear();
BlockCoord bc = new BlockCoord(xCoord, yCoord, zCoord);
ForgeDirection dir = ForgeDirection.DOWN;
BlockCoord checkLoc = bc.getLocation(dir);
TileEntity te = worldObj.getTileEntity(checkLoc.x, checkLoc.y, checkLoc.z);
IPowerInterface pi = PowerHandlerUtil.create(te);
if(pi != null) {
receptors.add(new Receptor(pi, dir));
}
receptorIterator = receptors.listIterator();
receptorsDirty = false;
}
static class Receptor {
IPowerInterface receptor;
ForgeDirection fromDir;
private Receptor(IPowerInterface rec, ForgeDirection fromDir) {
receptor = rec;
this.fromDir = fromDir;
}
}
@Override
public void readCustomNBT(NBTTagCompound tag) {
network.readFromNBT(this, tag);
}
@Override
public void writeCustomNBT(NBTTagCompound tag) {
if (network.isValid() && network.shouldSave(this)) {
network.writeToNBT(tag);
tag.setInteger("rfCap", network.getMaxEnergyStored()); // for WAILA
}
}
@Override
public Packet getDescriptionPacket() {
NBTTagCompound nbt = new NBTTagCompound();
writeToNBT(nbt);
return new S35PacketUpdateTileEntity(xCoord, yCoord, zCoord, 1, nbt);
}
@Override
public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) {
readFromNBT(pkt.func_148857_g());
}
@Override
public boolean displayPower() {
return true;
}
public void setNetwork(SolarPanelNetwork network) {
this.network = network;
}
public boolean isMaster() {
return network.getMaster() == this;
}
@Override
public void getData(NBTTagCompound tag) {
if (network.isValid()) {
network.getMaster().writeToNBT(tag);
}
}
}