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

import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlled
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.block.entity.SynchronizedRedstoneControl
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.component3
import ru.dbotthepony.mc.otm.menu.decorative.HoloSignMenu
import ru.dbotthepony.mc.otm.once
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MBlocks

class HoloSignBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBlockEntity(MBlockEntities.HOLO_SIGN, blockPos, blockState), MenuProvider, IRedstoneControlled {
	override val redstoneControl = SynchronizedRedstoneControl(syncher) { _, _ -> setChanged() }

	var signText by syncher.string("", setter = { access, value ->
		markDirtyFast()
		access.accept(value)
	}).delegate

	private fun colorSetter(access: Delegate<Float>, value: Float) {
		if (access.get() != value) {
			access.accept(value)

			if (level is ServerLevel) {
				markDirtyFast()
			} else if (level != null) {
				markDirtyClientside()
			}
		}
	}

	private fun markDirtyClientside() {
		val (x, y, z) = blockPos
		minecraft.levelRenderer.setBlocksDirty(x, y, z, x, y, z)
	}

	var textRed by syncher.float(1f, setter = ::colorSetter).delegate
	var textGreen by syncher.float(1f, setter = ::colorSetter).delegate
	var textBlue by syncher.float(85f / 255f, setter = ::colorSetter).delegate
	var textAlpha by syncher.float(1f).delegate

	var textAutoScale by syncher.boolean(true, setter = { access, value ->
		setChanged()
		access.accept(value)
	}).delegate

	var smallerFont by syncher.boolean(false, setter = { access, value ->
		setChanged()
		access.accept(value)
	}).delegate

	var isLocked = false

	init {
		savetablesConfig.string(::signText)
		savetablesLevel.bool(::isLocked)
		savetablesConfig.stateful(::redstoneControl)

		savetablesConfig.float(::textRed)
		savetablesConfig.float(::textGreen)
		savetablesConfig.float(::textBlue)
		savetablesConfig.float(::textAlpha)

		savetablesConfig.bool(::textAutoScale)
		savetablesConfig.bool(::smallerFont)
	}

	override fun createMenu(p_39954_: Int, p_39955_: Inventory, p_39956_: Player): AbstractContainerMenu {
		return HoloSignMenu(p_39954_, p_39955_, this)
	}

	override fun getDisplayName(): Component {
		return MBlocks.HOLO_SIGN.name
	}

	override fun setLevel(level: Level) {
		super.setLevel(level)

		level.once {
			if (!isRemoved && this.level == level) {
				redstoneControl.redstoneSignal = level.getBestNeighborSignal(blockPos)
			}
		}
	}

	override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) {
		super.loadAdditional(nbt, registry)
		signText = truncate(signText, isLocked)
	}

	companion object {
		const val DEFAULT_MAX_NEWLINES = 16
		const val DEFAULT_MAX_LINE_LENGTH = 60
		const val CREATIVE_MAX_NEWLINES = 60
		const val CREATIVE_MAX_LINE_LENGTH = 240
		private val NEWLINES = Regex("\r?\n")

		fun truncate(input: String, isLocked: Boolean): String {
			val maxLines = if (isLocked) CREATIVE_MAX_NEWLINES else DEFAULT_MAX_NEWLINES
			val maxLength = if (isLocked) CREATIVE_MAX_LINE_LENGTH else DEFAULT_MAX_LINE_LENGTH
			val lines = input.split(NEWLINES)
			val result = ArrayList<String>(lines.size.coerceAtMost(maxLines))

			for (i in 0 until lines.size.coerceAtMost(maxLines)) {
				if (lines[i].length > maxLength) {
					result.add(lines[i].substring(0, maxLength))
				} else {
					result.add(lines[i])
				}
			}

			return result.joinToString("\n")
		}
	}
}
