package pneumaticCraft.common.heat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import pneumaticCraft.api.IHeatExchangerLogic;
import pneumaticCraft.api.tileentity.HeatBehaviour;
import pneumaticCraft.common.heat.behaviour.HeatBehaviourManager;
import pneumaticCraft.common.network.GuiSynced;
public class HeatExchangerLogic implements IHeatExchangerLogic{
private final Set<IHeatExchangerLogic> hullExchangers = new HashSet<IHeatExchangerLogic>();
private final Set<IHeatExchangerLogic> connectedExchangers = new HashSet<IHeatExchangerLogic>();
private List<HeatBehaviour> behaviours = new ArrayList<HeatBehaviour>();
private List<HeatBehaviour> newBehaviours;//Required to prevent a CME
@GuiSynced
private double temperature = 295;//degrees Kelvin, 20 degrees by default.
private double thermalResistance = 1;
private double thermalCapacity = 1;
private static boolean isAddingOrRemovingLogic;
@Override
public void initializeAsHull(World world, int x, int y, int z, ForgeDirection... validSides){
if(world.isRemote) return;
for(IHeatExchangerLogic logic : hullExchangers) {
removeConnectedExchanger(logic);
}
hullExchangers.clear();
newBehaviours = new ArrayList<HeatBehaviour>();
for(ForgeDirection d : ForgeDirection.VALID_DIRECTIONS) {
if(isSideValid(validSides, d)) {
HeatBehaviourManager.getInstance().addHeatBehaviours(world, x + d.offsetX, y + d.offsetY, z + d.offsetZ, this, newBehaviours);
IHeatExchangerLogic logic = HeatExchangerManager.getInstance().getLogic(world, x + d.offsetX, y + d.offsetY, z + d.offsetZ, d.getOpposite());
if(logic != null) {
hullExchangers.add(logic);
addConnectedExchanger(logic);
}
}
}
}
private boolean isSideValid(ForgeDirection[] validSides, ForgeDirection side){
if(validSides.length == 0) return true;
for(ForgeDirection d : validSides) {
if(d == side) return true;
}
return false;
}
@Override
public void addConnectedExchanger(IHeatExchangerLogic exchanger){
connectedExchangers.add(exchanger);
if(!isAddingOrRemovingLogic) {
isAddingOrRemovingLogic = true;
exchanger.addConnectedExchanger(this);
isAddingOrRemovingLogic = false;
}
}
@Override
public void removeConnectedExchanger(IHeatExchangerLogic exchanger){
connectedExchangers.remove(exchanger);
if(!isAddingOrRemovingLogic) {
isAddingOrRemovingLogic = true;
exchanger.removeConnectedExchanger(this);
isAddingOrRemovingLogic = false;
}
}
@Override
public double getTemperature(){
return temperature;
}
@Override
public void setTemperature(double temperature){
this.temperature = temperature;
}
@Override
public void setThermalResistance(double thermalResistance){
this.thermalResistance = thermalResistance;
}
@Override
public double getThermalResistance(){
return thermalResistance;
}
@Override
public void setThermalCapacity(double capacity){
thermalCapacity = capacity;
}
@Override
public double getThermalCapacity(){
return thermalCapacity;
}
@Override
public void writeToNBT(NBTTagCompound tag){
tag.setDouble("temperature", temperature);
NBTTagList tagList = new NBTTagList();
for(HeatBehaviour behaviour : behaviours) {
NBTTagCompound t = new NBTTagCompound();
t.setString("id", behaviour.getId());
behaviour.writeToNBT(t);
tagList.appendTag(t);
}
tag.setTag("behaviours", tagList);
}
@Override
public void readFromNBT(NBTTagCompound tag){
temperature = tag.getDouble("temperature");
behaviours.clear();
NBTTagList tagList = tag.getTagList("behaviours", 10);
for(int i = 0; i < tagList.tagCount(); i++) {
NBTTagCompound t = tagList.getCompoundTagAt(i);
HeatBehaviour behaviour = HeatBehaviourManager.getInstance().getBehaviourForId(t.getString("id"));
if(behaviour != null) {
behaviour.readFromNBT(t);
behaviours.add(behaviour);
}
}
}
@Override
public void update(){
if(getThermalCapacity() < 0.1D) {
temperature = 295;
return;
}
if(newBehaviours != null) {
List<HeatBehaviour> oldBehaviours = behaviours;
behaviours = newBehaviours;
newBehaviours = null;
for(HeatBehaviour oldBehaviour : oldBehaviours) {//Transfer over equal heat behaviour's info.
int equalBehaviourIndex = behaviours.indexOf(oldBehaviour);
if(equalBehaviourIndex >= 0) {
NBTTagCompound tag = new NBTTagCompound();
oldBehaviour.writeToNBT(tag);
behaviours.get(equalBehaviourIndex).readFromNBT(tag);
}
}
}
Iterator<HeatBehaviour> iterator = behaviours.iterator();
while(iterator.hasNext()) {
HeatBehaviour behaviour = iterator.next();
if(behaviour.getWorld() != null) {//upon loading from NBT the world is null. gets initialized once 'initializeAsHull' is invoked.
if(behaviour.isApplicable()) {
behaviour.update();
} else {
iterator.remove();
}
}
}
for(IHeatExchangerLogic logic : connectedExchangers) {
exchange(logic, this, getTickingHeatExchangers());//As the connected logics also will tick, we should prevent dispersing more when more are connected.
}
}
public static void exchange(IHeatExchangerLogic logic, IHeatExchangerLogic logic2){
exchange(logic, logic2, 1);
}
public static void exchange(IHeatExchangerLogic logic, IHeatExchangerLogic logic2, double dispersionDivider){
if(logic.getThermalCapacity() < 0.1D) {
logic.setTemperature(295);
return;
}
double deltaTemp = logic.getTemperature() - logic2.getTemperature();
double totalResistance = logic2.getThermalResistance() + logic.getThermalResistance();
deltaTemp /= dispersionDivider;
deltaTemp /= totalResistance;
double maxDeltaTemp = (logic.getTemperature() * logic.getThermalCapacity() - logic2.getTemperature() * logic2.getThermalCapacity()) / 2;//Calculate the heat needed to exactly equalize the heat.
if(maxDeltaTemp >= 0 && deltaTemp > maxDeltaTemp || maxDeltaTemp <= 0 && deltaTemp < maxDeltaTemp) deltaTemp = maxDeltaTemp;
logic2.addHeat(deltaTemp);
logic.addHeat(-deltaTemp);
}
private int getTickingHeatExchangers(){
int tickingHeatExchangers = 1;
for(IHeatExchangerLogic logic : connectedExchangers) {
if(logic instanceof HeatExchangerLogic) tickingHeatExchangers++;
}
return tickingHeatExchangers;
}
@Override
public void addHeat(double amount){
temperature += amount / getThermalCapacity();
temperature = Math.max(0, Math.min(2273, temperature));
}
}