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

import net.minecraft.nbt.CompoundTag
import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer

interface IRedstoneControlled {
	val redstoneControl: AbstractRedstoneControl
}

abstract class AbstractRedstoneControl : INBTSerializable<CompoundTag?> {
	abstract var redstoneSetting: RedstoneSetting
	abstract var redstoneSignal: Int

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

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

	override fun deserializeNBT(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
			valueChanges.invoke(state, old)
		}

	override var redstoneSignal: Int = 0
		set(setting) {
			if (setting == field) return
			val old = isBlockedByRedstone
			field = setting
			val state = isBlockedByRedstone
			valueChanges.invoke(state, old)
		}
}

class SynchronizedRedstoneControl(
	synchronizer: FieldSynchronizer,
	private val valueChanges: (new: Boolean, old: Boolean) -> Unit,
) : AbstractRedstoneControl() {
	override var redstoneSetting: RedstoneSetting by synchronizer.enum(RedstoneSetting.LOW, setter = { value, access, setByRemote ->
		if (access.read() == value) return@enum
		if (setByRemote) {
			access.write(value)
		} else {
			val old = isBlockedByRedstone
			access.write(value)
			val state = isBlockedByRedstone
			valueChanges.invoke(state, old)
		}
	})

	override var redstoneSignal: Int by synchronizer.int(0, setter = { value, access, setByRemote ->
		if (access.readInt() == value) return@int
		if (setByRemote) {
			access.write(value)
		} else {
			val old = isBlockedByRedstone
			access.write(value)
			val state = isBlockedByRedstone
			valueChanges.invoke(state, old)
		}
	}).property
}
