package erogenousbeef.bigreactors.common.multiblock.helpers;

import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import erogenousbeef.bigreactors.common.BRLog;
import erogenousbeef.bigreactors.common.BigReactors;

public class CoolantContainer extends FluidHelper {

	public static final int HOT = 0;
	public static final int COLD = 1;

	private static final String[] tankNames = { "hot", "cold" };

	private int fluidVaporizedLastTick;
	
	public CoolantContainer() {
		super(true);
		fluidVaporizedLastTick = 0;
	}

	@Override
	public int getNumberOfFluidTanks() {
		return 2;
	}

	@Override
	protected String[] getNBTTankNames() {
		return tankNames;
	}
	
	public boolean isAcceptedCoolant(Fluid fluid) {
		if(fluid == null) { return false; }
		
		// TODO: Lookup
		return fluid.getID() == FluidRegistry.WATER.getID();
	}

	public int addCoolant(FluidStack incoming) {
		if(incoming == null) { return 0; }
		return fill(COLD, incoming, true);
	}
	
	public int drainCoolant(int amount) {
		return drainFluidFromStack(COLD, amount);
	}
	
	public Fluid getCoolantType() {
		return getFluidType(COLD);
	}
	
	public int getCoolantAmount() {
		return getFluidAmount(COLD);
	}
	
	public Fluid getVaporType() {
		return getFluidType(HOT);
	}
	
	public int getVaporAmount() {
		return getFluidAmount(HOT);
	}
	
	public NBTTagCompound writeToNBT(NBTTagCompound destination) {
		super.writeToNBT(destination);

		return destination;
	}
	
	public void readFromNBT(NBTTagCompound data) {
		super.readFromNBT(data);
	}
	
	public void emptyCoolant() {
		setFluid(COLD, null);
	}
	
	public void emptyVapor() {
		setFluid(HOT, null);
	}
	
	public void setCoolant(FluidStack newFuel) {
		setFluid(COLD, newFuel);
	}
	
	public void setVapor(FluidStack newWaste) {
		setFluid(HOT, newWaste);
	}
	
	public void merge(CoolantContainer other) {
		super.merge(other);
	}
	
	public float getCoolantTemperature(float reactorTemperature) {
		Fluid coolantType = getCoolantType();
		if(coolantType == null || getFluidAmount(COLD) <= 0) { return reactorTemperature; }

		return Math.min(reactorTemperature, getBoilingPoint(coolantType));
	}
	
	/**
	 * Attempt to transfer some heat (in RF) into the coolant system.
	 * This method assumes you've already checked the coolant temperature, above,
	 * and scaled the energy absorbed into the coolant based on surface area.
	 * 
	 * @param rfAbsorbed RF to transfer into the coolant system.
	 * @return RF remaining after absorption.
	 */
	public float onAbsorbHeat(float rfAbsorbed) {
		if(getFluidAmount(COLD) <= 0 || rfAbsorbed <= 0) { return rfAbsorbed; }

		Fluid coolantType = getCoolantType();
		int coolantAmt = getFluidAmount(COLD);

		float heatOfVaporization = getHeatOfVaporization(coolantType);
		
		int mbVaporized = Math.min(coolantAmt, (int)(rfAbsorbed / heatOfVaporization));

		// Cap by the available space in the vapor chamber
		mbVaporized = Math.min(mbVaporized, getRemainingSpaceForFluid(HOT));
		
		// We don't do partial vaporization. Just return all the heat.
		if(mbVaporized < 1) { return rfAbsorbed; }

		// Make sure we either have an empty vapor chamber or the vapor types match
		Fluid newVaporType = getVaporizedCoolantFluid(coolantType);
		if(newVaporType == null) {
			BRLog.warning("Coolant in tank (%s) has no registered vapor type!", coolantType.getName());
			return rfAbsorbed;
		}
		
		Fluid existingVaporType = getVaporType();
		if(existingVaporType != null && !newVaporType.getName().equals(existingVaporType.getName())) {
			// Can't vaporize anything with incompatible vapor in the vapor tank
			return rfAbsorbed;
		}
		
		// Vaporize! -- POINT OF NO RETURN
		fluidVaporizedLastTick = mbVaporized;
		this.drainCoolant(mbVaporized);
		
		if(existingVaporType != null) {
			addFluidToStack(HOT, mbVaporized);
		}
		else {
			fill(HOT, new FluidStack(newVaporType, mbVaporized), true);
		}
		
		// Calculate how much we actually absorbed via vaporization
		float energyConsumed = (float)mbVaporized * heatOfVaporization;
		
		// And return energy remaining after absorption
		return Math.max(0f, rfAbsorbed - energyConsumed);
	}
	
	private float getBoilingPoint(Fluid fluid) {
		if(fluid == null) { throw new IllegalArgumentException("Cannot pass a null fluid to getBoilingPoint"); } // just in case

		// TODO: Lookup
		return 100f;
	}
	
	/**
	 * Returns the amount of heat (in RF) needed to convert 1mB of liquid into 1mB of vapor. 
	 * @return
	 */
	private float getHeatOfVaporization(Fluid fluid) {
		if(fluid == null) { throw new IllegalArgumentException("Cannot pass a null fluid to getHeatOfVaporization"); } // just in case

		// TODO: Make a registry for this, do a lookup
		return 4f; // TE converts 1mB steam into 2 RF of work in a steam turbine, so we assume it's 50% efficient.
	}
	
	private Fluid getVaporizedCoolantFluid(Fluid fluid) {
		if(fluid == null) { throw new IllegalArgumentException("Cannot pass a null fluid to getVaporizedCoolantFluid"); } // just in case

		return BigReactors.fluidSteam;
	}

	@Override
	protected boolean isFluidValidForStack(int stackIdx, Fluid fluid) {
		switch(stackIdx) {
		case COLD:
			return isAcceptedCoolant(fluid);
		case HOT:
			return true;
		default:
			return false;
		}
	}
	
	public int getFluidVaporizedLastTick() {
		return fluidVaporizedLastTick;
	}
}
