package ru.dbotthepony.mc.otm.android.feature

import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.item.ItemEntity
import net.minecraftforge.network.NetworkEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.config.AndroidConfig
import ru.dbotthepony.mc.otm.android.AndroidResearchManager
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.ResearchIcons
import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.core.getEntitiesInEllipsoid
import ru.dbotthepony.mc.otm.core.math.RGBAColor
import ru.dbotthepony.mc.otm.core.math.minus
import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.core.position
import ru.dbotthepony.mc.otm.core.math.times
import ru.dbotthepony.mc.otm.network.MatteryPacket
import ru.dbotthepony.mc.otm.network.WorldNetworkChannel
import ru.dbotthepony.mc.otm.network.packetHandled
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
import ru.dbotthepony.mc.otm.registry.MNames
import java.util.UUID
import java.util.WeakHashMap
import java.util.function.Predicate
import java.util.function.Supplier

private data class SharedItemEntityData(val owner: UUID? = null, val age: Int = 0, val lifespan: Int = 0, val hasPickupDelay: Boolean = true) {
	companion object {
		val EMPTY = SharedItemEntityData()
	}
}

private val datatable = WeakHashMap<ItemEntity, SharedItemEntityData>()

class ItemEntityDataPacket(val itemUUID: Int, val owner: UUID? = null, val age: Int = 0, val lifespan: Int = 0, val hasPickupDelay: Boolean = true) : MatteryPacket {
	override fun write(buff: FriendlyByteBuf) {
		buff.writeVarInt(itemUUID)
		buff.writeBoolean(owner != null)
		if (owner != null) buff.writeUUID(owner)
		buff.writeVarInt(age)
		buff.writeVarInt(lifespan)
		buff.writeBoolean(hasPickupDelay)
	}

	override fun play(context: Supplier<NetworkEvent.Context>) {
		context.packetHandled = true
		val level = minecraft.player?.level() as ClientLevel? ?: return
		val entity = level.getEntity(itemUUID) as ItemEntity? ?: return
		datatable[entity] = SharedItemEntityData(owner, age, lifespan, hasPickupDelay)
	}

	companion object {
		fun read(buff: FriendlyByteBuf): ItemEntityDataPacket {
			return ItemEntityDataPacket(buff.readVarInt(), if (buff.readBoolean()) buff.readUUID() else null, buff.readVarInt(), buff.readVarInt(), buff.readBoolean())
		}
	}
}

class ItemMagnetFeature(capability: MatteryPlayerCapability) : AndroidSwitchableFeature(AndroidFeatures.ITEM_MAGNET, capability) {
	private data class ItemPos(var position: Vector, var ticksSinceActivity: Int)
	private val rememberPositions = WeakHashMap<ItemEntity, ItemPos>()

	private val serverPredicate = Predicate<Entity> { it is ItemEntity && !it.hasPickUpDelay() && (it.owner == null || it.owner != ply || it.lifespan - it.age <= 200) }
	private val clientPredicate = Predicate<Entity> { it is ItemEntity && (datatable[it] ?: SharedItemEntityData.EMPTY).let { !it.hasPickupDelay && (it.owner == null || it.owner != ply.uuid || it.lifespan - it.age <= 200) } }

	private fun doTick(server: Boolean) {
		if (ply.isSpectator || server && !android.androidEnergy.extractEnergyExact(AndroidConfig.Magnet.POWER_DRAW, true)) {
			return
		}

		val entities = ply.level().getEntitiesInEllipsoid(
			ply.position,
			Vector(AndroidConfig.Magnet.RADIUS_HORIZONTAL, AndroidConfig.Magnet.RADIUS_VERTICAL, AndroidConfig.Magnet.RADIUS_HORIZONTAL),
			if (server) Predicate<Entity> { it is ItemEntity } else clientPredicate
		)

		for ((ent, distance) in entities) {
			ent as ItemEntity

			if (server) {
				WorldNetworkChannel.send(ply, ItemEntityDataPacket(ent.id, ent.owner?.uuid, ent.age, ent.lifespan, ent.hasPickUpDelay()))

				if (!serverPredicate.test(ent)) {
					continue
				}

				val data = rememberPositions.computeIfAbsent(ent) { ItemPos(it.position, 0) }

				if (data.position.distanceToSqr(ent.position) < 1.0) {
					data.ticksSinceActivity++
				} else {
					if (!android.androidEnergy.extractEnergyExact(AndroidConfig.Magnet.POWER_DRAW, false)) {
						return
					}

					data.position = ent.position
					data.ticksSinceActivity = 0
				}
			}

			ent.deltaMovement += (ply.position - ent.position).normalize() * ((1.0 - distance) * 0.2)
		}
	}

	override fun tickClient() {
		super.tickClient()

		if (!ply.isSpectator && isActive && android.androidEnergy.extractEnergyExact(AndroidConfig.Magnet.POWER_DRAW, true)) {
			doTick(false)
		}
	}

	override fun tickServer() {
		super.tickServer()

		if (isActive) {
			doTick(true)
		}
	}

	override fun renderIcon(graphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float, color: RGBAColor) {
		ResearchIcons.ICON_ITEM_MAGNET.render(graphics, x, y, width, height, color = color)
	}
}
