package ru.dbotthepony.mc.otm.registry.game

import net.minecraft.client.renderer.blockentity.BlockEntityRenderers
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityType
import net.neoforged.bus.api.IEventBus
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent
import ru.dbotthepony.mc.otm.block.entity.*
import ru.dbotthepony.mc.otm.block.entity.tech.*
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleGeneratorBlockEntity
import ru.dbotthepony.mc.otm.block.entity.cable.SimpleEnergyCableBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.DevChestBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.FluidTankBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.GrillBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.HoloSignBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.InfiniteWaterSourceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.PainterBlockEntity
import ru.dbotthepony.mc.otm.block.entity.matter.*
import ru.dbotthepony.mc.otm.block.entity.tech.ItemHatchBlockEntity
import ru.dbotthepony.mc.otm.block.entity.storage.*
import ru.dbotthepony.mc.otm.block.entity.tech.AndroidStationBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.BatteryBankBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.EnergyServoBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.GravitationStabilizerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity
import ru.dbotthepony.mc.otm.client.render.blockentity.*
import ru.dbotthepony.mc.otm.config.CablesConfig
import ru.dbotthepony.mc.otm.core.collect.SupplierMap
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.registry.MRegistry
import java.util.function.Supplier

@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") // Type<*> is unused in BlockEntityType.Builder
object MBlockEntities {
	private val registry = MDeferredRegister(BuiltInRegistries.BLOCK_ENTITY_TYPE)

	private fun <T : BlockEntity> register(name: String, factory: BlockEntityType.BlockEntitySupplier<T>, vararg blocks: Supplier<Block>): MDeferredRegister<*>.Entry<BlockEntityType<T>> {
		return registry.register(name) { BlockEntityType.Builder.of(factory, *blocks.map { it.get() }.toTypedArray()).build(null) }
	}

	private fun <T : BlockEntity> register(name: String, factory: BlockEntityType.BlockEntitySupplier<T>, blocks: Supplier<Block>): MDeferredRegister<*>.Entry<BlockEntityType<T>> {
		return registry.register(name) { BlockEntityType.Builder.of(factory, blocks.get()).build(null) }
	}

	private fun <T : BlockEntity> register(name: String, factory: BlockEntityType.BlockEntitySupplier<T>, blocks: Map<*, Block>): MDeferredRegister<*>.Entry<BlockEntityType<T>> {
		return registry.register(name) { BlockEntityType.Builder.of(factory, *blocks.values.toTypedArray()).build(null) }
	}

	val ANDROID_STATION 		by register(MNames.ANDROID_STATION, ::AndroidStationBlockEntity, MBlocks.ANDROID_STATION)
	val BATTERY_BANK 			by register(MNames.BATTERY_BANK, ::BatteryBankBlockEntity, MBlocks.BATTERY_BANK)
	val MATTER_DECOMPOSER 		by register(MNames.MATTER_DECOMPOSER, ::MatterDecomposerBlockEntity, MBlocks.MATTER_DECOMPOSER)
	val MATTER_CAPACITOR_BANK 	by register(MNames.MATTER_CAPACITOR_BANK, ::MatterCapacitorBankBlockEntity, MBlocks.MATTER_CAPACITOR_BANK)
	val MATTER_CABLE 			by register(MNames.MATTER_CABLE, ::MatterCableBlockEntity, MBlocks::MATTER_CABLE)
	val STORAGE_CABLE 			by register(MNames.STORAGE_CABLE, ::StorageCableBlockEntity, MBlocks::STORAGE_CABLE)
	val PATTERN_STORAGE 		by register(MNames.PATTERN_STORAGE, ::PatternStorageBlockEntity, MBlocks::PATTERN_STORAGE)
	val MATTER_SCANNER 			by register(MNames.MATTER_SCANNER, ::MatterScannerBlockEntity, MBlocks.MATTER_SCANNER)
	val MATTER_PANEL 			by register(MNames.MATTER_PANEL, ::MatterPanelBlockEntity, MBlocks.MATTER_PANEL)
	val MATTER_REPLICATOR 		by register(MNames.MATTER_REPLICATOR, ::MatterReplicatorBlockEntity, MBlocks.MATTER_REPLICATOR)
	val MATTER_BOTTLER 			by register(MNames.MATTER_BOTTLER, ::MatterBottlerBlockEntity, MBlocks.MATTER_BOTTLER)
	val DRIVE_VIEWER 			by register(MNames.DRIVE_VIEWER, ::DriveViewerBlockEntity, MBlocks.DRIVE_VIEWER)
	val BLACK_HOLE 				by register(MNames.BLACK_HOLE, ::BlackHoleBlockEntity, MBlocks::BLACK_HOLE)
	val CARGO_CRATE				by register(MNames.CARGO_CRATE, ::CargoCrateBlockEntity, MRegistry.CARGO_CRATES.allBlocks)
	val DRIVE_RACK 				by register(MNames.DRIVE_RACK, ::DriveRackBlockEntity, MBlocks::DRIVE_RACK)
	val ITEM_MONITOR 			by register(MNames.ITEM_MONITOR, ::ItemMonitorBlockEntity, MBlocks.ITEM_MONITOR)
	val ENERGY_COUNTER 			by register(MNames.ENERGY_COUNTER, ::EnergyCounterBlockEntity, MBlocks.ENERGY_COUNTER)
	val CHEMICAL_GENERATOR 		by register(MNames.CHEMICAL_GENERATOR, ::ChemicalGeneratorBlockEntity, MBlocks.CHEMICAL_GENERATOR)
	val PLATE_PRESS 			by register(MNames.PLATE_PRESS, ::PlatePressBlockEntity, MBlocks.PLATE_PRESS)
	val TWIN_PLATE_PRESS 		by register(MNames.TWIN_PLATE_PRESS, { a, b -> PlatePressBlockEntity(a, b, true) }, MBlocks.TWIN_PLATE_PRESS)
	val GRAVITATION_STABILIZER 	by register(MNames.GRAVITATION_STABILIZER, ::GravitationStabilizerBlockEntity, MBlocks::GRAVITATION_STABILIZER)
	val MATTER_RECYCLER 		by register(MNames.MATTER_RECYCLER, ::MatterRecyclerBlockEntity, MBlocks.MATTER_RECYCLER)
	val ENERGY_SERVO 			by register(MNames.ENERGY_SERVO, ::EnergyServoBlockEntity, MBlocks.ENERGY_SERVO)
	val COBBLESTONE_GENERATOR 	by register(MNames.COBBLESTONE_GENERATOR, ::CobblerBlockEntity, MBlocks.COBBLESTONE_GENERATOR)
	val ESSENCE_STORAGE 		by register(MNames.ESSENCE_STORAGE, ::EssenceStorageBlockEntity, MBlocks.ESSENCE_STORAGE)
	val MATTER_RECONSTRUCTOR 	by register(MNames.MATTER_RECONSTRUCTOR, ::MatterReconstructorBlockEntity, MBlocks.MATTER_RECONSTRUCTOR)
	val FLUID_TANK 				by register(MNames.FLUID_TANK, ::FluidTankBlockEntity, MBlocks::FLUID_TANK)
	val ANDROID_CHARGER 		by register(MNames.ANDROID_CHARGER, ::AndroidChargerBlockEntity, MBlocks.ANDROID_CHARGER)
	val ANDROID_CHARGER_MIDDLE 	by register(MNames.ANDROID_CHARGER + "_middle", ::AndroidChargerMiddleBlockEntity, MBlocks.ANDROID_CHARGER)
	val ANDROID_CHARGER_TOP 	by register(MNames.ANDROID_CHARGER + "_top", ::AndroidChargerTopBlockEntity, MBlocks.ANDROID_CHARGER)
	val INFINITE_WATER_SOURCE 	by register(MNames.INFINITE_WATER_SOURCE, ::InfiniteWaterSourceBlockEntity, MBlocks::INFINITE_WATER_SOURCE)
	val DEV_CHEST 				by register(MNames.DEV_CHEST, ::DevChestBlockEntity, MBlocks::DEV_CHEST)
	val PAINTER 				by register(MNames.PAINTER, ::PainterBlockEntity, MBlocks::PAINTER)
	val MATTER_ENTANGLER 		by register(MNames.MATTER_ENTANGLER, ::MatterEntanglerBlockEntity, MBlocks::MATTER_ENTANGLER)
	val BLACK_HOLE_GENERATOR 	by register(MNames.BLACK_HOLE_GENERATOR, ::BlackHoleGeneratorBlockEntity, MBlocks::BLACK_HOLE_GENERATOR)
	val ITEM_INPUT_HATCH 		by register(MNames.ITEM_INPUT_HATCH, ItemHatchBlockEntity::input, MBlocks::ITEM_INPUT_HATCH)
	val ITEM_OUTPUT_HATCH 		by register(MNames.ITEM_OUTPUT_HATCH, ItemHatchBlockEntity::output, MBlocks::ITEM_OUTPUT_HATCH)
	val ENERGY_INPUT_HATCH 		by register(MNames.ENERGY_INPUT_HATCH, EnergyHatchBlockEntity::input, MBlocks::ENERGY_INPUT_HATCH)
	val ENERGY_OUTPUT_HATCH 	by register(MNames.ENERGY_OUTPUT_HATCH, EnergyHatchBlockEntity::output, MBlocks::ENERGY_OUTPUT_HATCH)
	val MATTER_INPUT_HATCH 		by register(MNames.MATTER_INPUT_HATCH, MatterHatchBlockEntity::input, MBlocks::MATTER_INPUT_HATCH)
	val MATTER_OUTPUT_HATCH 	by register(MNames.MATTER_OUTPUT_HATCH, MatterHatchBlockEntity::output, MBlocks::MATTER_OUTPUT_HATCH)
	val ENERGY_INPUT_INTERFACE 	by register(MNames.ENERGY_INPUT_INTERFACE, EnergyInterfaceBlockEntity::input, MBlocks::ENERGY_INPUT_INTERFACE)
	val ENERGY_OUTPUT_INTERFACE by register(MNames.ENERGY_OUTPUT_INTERFACE, EnergyInterfaceBlockEntity::output, MBlocks::ENERGY_OUTPUT_INTERFACE)

	val POWERED_FURNACE 		by register(MNames.POWERED_FURNACE, ::PoweredFurnaceBlockEntity, MBlocks.POWERED_FURNACE)
	val POWERED_BLAST_FURNACE 	by register(MNames.POWERED_BLAST_FURNACE, ::PoweredBlastFurnaceBlockEntity, MBlocks.POWERED_BLAST_FURNACE)
	val POWERED_SMOKER 			by register(MNames.POWERED_SMOKER, ::PoweredSmokerBlockEntity, MBlocks.POWERED_SMOKER)

	val FLYWHEEL_BATTERY		by register(MNames.FLYWHEEL_BATTERY, ::FlywheelBatteryBlockEntity, MBlocks::FLYWHEEL_BATTERY)

	val MULTIBLOCK_TEST 		by register("multiblock_test", ::MultiblockTestBlockEntity, MBlocks::MULTIBLOCK_TEST)

	val ENERGY_CABLES: Map<CablesConfig.E, BlockEntityType<SimpleEnergyCableBlockEntity>> = SupplierMap(CablesConfig.E.entries.map { conf ->
		var selfFeed: Supplier<BlockEntityType<SimpleEnergyCableBlockEntity>> = Supplier { TODO() }
		selfFeed = register("${conf.name.lowercase()}_energy_cable", { a, b -> SimpleEnergyCableBlockEntity(selfFeed.get(), a, b, conf) }, { MBlocks.ENERGY_CABLES[conf]!! })
		conf to selfFeed
	})

	val STORAGE_BUS				by register(MNames.STORAGE_BUS, ::StorageBusBlockEntity, MBlocks::STORAGE_BUS)
	val STORAGE_IMPORTER		by register(MNames.STORAGE_IMPORTER, ::StorageImporterBlockEntity, MBlocks::STORAGE_IMPORTER)
	val STORAGE_EXPORTER		by register(MNames.STORAGE_EXPORTER, ::StorageExporterBlockEntity, MBlocks::STORAGE_EXPORTER)
	val STORAGE_POWER_SUPPLIER	by register(MNames.STORAGE_POWER_SUPPLIER, ::StoragePowerSupplierBlockEntity, MBlocks.STORAGE_POWER_SUPPLIER)

	val GRILL					by register(MNames.GRILL, ::GrillBlockEntity, MBlocks.GRILL)

	val HOLO_SIGN: BlockEntityType<HoloSignBlockEntity> by registry.register(MNames.HOLO_SIGN) { BlockEntityType.Builder.of(::HoloSignBlockEntity, MBlocks.HOLO_SIGN).build(null) }

	fun register(bus: IEventBus) {
		registry.register(bus)
		bus.addListener(this::registerClient)
	}

	private fun registerClient(event: FMLClientSetupEvent) {
		BlockEntityRenderers.register(BLACK_HOLE, ::BlackHoleRenderer)
		BlockEntityRenderers.register(BLACK_HOLE_GENERATOR, ::BlackHoleGeneratorRenderer)
		BlockEntityRenderers.register(GRAVITATION_STABILIZER, ::GravitationStabilizerRenderer)
		BlockEntityRenderers.register(ENERGY_COUNTER, ::EnergyCounterRenderer)
		BlockEntityRenderers.register(BATTERY_BANK, ::BatteryBankRenderer)
		BlockEntityRenderers.register(MATTER_CAPACITOR_BANK, ::MatterBatteryBankRenderer)
		BlockEntityRenderers.register(MATTER_RECONSTRUCTOR, ::MatterReconstructorRenderer)
		BlockEntityRenderers.register(MATTER_REPLICATOR, ::MatterReplicatorRenderer)
		BlockEntityRenderers.register(MATTER_SCANNER, ::MatterScannerRenderer)
		BlockEntityRenderers.register(POWERED_SMOKER, ::PoweredSmokerRenderer)
		BlockEntityRenderers.register(HOLO_SIGN, ::HoloSignRenderer)
		BlockEntityRenderers.register(FLUID_TANK, ::FluidTankRenderer)
	}
}
