content: package mods.eln.sixnode.batterycharger;
  
  import mods.eln.Eln;
  import mods.eln.i18n.I18N;
  import mods.eln.item.MachineBoosterDescriptor;
  import mods.eln.item.electricalinterface.IItemEnergyBattery;
  import mods.eln.misc.Direction;
  import mods.eln.misc.LRDU;
  import mods.eln.misc.Utils;
  import mods.eln.node.AutoAcceptInventoryProxy;
  import mods.eln.node.NodeBase;
  import mods.eln.node.six.SixNode;
  import mods.eln.node.six.SixNodeDescriptor;
  import mods.eln.node.six.SixNodeElement;
  import mods.eln.node.six.SixNodeElementInventory;
  import mods.eln.sim.ElectricalLoad;
  import mods.eln.sim.IProcess;
  import mods.eln.sim.ThermalLoad;
  import mods.eln.sim.mna.component.Resistor;
  import mods.eln.sim.nbt.NbtElectricalLoad;
  import mods.eln.sim.process.destruct.VoltageStateWatchDog;
  import mods.eln.sim.process.destruct.WorldExplosion;
  import net.minecraft.entity.player.EntityPlayer;
  import net.minecraft.inventory.Container;
  import net.minecraft.inventory.IInventory;
  import net.minecraft.item.ItemStack;
  import net.minecraft.nbt.NBTTagCompound;
  import org.jetbrains.annotations.NotNull;
  import org.jetbrains.annotations.Nullable;
  
  import java.io.DataInputStream;
  import java.io.DataOutputStream;
  import java.io.IOException;
  import java.util.HashMap;
  import java.util.Map;
  
  public class BatteryChargerElement extends SixNodeElement {
  
      public BatteryChargerDescriptor descriptor;
  
      public NbtElectricalLoad powerLoad = new NbtElectricalLoad("powerLoad");
      public BatteryChargerSlowProcess slowProcess = new BatteryChargerSlowProcess();
      Resistor powerResistor = new Resistor(powerLoad, null);
  
      AutoAcceptInventoryProxy inventory = (new AutoAcceptInventoryProxy(new SixNodeElementInventory(5, 64, this)))
          .acceptIfEmpty(0, IItemEnergyBattery.class)
          .acceptIfEmpty(1, IItemEnergyBattery.class)
          .acceptIfEmpty(2, IItemEnergyBattery.class)
          .acceptIfEmpty(3, IItemEnergyBattery.class)
          .acceptIfIncrement(4, 5, MachineBoosterDescriptor.class);
  
      VoltageStateWatchDog voltageWatchDog = new VoltageStateWatchDog(powerLoad);
  
      public String channel = "Default channel";
  
      boolean invChanged = false;
  
      boolean powerOn = false;
  
      public static final byte toogleCharge = 1;
  
      byte charged, presence;
  
      @Override
      public IInventory getInventory() {
          if (inventory != null)
              return inventory.getInventory();
          else
              return null;
      }
  
      @Nullable
      @Override
      public Container newContainer(@NotNull Direction side, @NotNull EntityPlayer player) {
          return new BatteryChargerContainer(player, inventory.getInventory());
      }
  
      public BatteryChargerElement(SixNode sixNode, Direction side, SixNodeDescriptor descriptor) {
          super(sixNode, side, descriptor);
          this.descriptor = (BatteryChargerDescriptor) descriptor;
  
          electricalLoadList.add(powerLoad);
          electricalComponentList.add(powerResistor);
          slowProcessList.add(slowProcess);
  
          WorldExplosion exp = new WorldExplosion(this).machineExplosion();
          slowProcessList.add(voltageWatchDog.setNominalVoltage(this.descriptor.nominalVoltage).setDestroys(exp));
      }
  
      @Override
      public ElectricalLoad getElectricalLoad(LRDU lrdu, int mask) {
          if (front == lrdu) return powerLoad;
          return null;
      }
  
      @Nullable
      @Override
      public ThermalLoad getThermalLoad(@NotNull LRDU lrdu, int mask) {
          return null;
      }
  
      @Override
      public int getConnectionMask(LRDU lrdu) {
          if (front == lrdu) return NodeBase.maskElectricalPower;
          return 0;
      }
  
      @Override
      public String multiMeterString() {
          return Utils.plotUIP(powerLoad.getVoltage(), powerLoad.getCurrent());
      }
  
      @NotNull
      @Override
      public Map<String, String> getWaila() {
          Map<String, String> info = new HashMap<String, String>();
          info.put(I18N.tr("Charge Current"), Utils.plotAmpere("", powerLoad.getCurrent()));
          if (Eln.wailaEasyMode) {
              info.put(I18N.tr("Voltage"), Utils.plotVolt("", powerLoad.getVoltage()));
              info.put(I18N.tr("Power"), Utils.plotPower("", powerLoad.getCurrent() * powerLoad.getVoltage()));
          }
          return info;
      }
  
      @NotNull
      @Override
      public String thermoMeterString() {
          return null;
      }
  
      @Override
      public void initialize() {
          descriptor.applyTo(powerLoad);
      }
  
      @Override
      public void inventoryChanged() {
          super.inventoryChanged();
          //needPublish();
          invChanged = true;
      }
  
      @Override
      public boolean onBlockActivated(EntityPlayer entityPlayer, Direction side, float vx, float vy, float vz) {
          if (onBlockActivatedRotate(entityPlayer)) {
              return true;
          } else {
              return inventory.take(entityPlayer.getCurrentEquippedItem(), this, false, true);
          }
      }
  
      @Override
      public void writeToNBT(NBTTagCompound nbt) {
          super.writeToNBT(nbt);
          nbt.setBoolean("powerOn", powerOn);
          nbt.setDouble("energyCounter", slowProcess.energyCounter);
      }
  
      @Override
      public void readFromNBT(@NotNull NBTTagCompound nbt) {
          super.readFromNBT(nbt);
          powerOn = nbt.getBoolean("powerOn");
          slowProcess.energyCounter = nbt.getDouble("energyCounter");
      }
  
      @Override
      public void networkUnserialize(DataInputStream stream) {
          super.networkUnserialize(stream);
  
          try {
              switch (stream.readByte()) {
                  case toogleCharge:
                      powerOn = !powerOn;
                      needPublish();
                      break;
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  
      @Override
      public boolean hasGui() {
          return true;
      }
  
      @Override
      public void networkSerialize(DataOutputStream stream) {
          super.networkSerialize(stream);
          try {
              stream.writeBoolean(powerOn);
              stream.writeFloat((float) powerLoad.getVoltage());
              Utils.serialiseItemStack(stream, getInventory().getStackInSlot(0));
              Utils.serialiseItemStack(stream, getInventory().getStackInSlot(1));
              Utils.serialiseItemStack(stream, getInventory().getStackInSlot(2));
              Utils.serialiseItemStack(stream, getInventory().getStackInSlot(3));
  
              stream.writeByte(charged);
              stream.writeByte(presence);
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  
      class BatteryChargerSlowProcess implements IProcess {
          double energyCounter = 0;
          double timeout = 0;
  
          @Override
          public void process(double time) {
              timeout -= time;
              if (timeout > 0) return;
              timeout = 1;
              time = 1;
  
              byte oldCharged = charged;
              charged = 0;
              presence = 0;
              if (!powerOn) {
                  descriptor.setRp(powerResistor, false);
              } else {
                  ItemStack booster = (getInventory().getStackInSlot(BatteryChargerContainer.boosterSlotId));
                  double boost = 1.0;
                  double eff = 1.0;
                  if (booster != null) {
                      boost = Math.pow(1.25, booster.stackSize);
                      eff = Math.pow(0.9, booster.stackSize);
                  }
  
                  energyCounter += powerResistor.getPower() * time * eff;
  
                  for (int idx = 0; idx < 4; idx++) {
                      ItemStack stack = getInventory().getStackInSlot(idx);
                      Object o = Utils.getItemObject(stack);
                      if (o instanceof IItemEnergyBattery) {
                          IItemEnergyBattery b = (IItemEnergyBattery) o;
                          double e = Math.min(Math.min(energyCounter, b.getChargePower(stack) * time * boost), b.getEnergyMax(stack) - b.getEnergy(stack));
                          b.setEnergy(stack, b.getEnergy(stack) + e);
                          energyCounter -= e;
                      }
                  }
  
                  if (energyCounter < descriptor.nominalPower * time * 2 * boost) {
                      //double target = descriptor.nominalPower * time * 2;
                      double power = Math.min(descriptor.nominalPower * boost, (descriptor.nominalPower * time * 2 * boost - energyCounter) / time);
                      powerResistor.setResistance(Math.max(powerLoad.getVoltage() * powerLoad.getVoltage() / power, descriptor.Rp / boost));
                  } else {
                      descriptor.setRp(powerResistor, false);
                  }
              }
              for (int idx = 0; idx < 4; idx++) {
                  ItemStack stack = getInventory().getStackInSlot(idx);
                  Object o = Utils.getItemObject(stack);
                  if (o instanceof IItemEnergyBattery) {
                      IItemEnergyBattery b = (IItemEnergyBattery) o;
                      if (b.getEnergy(stack) == b.getEnergyMax(stack)) {
                          charged += 1 << idx;
                      }
                      presence += 1 << idx;
                  }
              }
  
              if (charged != oldCharged)
                  needPublish();
              if (invChanged) {
                  invChanged = false;
                  needPublish();
              }
          }
      }
  }
  