package ru.dbotthepony.mc.otm.graph.storage

import it.unimi.dsi.fastutil.objects.ObjectArraySet
import net.minecraft.world.level.block.entity.BlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.graph.GraphNode
import ru.dbotthepony.mc.otm.storage.IStorage

open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null) : GraphNode<StorageNode, StorageGraph>(::StorageGraph) {
	protected val components = ObjectArraySet<IStorage<*>>()

	/**
	 * Setting this to true will render non functional default [attachComponents],
	 * make [addStorageComponent] and [removeStorageComponent] not add/remove components
	 * to attached graph, and [onRevived] not re-attach components.
	 *
	 * [onInvalidated] and [removeComponents] still detach all components
	 */
	protected var manualAttaching = false
	private var demandingEnergy = false

	/**
	 * Called by storage graph on node being attached to it
	 */
	open fun attachComponents(to: StorageGraph) {
		if (energyDemander != null) {
			if (energyDemander.missingPower.isPositive) {
				demandingEnergy = true
				to.powerDemandingNodes.add(energyDemander)
			}
		}

		if (manualAttaching) {
			return
		}

		for (component in components) {
			to.add(component)
		}
	}

	/**
	 * Called by storage graph on node being detached from it
	 *
	 * This is NOT called when graph is being destroyed (e.g. even by merging
	 * with another graph).
	 */
	open fun removeComponents(from: StorageGraph) {
		for (component in components) {
			from.remove(component)
		}

		if (energyDemander != null) {
			from.powerDemandingNodes.remove(energyDemander)
			demandingEnergy = false
		}
	}

	fun tickEnergyDemanding() {
		energyDemander ?: throw IllegalStateException("No energy demander")

		if (!demandingEnergy && energyDemander.missingPower.isPositive) {
			demandingEnergy = true
			graph.powerDemandingNodes.add(energyDemander)
		} else if (demandingEnergy && !energyDemander.missingPower.isPositive) {
			demandingEnergy = false
			graph.powerDemandingNodes.remove(energyDemander)
		}
	}

	fun addStorageComponent(component: IStorage<*>) {
		if (components.add(component))
			if (isValid && !manualAttaching && !isDetached)
				graph.add(component)
	}

	fun removeStorageComponent(component: IStorage<*>) {
		if (components.remove(component) && isValid)
			graph.remove(component)
	}

	var isDetached = false
		set(value) {
			if (field != value) {
				field = value

				if (value) {
					for (component in components) {
						graph.remove(component)
					}
				} else if (!manualAttaching) {
					for (component in components) {
						graph.add(component)
					}
				}
			}
		}

	override fun onInvalidated() {
		for (component in components) {
			graph.remove(component)
		}
	}

	override fun onRevived() {
		if (!manualAttaching) {
			for (component in components) {
				graph.add(component)
			}
		}
	}

	fun discover(blockEntity: BlockEntity) {
		discover(blockEntity, MatteryCapability.STORAGE_NODE)
	}
}
