package com.hbm.tileentity.machine;

import java.util.HashMap;

import com.hbm.blocks.BlockDummyable;
import com.hbm.handler.CompatHandler;
import com.hbm.handler.pollution.PollutionHandler;
import com.hbm.handler.pollution.PollutionHandler.PollutionType;
import com.hbm.interfaces.IControlReceiver;
import com.hbm.inventory.fluid.tank.FluidTank;
import com.hbm.inventory.fluid.trait.FT_Combustible;
import com.hbm.inventory.fluid.trait.FT_Combustible.FuelGrade;
import com.hbm.inventory.gui.GUIMachineTurbineGas;
import com.hbm.inventory.container.ContainerMachineTurbineGas;
import com.hbm.inventory.fluid.FluidType;
import com.hbm.inventory.fluid.Fluids;
import com.hbm.items.machine.IItemFluidIdentifier;
import com.hbm.lib.Library;
import com.hbm.main.MainRegistry;
import com.hbm.sound.AudioWrapper;
import com.hbm.tileentity.IFluidCopiable;
import com.hbm.tileentity.IGUIProvider;
import com.hbm.tileentity.TileEntityMachineBase;
import com.hbm.util.BobMathUtil;
import com.hbm.util.CompatEnergyControl;

import api.hbm.energymk2.IEnergyProviderMK2;
import api.hbm.fluid.IFluidStandardTransceiver;
import api.hbm.tile.IInfoProviderEC;
import cpw.mods.fml.common.Optional;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import li.cil.oc.api.machine.Arguments;
import li.cil.oc.api.machine.Callback;
import li.cil.oc.api.machine.Context;
import li.cil.oc.api.network.SimpleComponent;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;

@Optional.InterfaceList({@Optional.Interface(iface = "li.cil.oc.api.network.SimpleComponent", modid = "OpenComputers")})
public class TileEntityMachineTurbineGas extends TileEntityMachineBase implements IFluidStandardTransceiver, IEnergyProviderMK2, IControlReceiver, IGUIProvider, SimpleComponent, IInfoProviderEC, CompatHandler.OCComponent, IFluidCopiable {

	public long power;
	public static final long maxPower = 1000000L;

	public int rpm; //0-100, crescent moon gauge, used for calculating the amount of power generated, starts past 10%
	public int temp; //0-800, used for figuring out how much water to boil, starts boiling at 300°C
	public int rpmIdle = 10;
	public int tempIdle = 300;

	public int powerSliderPos; //goes from 0 to 60, 0 is idle, 60 is max power
	public int throttle; //the same thing, but goes from 0 to 100

	public boolean autoMode;
	public int state = 0; //0 is offline, -1 is startup, 1 is online

	public int counter = 0; //used to startup and shutdown
	public int instantPowerOutput;
	public double waterToBoil;

	public FluidTank[] tanks;

	private AudioWrapper audio;

	public static HashMap<FluidType, Double> fuelMaxCons = new HashMap(); //fuel consumption per tick at max power

	static {
		fuelMaxCons.put(Fluids.GAS, 50D);			// natgas doesn't burn well so it burns faster to compensate
		fuelMaxCons.put(Fluids.SYNGAS, 10D);		// syngas just fucks
		fuelMaxCons.put(Fluids.OXYHYDROGEN, 100D);	// oxyhydrogen is terrible so it needs to burn a ton for the bare minimum
		fuelMaxCons.put(Fluids.REFORMGAS, 5D);		// fuck it we ball
		// default to 5 if not in list
	}

	//TODO particles from heat exchanger maybe? maybe in a future

	public TileEntityMachineTurbineGas() {
		super(2);
		this.tanks = new FluidTank[4];
		tanks[0] = new FluidTank(Fluids.GAS, 100000);
		tanks[1] = new FluidTank(Fluids.LUBRICANT, 16000);
		tanks[2] = new FluidTank(Fluids.WATER, 16000);
		tanks[3] = new FluidTank(Fluids.HOTSTEAM, 160000);
	}

	private long powerBeforeNet;

	@Override
	public void updateEntity() {

		if(!worldObj.isRemote) {

			waterToBoil = 0; //reset
			throttle = powerSliderPos * 100 / 60;

			if(slots[1] != null && slots[1].getItem() instanceof IItemFluidIdentifier) {
				FluidType fluid = ((IItemFluidIdentifier) slots[1].getItem()).getType(worldObj, xCoord, yCoord, zCoord, slots[1]);
				if(fluid.hasTrait(FT_Combustible.class) && fluid.getTrait(FT_Combustible.class).getGrade() == FuelGrade.GAS) {
					tanks[0].setTankType(fluid);
				}
			}

			if(autoMode) { //power production depending on power requirement and fuel level

				int powerSliderTarget;

				//when low on fuel, decrease consumption linearly
				if(tanks[0].getFill() * 10 > tanks[0].getMaxFill()) {
					powerSliderTarget = 60 - (int) (60 * power / maxPower); //scales the slider proportionally to the power gauge
				}
				else {
					powerSliderTarget = (int) ( tanks[0].getFill() * 0.0001 * (60 - (int) (60 * power / maxPower)) );
				}

				if(powerSliderTarget > powerSliderPos) { //makes the auto slider slide instead of snapping into position
					powerSliderPos++;
				}
				else if(powerSliderTarget < powerSliderPos) {
					powerSliderPos--;
				}
			}

			switch(state) { //what to do when turbine offline, starting up and online
			case 0:
				shutdown();
				break;
			case -1:
				stopIfNotReady();
				startup();
				break;
			case 1:
				stopIfNotReady();
				run();
				break;
			default:
				break;
			}

			ForgeDirection dir = ForgeDirection.getOrientation(this.getBlockMetadata() - BlockDummyable.offset);
			ForgeDirection rot = dir.getRotation(ForgeDirection.UP);

			powerBeforeNet = Math.min(this.power, maxPower);

			//do net/battery deductions first...
			power = Library.chargeItemsFromTE(slots, 0, power, maxPower);
			this.tryProvide(worldObj, xCoord - dir.offsetZ * 5, yCoord + 1, zCoord + dir.offsetX * 5, rot); //sends out power

			//...and then cap it. Prevents potential future cases where power would be limited due to the fuel being too strong and the buffer too small.
			if(this.power > this.maxPower)
				this.power = this.maxPower;

			for(int i = 0; i < 2; i++) { //fuel and lube
				this.trySubscribe(tanks[i].getTankType(), worldObj, xCoord - dir.offsetX * 2 + rot.offsetX, yCoord, zCoord - dir.offsetZ * 2 + rot.offsetZ, dir.getOpposite());
				this.trySubscribe(tanks[i].getTankType(), worldObj, xCoord + dir.offsetX * 2 + rot.offsetX, yCoord, zCoord + dir.offsetZ * 2 + rot.offsetZ, dir);
			}
			//water
			this.trySubscribe(tanks[2].getTankType(), worldObj, xCoord - dir.offsetX * 2 + rot.offsetX * -4, yCoord, zCoord - dir.offsetZ * 2 + rot.offsetZ * -4, dir.getOpposite());
			this.trySubscribe(tanks[2].getTankType(), worldObj, xCoord + dir.offsetX * 2 + rot.offsetX * -4, yCoord, zCoord + dir.offsetZ * 2 + rot.offsetZ * -4, dir);
			//steam
			this.sendFluid(tanks[3], worldObj, xCoord + dir.offsetZ * 6, yCoord + 1, zCoord - dir.offsetX * 6, rot.getOpposite());

			this.networkPackNT(150);

		} else { //client side, for sounds n shit

			if(rpm >= 10 && state != -1) { //if conditions are right, play the sound

				if(audio == null) { //if there is no sound playing, start it

					audio = MainRegistry.proxy.getLoopedSound("hbm:block.turbinegasRunning", xCoord, yCoord, zCoord, getVolume(1.0F), 20F, 2.0F, 20);
					audio.startSound();

				} else if(!audio.isPlaying()) {
					audio.stopSound();
					audio = MainRegistry.proxy.getLoopedSound("hbm:block.turbinegasRunning", xCoord, yCoord, zCoord, getVolume(1.0F), 20F, 2.0F, 20);
					audio.startSound();
				}

				audio.updatePitch((float) (0.55 + 0.1 * rpm / 10)); //dynamic pitch update based on rpm
				audio.updateVolume(getVolume(2F)); //yeah i need this
				audio.keepAlive();

			} else {

				if(audio != null) {
					audio.stopSound();
					audio = null;
				}
			}
		}
	}

	@Override
	public void serialize(ByteBuf buf) {
		super.serialize(buf);
		buf.writeLong(this.powerBeforeNet);
		buf.writeInt(this.rpm);
		buf.writeInt(this.temp);
		buf.writeInt(this.state);
		buf.writeBoolean(this.autoMode);
		buf.writeInt(this.throttle);
		buf.writeInt(this.powerSliderPos);

		if(state != 1) {
			buf.writeInt(this.counter); //sent during startup and shutdown
		} else {
			buf.writeInt(this.instantPowerOutput); //sent while running
		}

		tanks[0].serialize(buf);
		tanks[1].serialize(buf);
		tanks[2].serialize(buf);
		tanks[3].serialize(buf);
	}

	@Override
	public void deserialize(ByteBuf buf) {
		super.deserialize(buf);
		this.power = buf.readLong();
		this.rpm = buf.readInt();
		this.temp = buf.readInt();
		this.state = buf.readInt();
		this.autoMode = buf.readBoolean();
		this.throttle = buf.readInt();
		this.powerSliderPos = buf.readInt();

		if(state != 1)
			this.counter = buf.readInt();
		else
			this.instantPowerOutput = buf.readInt(); //state 1

		this.tanks[0].deserialize(buf);
		this.tanks[1].deserialize(buf);
		this.tanks[2].deserialize(buf);
		this.tanks[3].deserialize(buf);
	}

	private void stopIfNotReady() {

		if(tanks[0].getFill() == 0 || tanks[1].getFill() == 0) {
			state = 0;
		}
		if(!hasAcceptableFuel()) {
			state = 0;
		}
	}

	public boolean hasAcceptableFuel() {

		if(tanks[0].getTankType().hasTrait(FT_Combustible.class)) {
			return tanks[0].getTankType().getTrait(FT_Combustible.class).getGrade() == FuelGrade.GAS;
		}

		return false;
	}

	private void startup() {

		counter++;

		if(counter <= 20) //rpm gauge 0-100-0
			rpm = 5 * counter;
		else if (counter > 20 && counter <= 40)
			rpm = 100 - 5 * (counter - 20);
		else if (counter > 50) {
			rpm = (int) (rpmIdle * (counter - 50) / 530); //slowly ramps up temp and RPM
			temp = (int) (tempIdle * (counter - 50) / 530);
		}

		if(counter == 50) {
			worldObj.playSoundEffect(xCoord, yCoord + 2, zCoord, "hbm:block.turbinegasStartup", getVolume(1.0F), 1.0F);
		}

		if(counter == 580) {
			state = 1;
		}
	}


	int rpmLast; //used to progressively slow down and cool the turbine without immediatly setting rpm and temp to 0
	int tempLast;

	private void shutdown() {

		autoMode = false;
		instantPowerOutput = 0;

		if(powerSliderPos > 0)
			powerSliderPos--;

		if(rpm <= 10 && counter > 0) {

			if(counter == 225) {

				worldObj.playSoundEffect(xCoord, yCoord + 2, zCoord, "hbm:block.turbinegasShutdown", getVolume(1.0F), 1.0F);

				rpmLast = rpm;
				tempLast = temp;
			}

			counter--;

			rpm = (int) (rpmLast * (counter) / 225);
			temp = (int) (tempLast * (counter) / 225);

		} else if(rpm > 11) { //quickly slows down the turbine to idle before shutdown
			counter = 42069; //absolutely necessary to avoid fuckeries on shutdown
			rpm--;
		} else if(rpm == 11) {
			counter = 225;
			rpm--;
		}
	}

	/** Dynamically calculates a (hopefully) sensible burn heat from the combustion energy, scales from 300°C - 800°C */
	protected int getFluidBurnTemp(FluidType type) {
		double dFuel = type.hasTrait(FT_Combustible.class) ? type.getTrait(FT_Combustible.class).getCombustionEnergy() : 0;
		return (int) Math.floor(800D - (Math.pow(Math.E, -dFuel / 100_000D)) * 300D);
	}

	private void run() {

		if((int) (throttle * 0.9) > rpm - rpmIdle) { //simulates the rotor's moment of inertia
			if(worldObj.getTotalWorldTime() % 5 == 0) {
				rpm++;
			}
		} else if((int) (throttle * 0.9) < rpm - rpmIdle) {
			if(worldObj.getTotalWorldTime() % 2 == 0) {
				rpm--;
			}
		}

		int maxTemp = getFluidBurnTemp(tanks[0].getTankType()); // fuelMaxTemp.get(tanks[0].getTankType())

		if(throttle * 5 * (maxTemp - tempIdle) / 500 > temp - tempIdle) { //simulates the heat exchanger's resistance to temperature variation
			if(worldObj.getTotalWorldTime() % 2 == 0) {
				temp++;
			}
		} else if(throttle * 5 * (maxTemp - tempIdle) / 500 < temp - tempIdle) {
			if(worldObj.getTotalWorldTime() % 2 == 0) {
				temp--;
			}
		}

		double consumption = fuelMaxCons.containsKey(tanks[0].getTankType()) ? fuelMaxCons.get(tanks[0].getTankType()) : 5D;
		if(worldObj.getTotalWorldTime() % 20 == 0 && tanks[0].getTankType() != Fluids.OXYHYDROGEN) PollutionHandler.incrementPollution(worldObj, xCoord, yCoord, zCoord, PollutionType.SOOT, PollutionHandler.SOOT_PER_SECOND * 3);
		makePower(consumption, throttle);
	}


	double fuelToConsume; //used to consume 1 mb of fuel at a time when consumption is <1 mb/tick

	private void makePower(double consMax, int throttle) {

		double idleConsumption = consMax * 0.05D;
		double consumption = idleConsumption + consMax * throttle / 100;

		fuelToConsume += consumption;

		tanks[0].setFill(tanks[0].getFill() - (int) Math.floor(fuelToConsume));
		fuelToConsume -= (int) Math.floor(fuelToConsume);

		if(worldObj.getTotalWorldTime() % 10 == 0) //lube consumption
			tanks[1].setFill(tanks[1].getFill() - 1);

		if(tanks[0].getFill() < 0) { //avoids negative amounts of fluid
			tanks[0].setFill(0);
			state = 0;
		}
		if(tanks[1].getFill() < 0) {
			tanks[1].setFill(0);
			state = 0;
		}


		long energy = 0; //energy per mb of fuel

		if(tanks[0].getTankType().hasTrait(FT_Combustible.class)) {
			energy = tanks[0].getTankType().getTrait(FT_Combustible.class).getCombustionEnergy() / 1000L;
		}

		int rpmEff = rpm - rpmIdle; // RPM above idle level, 0-90

		//consMax*energy is equivalent to power production at 100%
		if(instantPowerOutput < (consMax * energy * rpmEff / 90)) { //this shit avoids power rising in steps of 2000 or so HE at a time, instead it does it smoothly
			instantPowerOutput += Math.random() * 0.005 * consMax * energy;
			if(instantPowerOutput > (consMax * energy * rpmEff / 90))
				instantPowerOutput = (int) (consMax * energy * rpmEff / 90);
		}
		else if(instantPowerOutput > (consMax * energy * rpmEff / 90)) {
			instantPowerOutput -= Math.random() * 0.011 * consMax * energy;
			if(instantPowerOutput < (consMax * energy * rpmEff / 90))
				instantPowerOutput = (int) (consMax * energy * rpmEff / 90);
		}
		this.power += instantPowerOutput;

		double waterPerTick = (consMax * energy * (temp - tempIdle) / 220000); //it just works fuck you

		this.waterToBoil = waterPerTick; //caching in a field for the EC compat to use

		int heatCycles = (int) Math.floor(waterToBoil);
		int waterCycles = tanks[2].getFill();
		int steamCycles = (tanks[3].getMaxFill() - tanks[3].getFill()) / 10;
		int cycles = BobMathUtil.min(heatCycles, waterCycles, steamCycles);

		tanks[2].setFill(tanks[2].getFill() - cycles);
		tanks[3].setFill(tanks[3].getFill() + cycles * 10);
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	@Override
	public void readFromNBT(NBTTagCompound nbt) {
		super.readFromNBT(nbt);
		this.tanks[0].readFromNBT(nbt, "gas");
		this.tanks[1].readFromNBT(nbt, "lube");
		this.tanks[2].readFromNBT(nbt, "water");
		this.tanks[3].readFromNBT(nbt, "densesteam");
		this.autoMode = nbt.getBoolean("automode");
		this.power = nbt.getLong("power");
		this.state = nbt.getInteger("state");
		this.rpm = nbt.getInteger("rpm");
		this.temp = nbt.getInteger("temperature");
		this.powerSliderPos = nbt.getInteger("slidPos");
		this.instantPowerOutput = nbt.getInteger("instPwr");
		this.counter = nbt.getInteger("counter");
	}

	@Override
	public void writeToNBT(NBTTagCompound nbt) {
		super.writeToNBT(nbt);

		tanks[0].writeToNBT(nbt, "gas");
		tanks[1].writeToNBT(nbt, "lube");
		tanks[2].writeToNBT(nbt, "water");
		tanks[3].writeToNBT(nbt, "densesteam");
		nbt.setBoolean("automode", autoMode);
		nbt.setLong("power", power);
		if(state == 1) {
			nbt.setInteger("state", this.state);
			nbt.setInteger("rpm", this.rpm);
			nbt.setInteger("temperature", this.temp);
			nbt.setInteger("slidPos", this.powerSliderPos);
			nbt.setInteger("instPwr", instantPowerOutput);
			nbt.setInteger("counter", 225);
		} else {
			nbt.setInteger("state", 0);
			nbt.setInteger("rpm", 0);
			nbt.setInteger("temperature", 20);
			nbt.setInteger("slidPos", 0);
			nbt.setInteger("instpwr", 0);
			nbt.setInteger("counter", 0);
		}
	}

	@Override
	public void receiveControl(NBTTagCompound data) {

		if(data.hasKey("slidPos"))
			powerSliderPos = data.getInteger("slidPos");

		if(data.hasKey("autoMode"))
			autoMode = data.getBoolean("autoMode");

		if(data.hasKey("state"))
			state = data.getInteger("state");

		this.markDirty();
	}

	@Override
	public boolean hasPermission(EntityPlayer player) {
		return Vec3.createVectorHelper(xCoord - player.posX, yCoord - player.posY, zCoord - player.posZ).lengthVector() < 25;
	}

	@Override
	public void onChunkUnload() {

		if(audio != null) {
			audio.stopSound();
			audio = null;
		}
	}

	@Override
	public void invalidate() {

		super.invalidate();

		if(audio != null) {
			audio.stopSound();
			audio = null;
		}
	}

	@Override
	public void setPower(long power) {
		this.power = power;
	}

	@Override
	public long getPower() {
		return this.power;
	}

	@Override
	public long getMaxPower() {
		return this.maxPower;
	}

	AxisAlignedBB bb = null;

	@Override
	public AxisAlignedBB getRenderBoundingBox() {

		if(bb == null) {
			bb = AxisAlignedBB.getBoundingBox(
					xCoord - 5,
					yCoord,
					zCoord - 5,
					xCoord + 6,
					yCoord + 3,
					zCoord + 6
					);
		}

		return bb;
	}

	@Override
	@SideOnly(Side.CLIENT)
	public double getMaxRenderDistanceSquared() {
		return 65536.0D;
	}

	@Override
	public String getName() {
		return "container.turbinegas";
	}

	@Override
	public FluidTank[] getAllTanks() {
		return tanks;
	}

	@Override
	public FluidTank[] getReceivingTanks() {
		return new FluidTank[] { tanks[0], tanks[1], tanks[2] };
	}

	@Override
	public FluidTank[] getSendingTanks() {
		return new FluidTank[] { tanks[3] };
	}

	@Override
	public boolean canConnect(ForgeDirection dir) {
		return dir != ForgeDirection.DOWN;
	}

	@Override
	public boolean canConnect(FluidType type, ForgeDirection dir) {
		return dir != ForgeDirection.DOWN;
	}

	@Override
	@Optional.Method(modid = "OpenComputers")
	public String getComponentName() {
		return "ntm_gas_turbine";
	}

	@Callback(direct = true)
	@Optional.Method(modid = "OpenComputers")
	public Object[] getFluid(Context context, Arguments args) {
		return new Object[] {
				tanks[0].getFill(), tanks[0].getMaxFill(),
				tanks[1].getFill(), tanks[1].getMaxFill(),
				tanks[2].getFill(), tanks[2].getMaxFill(),
				tanks[3].getFill(), tanks[3].getMaxFill()
		};
	}

	@Callback(direct = true)
	@Optional.Method(modid = "OpenComputers")
	public Object[] getType(Context context, Arguments args) {
		return new Object[] {tanks[0].getTankType().getName()};
	}

	@Callback(direct = true)
	@Optional.Method(modid = "OpenComputers")
	public Object[] getPower(Context context, Arguments args) {
		return new Object[] {power};
	}

	@Callback(direct = true)
	@Optional.Method(modid = "OpenComputers")
	public Object[] getThrottle(Context context, Arguments args) {
		return new Object[] {throttle};
	}

	@Callback(direct = true)
	@Optional.Method(modid = "OpenComputers")
	public Object[] getState(Context context, Arguments args) {
		return new Object[] {state};
	}

	@Callback(direct = true)
	@Optional.Method(modid = "OpenComputers")
	public Object[] getAuto(Context context, Arguments args) {
		return new Object[] {autoMode};
	}

	@Callback(direct = true, limit = 4)
	@Optional.Method(modid = "OpenComputers")
	public Object[] setThrottle(Context context, Arguments args) {
		double input = args.checkInteger(0) * 60D / 100D;
		if (input < 0 || input > 100)
			return new Object[] {null, "Input out of range."};
		powerSliderPos = (int) (input);
		return new Object[] {true};
	}

	@Callback(direct = true, limit = 4)
	@Optional.Method(modid = "OpenComputers")
	public Object[] setAuto(Context context, Arguments args) {
		autoMode = args.checkBoolean(0);
		return new Object[] {};
	}

	@Callback(direct = true, limit = 4)
	@Optional.Method(modid = "OpenComputers")
	public Object[] start(Context context, Arguments args) {
		state = -1;
		return new Object[] {};
	}

	@Callback(direct = true, limit = 4)
	@Optional.Method(modid = "OpenComputers")
	public Object[] stop(Context context, Arguments args) {
		state = 0;
		return new Object[] {};
	}

	@Callback(direct = true)
	@Optional.Method(modid = "OpenComputers")
	public Object[] getInfo(Context context, Arguments args) {
		return new Object[] {throttle, state,
				tanks[0].getFill(), tanks[0].getMaxFill(),
				tanks[1].getFill(), tanks[1].getMaxFill(),
				tanks[2].getFill(), tanks[2].getMaxFill(),
				tanks[3].getFill(), tanks[3].getMaxFill()};
	}

	@Override
	@Optional.Method(modid = "OpenComputers")
	public String[] methods() {
		return new String[] {
				"getFluid",
				"getType",
				"getPower",
				"getThrottle",
				"getState",
				"getAuto",
				"setThrottle",
				"setAuto",
				"start",
				"stop",
				"getInfo"
		};
	}

	@Override
	@Optional.Method(modid = "OpenComputers")
	public Object[] invoke(String method, Context context, Arguments args) throws Exception {
		switch(method) {
			case ("getFluid"):
				return getFluid(context, args);
			case ("getType"):
				return getType(context, args);
			case ("getPower"):
				return getPower(context, args);
			case ("getThrottle"):
				return getThrottle(context, args);
			case ("getState"):
				return getState(context, args);
			case ("getAuto"):
				return getAuto(context, args);
			case ("setThrottle"):
				return setThrottle(context, args);
			case ("setAuto"):
				return setAuto(context, args);
			case ("start"):
				return start(context, args);
			case ("stop"):
				return stop(context, args);
			case ("getInfo"):
				return getInfo(context, args);
		}
		throw new NoSuchMethodException();
	}

	@Override
	public Container provideContainer(int ID, EntityPlayer player, World world, int x, int y, int z) {
		return new ContainerMachineTurbineGas(player.inventory, this);
	}

	@Override
	@SideOnly(Side.CLIENT)
	public Object provideGUI(int ID, EntityPlayer player, World world, int x, int y, int z) {
		return new GUIMachineTurbineGas(player.inventory, this);
	}

	@Override
	public void provideExtraInfo(NBTTagCompound data) {
		data.setBoolean(CompatEnergyControl.B_ACTIVE, this.state == 1);
		data.setDouble(CompatEnergyControl.D_HEAT_C, Math.max(20D, this.temp));
		data.setDouble(CompatEnergyControl.D_TURBINE_PERCENT, this.powerSliderPos * 100D / 60D);
		data.setInteger(CompatEnergyControl.I_TURBINE_SPEED, this.rpm);
		data.setDouble(CompatEnergyControl.D_OUTPUT_HE, this.instantPowerOutput);
		data.setDouble(CompatEnergyControl.D_CONSUMPTION_MB, this.waterToBoil);
		data.setDouble(CompatEnergyControl.D_OUTPUT_MB, this.waterToBoil * 10);
	}
}
