package ru.dbotthepony.mc.otm.client.render.blockentity

import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.client.resources.model.BakedModel
import net.minecraft.core.Direction
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.level.levelgen.XoroshiroRandomSource
import net.minecraftforge.client.event.ModelEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.matter.MatterCapacitorBankBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.BatteryBankBlockEntity
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
import ru.dbotthepony.mc.otm.client.render.DynamicBufferSource
import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.math.BlockRotationFreedom
import ru.dbotthepony.mc.otm.core.math.RGBAColor
import ru.dbotthepony.mc.otm.core.math.facingOne
import ru.dbotthepony.mc.otm.core.math.rotate
import ru.dbotthepony.mc.otm.core.math.rotateY
import ru.dbotthepony.mc.otm.nanoTime
import java.util.function.BooleanSupplier
import java.util.function.Supplier
import kotlin.math.PI

abstract class BankRenderer<T : MatteryDeviceBlockEntity>(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<T> {
	protected abstract fun gaugeLevel(entity: T): Float
	protected abstract val texture: AbstractMatterySprite
	protected abstract val models: List<BakedModel>
	protected abstract fun status(entity: T): List<BooleanSupplier>

	private val random = XoroshiroRandomSource(nanoTime)

	override fun render(
		blockEntity: T,
		p_112308_: Float,
		stack: PoseStack,
		p_112310_: MultiBufferSource,
		p_112311_: Int,
		p_112312_: Int
	) {
		val status = status(blockEntity)

		stack.pushPose()
		stack.translate(0.5f, 0.5f, 0.5f)
		stack.rotate(blockEntity.blockState[BlockRotationFreedom.ONE.property].front)
		stack.translate(-0.5f, -0.5f, -0.5f)

		for ((i, model) in models.withIndex()) {
			if (!status[i].asBoolean) {
				continue
			}

			val buffer = DynamicBufferSource.WORLD.getBuffer(RenderType.solid())

			for (quad in model.getQuads(null, null, random))
				buffer.putBulkData(stack.last(), quad, 1f, 1f, 1f, p_112311_, p_112312_)
		}

		stack.popPose()

		val plainLevel = gaugeLevel(blockEntity)

		if (plainLevel <= 0f) {
			return
		}

		stack.pushPose()

		val facing = blockEntity.blockState.facingOne
		val rotateFacing = facing == Direction.NORTH || facing == Direction.SOUTH

		if (rotateFacing) {
			stack.rotateY(PI.toFloat() / 2f)
		}

		stack.scale(0.02f, 0.01f, 0.01f)
		stack.translate(if (rotateFacing) -50.0 else 0.0, 0.0, -0.5)

		// ~50x100 canvas

		val width = 9f
		val heightMax = 39.36f

		val buffer = DynamicBufferSource.WORLD.getBuffer(texture.renderTypeWorld)

		texture.uploadOntoPartial(
			stack,
			buffer,
			x = 25f - width / 2f,
			y = 30f,
			width = width,
			height = heightMax * gaugeLevel(blockEntity),
			color = RGBAColor.WHITE
		)

		stack.rotateY(PI.toFloat())
		stack.translate(-50.0, 0.0, -101.0)

		texture.uploadOntoPartial(
			stack,
			buffer,
			x = 25f - width / 2f,
			y = 30f,
			width = width,
			height = heightMax * gaugeLevel(blockEntity),
			color = RGBAColor.WHITE
		)

		stack.popPose()
	}
}

class BatteryBankRenderer(context: BlockEntityRendererProvider.Context) : BankRenderer<BatteryBankBlockEntity>(context) {
	override val models: List<BakedModel> by lazy {
		immutableList(12) {
			minecraft.modelManager.modelBakery.bakedTopLevelModels[ResourceLocation(OverdriveThatMatters.MOD_ID, "block/battery/battery$it")]!!
		}
	}

	override fun status(entity: BatteryBankBlockEntity): List<BooleanSupplier> {
		return entity.batteryStatus
	}

	override fun gaugeLevel(entity: BatteryBankBlockEntity): Float {
		return entity.gaugeLevel
	}

	override val texture: AbstractMatterySprite
		get() = PowerGaugePanel.GAUGE_FOREGROUND

	companion object {
		fun onRegisterAdditionalModels(event: ModelEvent.RegisterAdditional) {
			for (i in 0 .. 11) {
				event.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "block/battery/battery$i"))
			}
		}
	}
}

class MatterBatteryBankRenderer(context: BlockEntityRendererProvider.Context) : BankRenderer<MatterCapacitorBankBlockEntity>(context) {
	override val models: List<BakedModel> by lazy {
		immutableList(12) {
			minecraft.modelManager.modelBakery.bakedTopLevelModels[ResourceLocation(OverdriveThatMatters.MOD_ID, "block/battery/matter_capacitor$it")]!!
		}
	}

	override fun status(entity: MatterCapacitorBankBlockEntity): List<BooleanSupplier> {
		return entity.capacitorStatus
	}

	override fun gaugeLevel(entity: MatterCapacitorBankBlockEntity): Float {
		return entity.gaugeLevel
	}

	override val texture: AbstractMatterySprite
		get() = MatterGaugePanel.GAUGE_FOREGROUND

	companion object {
		fun onRegisterAdditionalModels(event: ModelEvent.RegisterAdditional) {
			for (i in 0 .. 11) {
				event.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "block/battery/matter_capacitor$i"))
			}
		}
	}
}

