package ru.dbotthepony.mc.otm.capability.fluid

import net.minecraftforge.fluids.FluidStack
import net.minecraftforge.fluids.capability.IFluidHandler
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.core.isNotEmpty

abstract class AbstractMatteryFluidHandler : IFluidHandler {
	abstract var fluid: FluidStack
	abstract val capacity: Int

	val isEmpty: Boolean
		get() = fluid.isEmpty

	val isNotEmpty: Boolean
		get() = fluid.isNotEmpty

	val isNotFull: Boolean
		get() = fluid.isEmpty || fluid.amount < capacity

	open val direction: FlowDirection
		get() = FlowDirection.BI_DIRECTIONAL

	final override fun getTanks() = 1

	final override fun getFluidInTank(tank: Int): FluidStack {
		require(tank == 0) { "Invalid tank: $tank" }
		return fluid.copy()
	}

	final override fun getTankCapacity(tank: Int): Int {
		require(tank == 0) { "Invalid tank: $tank" }
		return capacity
	}

	protected open fun isFluidValid(stack: FluidStack): Boolean {
		return true
	}

	final override fun isFluidValid(tank: Int, stack: FluidStack): Boolean {
		require(tank == 0) { "Invalid tank: $tank" }
		return isFluidValid(stack)
	}

	override fun fill(resource: FluidStack, action: IFluidHandler.FluidAction): Int {
		if (resource.isEmpty || !isFluidValid(resource) || !direction.input) {
			return 0
		}

		val fluid = fluid

		if (fluid.isEmpty || fluid.isFluidEqual(resource)) {
			val new = (fluid.amount + resource.amount).coerceAtMost(capacity)
			if (new <= fluid.amount) return 0

			if (action.execute()) {
				this.fluid = FluidStack(resource, new)
			}

			return new - fluid.amount
		} else {
			return 0
		}
	}

	override fun drain(resource: FluidStack, action: IFluidHandler.FluidAction): FluidStack {
		if (resource.isEmpty || !direction.output) {
			return FluidStack.EMPTY
		}

		val fluid = fluid

		if (!fluid.isEmpty && fluid.isFluidEqual(resource)) {
			return drain(resource.amount, action)
		} else {
			return FluidStack.EMPTY
		}
	}

	override fun drain(maxDrain: Int, action: IFluidHandler.FluidAction): FluidStack {
		require(maxDrain >= 0) { "Invalid amount to drain: $maxDrain" }
		if (maxDrain == 0 || !direction.output) return FluidStack.EMPTY

		val fluid = fluid

		if (fluid.isEmpty) {
			return FluidStack.EMPTY
		} else {
			val new = (fluid.amount - maxDrain).coerceAtLeast(0)

			if (action.execute()) {
				if (new == 0) {
					this.fluid = FluidStack.EMPTY
				} else {
					this.fluid = FluidStack(fluid, new)
				}
			}

			return FluidStack(fluid, fluid.amount - new)
		}
	}
}
