package ru.dbotthepony.mc.otm.block.entity

import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.neoforged.neoforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
import ru.dbotthepony.kommons.util.Listenable
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set
import java.util.function.Consumer

interface IRedstoneControlled {
	val redstoneControl: AbstractRedstoneControl
}

abstract class AbstractRedstoneControl : INBTSerializable<CompoundTag?>, Listenable<Boolean> {
	abstract var redstoneSetting: RedstoneSetting
	abstract var redstoneSignal: Int
	protected val listeners = Listenable.Impl<Boolean>()

	val isBlockedByRedstone: Boolean get() = !redstoneSetting.test(redstoneSignal)

	final override fun addListener(listener: Consumer<Boolean>): Listenable.L {
		return listeners.addListener(listener)
	}

	override fun serializeNBT(registry: HolderLookup.Provider): CompoundTag {
		return CompoundTag().also {
			it[SETTING_KEY] = redstoneSetting.toString()
			it[SIGNAL_KEY] = redstoneSignal
		}
	}

	override fun deserializeNBT(registry: HolderLookup.Provider, nbt: CompoundTag?) {
		if (nbt == null) {
			redstoneSetting = RedstoneSetting.LOW
			redstoneSignal = 0
			return
		}

		redstoneSetting = nbt.mapString(SETTING_KEY, RedstoneSetting::valueOf, RedstoneSetting.LOW)
		redstoneSignal = nbt.getInt(SIGNAL_KEY)
	}

	companion object {
		const val SETTING_KEY = "RedstoneSetting"
		const val SIGNAL_KEY = "RedstoneSignal"
	}
}

class RedstoneControl(private val valueChanges: (new: Boolean, old: Boolean) -> Unit) : AbstractRedstoneControl() {
	override var redstoneSetting: RedstoneSetting = RedstoneSetting.LOW
		set(level) {
			if (level == field) return
			val old = isBlockedByRedstone
			field = level
			val state = isBlockedByRedstone

			if (state != old) {
				valueChanges.invoke(state, old)
				listeners.accept(state)
			}
		}

	override var redstoneSignal: Int = 0
		set(setting) {
			if (setting == field) return
			val old = isBlockedByRedstone
			field = setting
			val state = isBlockedByRedstone

			if (state != old) {
				valueChanges.invoke(state, old)
				listeners.accept(state)
			}
		}
}

class SynchronizedRedstoneControl(
	synchronizer: SynchableGroup,
	private val valueChanges: (new: Boolean, old: Boolean) -> Unit,
) : AbstractRedstoneControl() {
	override var redstoneSetting: RedstoneSetting by synchronizer.enum(RedstoneSetting.LOW, setter = { access, value ->
		if (access.get() == value) return@enum

		val old = isBlockedByRedstone
		access.accept(value)
		val state = isBlockedByRedstone

		if (state != old) {
			valueChanges.invoke(state, old)
			listeners.accept(state)
		}
	}).delegate

	override var redstoneSignal: Int by synchronizer.int(0, setter = { access, value ->
		if (access.get() == value) return@int

		val old = isBlockedByRedstone
		access.accept(value)
		val state = isBlockedByRedstone

		if (state != old) {
			valueChanges.invoke(state, old)
			listeners.accept(state)
		}
	}).delegate
}
