package ru.dbotthepony.mc.otm.registry

import net.minecraft.ChatFormatting
import net.minecraft.core.BlockPos
import net.minecraft.network.chat.Component
import net.minecraft.util.valueproviders.UniformInt
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.monster.Zombie
import net.minecraft.world.item.DyeColor
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.item.crafting.RecipeType
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.state.BlockBehaviour
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.properties.BlockSetType
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument
import net.minecraft.world.level.material.MapColor
import net.minecraft.world.level.material.PushReaction
import net.minecraftforge.eventbus.api.IEventBus
import net.minecraftforge.registries.DeferredRegister
import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.block.tech.AndroidStationBlock
import ru.dbotthepony.mc.otm.block.tech.BatteryBankBlock
import ru.dbotthepony.mc.otm.block.BlackHoleBlock
import ru.dbotthepony.mc.otm.block.BlockExplosionDebugger
import ru.dbotthepony.mc.otm.block.tech.BlockGravitationStabilizer
import ru.dbotthepony.mc.otm.block.tech.BlockGravitationStabilizerLens
import ru.dbotthepony.mc.otm.block.BlockSphereDebugger
import ru.dbotthepony.mc.otm.block.tech.ChemicalGeneratorBlock
import ru.dbotthepony.mc.otm.block.tech.EnergyCounterBlock
import ru.dbotthepony.mc.otm.block.tech.EnergyServoBlock
import ru.dbotthepony.mc.otm.block.decorative.LaboratoryLamp
import ru.dbotthepony.mc.otm.block.decorative.LaboratoryLampLight
import ru.dbotthepony.mc.otm.block.MatterCableBlock
import ru.dbotthepony.mc.otm.block.tech.PhantomAttractorBlock
import ru.dbotthepony.mc.otm.block.tech.PlatePressBlock
import ru.dbotthepony.mc.otm.block.StorageCableBlock
import ru.dbotthepony.mc.otm.block.decorative.EngineBlock
import ru.dbotthepony.mc.otm.block.decorative.FluidTankBlock
import ru.dbotthepony.mc.otm.block.decorative.HoloSignBlock
import ru.dbotthepony.mc.otm.block.matter.MatterReconstructorBlock
import ru.dbotthepony.mc.otm.block.matter.MatterBottlerBlock
import ru.dbotthepony.mc.otm.block.matter.MatterCapacitorBankBlock
import ru.dbotthepony.mc.otm.block.matter.MatterDecomposerBlock
import ru.dbotthepony.mc.otm.block.matter.MatterPanelBlock
import ru.dbotthepony.mc.otm.block.matter.MatterRecyclerBlock
import ru.dbotthepony.mc.otm.block.matter.MatterReplicatorBlock
import ru.dbotthepony.mc.otm.block.matter.MatterScannerBlock
import ru.dbotthepony.mc.otm.block.matter.PatternStorageBlock
import ru.dbotthepony.mc.otm.block.storage.DriveRackBlock
import ru.dbotthepony.mc.otm.block.storage.DriveViewerBlock
import ru.dbotthepony.mc.otm.block.storage.ItemMonitorBlock
import ru.dbotthepony.mc.otm.block.storage.StorageBusBlock
import ru.dbotthepony.mc.otm.block.storage.StorageExporterBlock
import ru.dbotthepony.mc.otm.block.storage.StorageImporterBlock
import ru.dbotthepony.mc.otm.block.storage.StoragePowerSupplierBlock
import ru.dbotthepony.mc.otm.block.tech.AndroidChargerBlock
import ru.dbotthepony.mc.otm.block.tech.CobblerBlock
import ru.dbotthepony.mc.otm.block.tech.EssenceStorageBlock
import ru.dbotthepony.mc.otm.block.tech.PoweredFurnaceBlock
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.collect.SupplierList
import ru.dbotthepony.mc.otm.shapes.BlockShapes

object MBlocks {
	private val registry = DeferredRegister.create(ForgeRegistries.BLOCKS, OverdriveThatMatters.MOD_ID)

	internal fun register(bus: IEventBus) {
		registry.register(bus)
	}

	val ANDROID_STATION: Block by registry.register(MNames.ANDROID_STATION) { AndroidStationBlock() }
	val ANDROID_CHARGER: Block by registry.register(MNames.ANDROID_CHARGER) { AndroidChargerBlock() }
	val BATTERY_BANK: Block by registry.register(MNames.BATTERY_BANK) { BatteryBankBlock() }
	val MATTER_DECOMPOSER: Block by registry.register(MNames.MATTER_DECOMPOSER) { MatterDecomposerBlock() }
	val MATTER_CAPACITOR_BANK: Block by registry.register(MNames.MATTER_CAPACITOR_BANK) { MatterCapacitorBankBlock() }
	val MATTER_CABLE: Block by registry.register(MNames.MATTER_CABLE) { MatterCableBlock() }
	val PATTERN_STORAGE: Block by registry.register(MNames.PATTERN_STORAGE) { PatternStorageBlock() }
	val MATTER_SCANNER: Block by registry.register(MNames.MATTER_SCANNER) { MatterScannerBlock() }
	val MATTER_PANEL: Block by registry.register(MNames.MATTER_PANEL) { MatterPanelBlock() }
	val MATTER_REPLICATOR: Block by registry.register(MNames.MATTER_REPLICATOR) { MatterReplicatorBlock() }
	val MATTER_BOTTLER: Block by registry.register(MNames.MATTER_BOTTLER) { MatterBottlerBlock() }
	val ENERGY_COUNTER: Block by registry.register(MNames.ENERGY_COUNTER) { EnergyCounterBlock() }
	val CHEMICAL_GENERATOR: Block by registry.register(MNames.CHEMICAL_GENERATOR) { ChemicalGeneratorBlock() }
	val PLATE_PRESS: Block by registry.register(MNames.PLATE_PRESS) { PlatePressBlock() }
	val TWIN_PLATE_PRESS: Block by registry.register(MNames.TWIN_PLATE_PRESS) { PlatePressBlock(isTwin = true) }
	val POWERED_FURNACE: PoweredFurnaceBlock by registry.register(MNames.POWERED_FURNACE) { PoweredFurnaceBlock(MBlockEntities::POWERED_FURNACE, RecipeType.SMELTING, MachinesConfig.POWERED_FURNACE, BlockShapes.POWERED_FURNACE) }
	val POWERED_BLAST_FURNACE: PoweredFurnaceBlock by registry.register(MNames.POWERED_BLAST_FURNACE) { PoweredFurnaceBlock(MBlockEntities::POWERED_BLAST_FURNACE, RecipeType.BLASTING, MachinesConfig.POWERED_BLAST_FURNACE, BlockShapes.POWERED_BLAST_FURNACE) }
	val POWERED_SMOKER: PoweredFurnaceBlock by registry.register(MNames.POWERED_SMOKER) { PoweredFurnaceBlock(MBlockEntities::POWERED_SMOKER, RecipeType.SMOKING, MachinesConfig.POWERED_SMOKER, null) }
	val MATTER_RECYCLER: Block by registry.register(MNames.MATTER_RECYCLER) { MatterRecyclerBlock() }
	val ENERGY_SERVO: Block by registry.register(MNames.ENERGY_SERVO) { EnergyServoBlock() }
	val COBBLESTONE_GENERATOR: Block by registry.register(MNames.COBBLESTONE_GENERATOR) { CobblerBlock() }
	val ESSENCE_STORAGE: EssenceStorageBlock by registry.register(MNames.ESSENCE_STORAGE) { EssenceStorageBlock() }
	val MATTER_RECONSTRUCTOR: MatterReconstructorBlock by registry.register(MNames.MATTER_RECONSTRUCTOR) { MatterReconstructorBlock() }

	val STORAGE_BUS: Block by registry.register(MNames.STORAGE_BUS) { StorageBusBlock() }
	val STORAGE_IMPORTER: Block by registry.register(MNames.STORAGE_IMPORTER) { StorageImporterBlock() }
	val STORAGE_EXPORTER: Block by registry.register(MNames.STORAGE_EXPORTER) { StorageExporterBlock() }

	val DRIVE_VIEWER: Block by registry.register(MNames.DRIVE_VIEWER) { DriveViewerBlock() }
	val DRIVE_RACK: Block by registry.register(MNames.DRIVE_RACK) { DriveRackBlock() }
	val ITEM_MONITOR: Block by registry.register(MNames.ITEM_MONITOR) { ItemMonitorBlock() }
	val STORAGE_CABLE: Block by registry.register(MNames.STORAGE_CABLE) { StorageCableBlock() }
	val STORAGE_POWER_SUPPLIER: Block by registry.register(MNames.STORAGE_POWER_SUPPLIER) { StoragePowerSupplierBlock() }

	val DEBUG_EXPLOSION_SMALL: Block by registry.register(MNames.DEBUG_EXPLOSION_SMALL) { BlockExplosionDebugger() }
	val DEBUG_SPHERE_POINTS: Block by registry.register(MNames.DEBUG_SPHERE_POINTS) { BlockSphereDebugger() }

	val BLACK_HOLE: Block by registry.register(MNames.BLACK_HOLE) { BlackHoleBlock() }
	val GRAVITATION_STABILIZER: Block by registry.register(MNames.GRAVITATION_STABILIZER) { BlockGravitationStabilizer() }
	val GRAVITATION_STABILIZER_LENS: Block by registry.register(MNames.GRAVITATION_STABILIZER_LENS) { BlockGravitationStabilizerLens() }

	val PHANTOM_ATTRACTOR: Block by registry.register(MNames.PHANTOM_ATTRACTOR) { PhantomAttractorBlock() }
	val FLUID_TANK: FluidTankBlock by registry.register(MNames.FLUID_TANK) { FluidTankBlock() }

	val TRITANIUM_ORE: Block by registry.register(MNames.TRITANIUM_ORE) { DropExperienceBlock(
		BlockBehaviour.Properties.of()
			.mapColor(MapColor.STONE)
			.strength(3.25f, 6.0f)
			.requiresCorrectToolForDrops(),
		UniformInt.of(0, 3)
	) }

	val TRITANIUM_INGOT_BLOCK: Block by registry.register(MNames.TRITANIUM_INGOT_BLOCK) {
		Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BLUE).sound(SoundType.METAL).explosionResistance(400f).destroyTime(3f).requiresCorrectToolForDrops())
	}

	val METAL_JUNK: Block by registry.register(MNames.METAL_JUNK) {
		object : Block(BlockBehaviour.Properties.of().sound(SoundType.NETHERITE_BLOCK).mapColor(MapColor.COLOR_GRAY).explosionResistance(45f).destroyTime(3f).requiresCorrectToolForDrops()) {
			override fun appendHoverText(
				p_49816_: ItemStack,
				p_49817_: BlockGetter?,
				p_49818_: MutableList<Component>,
				p_49819_: TooltipFlag
			) {
				super.appendHoverText(p_49816_, p_49817_, p_49818_, p_49819_)
				p_49818_.add(TranslatableComponent("$descriptionId.desc").withStyle(ChatFormatting.DARK_GRAY))
			}
		}
	}

	val METAL_MESH: Block by registry.register(MNames.METAL_MESH) {
		Block(BlockBehaviour.Properties.of()
				.mapColor(MapColor.COLOR_GRAY)
				.noOcclusion()
				.sound(SoundType.COPPER).explosionResistance(30f)
				.destroyTime(2f).requiresCorrectToolForDrops())
	}

	val TRITANIUM_BARS: IronBarsBlock by registry.register(MNames.TRITANIUM_BARS) {
		IronBarsBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).sound(SoundType.METAL).explosionResistance(45f).destroyTime(2.5f).requiresCorrectToolForDrops())
	}

	val DEEPSLATE_TRITANIUM_ORE: Block by registry.register(MNames.DEEPSLATE_TRITANIUM_ORE) { DropExperienceBlock(
		BlockBehaviour.Properties.of()
			.mapColor(MapColor.DEEPSLATE)
			.sound(SoundType.DEEPSLATE)
			.strength(4.75f, 6.5f)
			.requiresCorrectToolForDrops().sound(SoundType.DEEPSLATE),
		UniformInt.of(0, 3)
	) }

	val TRITANIUM_RAW_BLOCK: Block by registry.register(MNames.TRITANIUM_RAW_BLOCK) { Block(
		BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASEDRUM).strength(8.0f, 10f).requiresCorrectToolForDrops()
	) }

	val LABORATORY_LAMP: Block by registry.register(MNames.LABORATORY_LAMP) { LaboratoryLamp(false) }
	val LABORATORY_LAMP_INVERTED: Block by registry.register(MNames.LABORATORY_LAMP_INVERTED) { LaboratoryLamp(true) }
	val LABORATORY_LAMP_LIGHT: Block by registry.register(MNames.LABORATORY_LAMP_LIGHT) { LaboratoryLampLight() }
	val DANGER_STRIPE_BLOCK: Block by registry.register(MNames.DANGER_STRIPE_BLOCK) { Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_GRAY).explosionResistance(6f).destroyTime(1.5f).requiresCorrectToolForDrops()) }
	val METAL_BEAM: Block by registry.register(MNames.METAL_BEAM) { Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_GRAY).sound(SoundType.METAL).explosionResistance(14f).destroyTime(2.5f).requiresCorrectToolForDrops()) }
	val ENGINE: Block by registry.register(MNames.ENGINE) { EngineBlock() }
	val HOLO_SIGN: Block by registry.register(MNames.HOLO_SIGN) { HoloSignBlock() }

	const val TRITANIUM_ANVIL_VARIANTS = 7

	val TRITANIUM_ANVIL: List<Block>

	init {
		val anvils = ArrayList<() -> Block>()

		for (i in 0 until TRITANIUM_ANVIL_VARIANTS) {
			val props = BlockBehaviour.Properties.of()
				.mapColor(MapColor.COLOR_LIGHT_BLUE)
				.sound(SoundType.ANVIL)
				.destroyTime(2.5f - i * 0.15f)
				.explosionResistance(1200f - i * 80f)
				.pushReaction(PushReaction.BLOCK)
				.requiresCorrectToolForDrops()

			anvils.add(registry.register(MNames.TRITANIUM_ANVIL + i) { AnvilBlock(props) }::get)
		}

		TRITANIUM_ANVIL = SupplierList(anvils)
	}

	val TRITANIUM_DOOR = registry.allColored(MNames.TRITANIUM_DOOR) { color, _ ->
		object : DoorBlock(
			Properties.of()
				.mapColor(color ?: DyeColor.LIGHT_BLUE)
				.explosionResistance(80f)
				.noOcclusion()
				.destroyTime(3f)
				.pushReaction(PushReaction.DESTROY)
				.requiresCorrectToolForDrops(),
			BlockSetType.IRON
		) {
			override fun appendHoverText(
				p_49816_: ItemStack,
				p_49817_: BlockGetter?,
				p_49818_: MutableList<Component>,
				p_49819_: TooltipFlag
			) {
				super.appendHoverText(p_49816_, p_49817_, p_49818_, p_49819_)
				p_49818_.add(TranslatableComponent("$descriptionId.description0").withStyle(ChatFormatting.DARK_GRAY))
				p_49818_.add(TranslatableComponent("$descriptionId.description1").withStyle(ChatFormatting.DARK_GRAY))

				if (color != null) {
					p_49818_.add(TranslatableComponent("$descriptionId.description2").withStyle(ChatFormatting.DARK_GRAY))
				}
			}

			override fun canEntityDestroy(
				state: BlockState,
				level: BlockGetter,
				pos: BlockPos,
				entity: Entity
			): Boolean {
				return entity !is Zombie && super.canEntityDestroy(state, level, pos, entity)
			}
		}
	}

	val TRITANIUM_TRAPDOOR = registry.allColored(MNames.TRITANIUM_TRAPDOOR) { color, _ ->
		object : TrapDoorBlock(
			Properties.of()
				.mapColor(color ?: DyeColor.LIGHT_BLUE)
				.explosionResistance(80f)
				.noOcclusion().destroyTime(3f)
				.requiresCorrectToolForDrops()
				.isValidSpawn { _: BlockState, _: BlockGetter, _: BlockPos, _: EntityType<*>? -> false },
			BlockSetType.IRON
		) {
			override fun appendHoverText(
				p_49816_: ItemStack,
				p_49817_: BlockGetter?,
				p_49818_: MutableList<Component>,
				p_49819_: TooltipFlag
			) {
				super.appendHoverText(p_49816_, p_49817_, p_49818_, p_49819_)
				p_49818_.add(TranslatableComponent("$descriptionId.description0").withStyle(ChatFormatting.DARK_GRAY))
				p_49818_.add(TranslatableComponent("$descriptionId.description1").withStyle(ChatFormatting.DARK_GRAY))

				if (color != null) {
					p_49818_.add(TranslatableComponent("$descriptionId.description2").withStyle(ChatFormatting.DARK_GRAY))
				}
			}

			override fun canEntityDestroy(
				state: BlockState,
				level: BlockGetter,
				pos: BlockPos,
				entity: Entity
			): Boolean {
				return entity !is Zombie && super.canEntityDestroy(state, level, pos, entity)
			}
		} }

	init {
		MRegistry.TRITANIUM_PRESSURE_PLATE.registerBlocks(registry)

		MRegistry.CARGO_CRATES.registerBlocks(registry)
		MRegistry.TRITANIUM_BLOCK.registerBlocks(registry)
		MRegistry.TRITANIUM_STAIRS.registerBlocks(registry)
		MRegistry.TRITANIUM_SLAB.registerBlocks(registry)
		MRegistry.TRITANIUM_WALL.registerBlocks(registry)
	}

	val TRITANIUM_STRIPED_BLOCK: Block by registry.register(MNames.TRITANIUM_STRIPED_BLOCK) { Block(
		BlockBehaviour.Properties.of()
			.mapColor(MapColor.COLOR_LIGHT_BLUE)
			.sound(SoundType.BASALT)
			.requiresCorrectToolForDrops()
			.explosionResistance(80f)
			.strength(4f)
	) }

	val TRITANIUM_STRIPED_STAIRS: Block by registry.register(MNames.TRITANIUM_STRIPED_STAIRS) { StairBlock(
		{ TRITANIUM_STRIPED_BLOCK.defaultBlockState() },
		BlockBehaviour.Properties.copy(TRITANIUM_STRIPED_BLOCK)
	) }

	val TRITANIUM_STRIPED_SLAB: Block by registry.register(MNames.TRITANIUM_STRIPED_SLAB) {
		SlabBlock(BlockBehaviour.Properties.copy(TRITANIUM_STRIPED_BLOCK))
	}

	val TRITANIUM_STRIPED_WALL: Block by registry.register(MNames.TRITANIUM_STRIPED_WALL) {
		WallBlock(BlockBehaviour.Properties.copy(TRITANIUM_STRIPED_BLOCK))
	}

	val CARBON_FIBRE_BLOCK: Block by registry.register(MNames.CARBON_FIBRE_BLOCK) { Block(
		BlockBehaviour.Properties.of()
			.mapColor(MapColor.COLOR_GRAY)
			.sound(SoundType.BASALT)
			.requiresCorrectToolForDrops()
			.explosionResistance(40f)
			.strength(3f)
	) }

	init {
		MRegistry.INDUSTRIAL_GLASS.registerBlocks(registry)
		MRegistry.INDUSTRIAL_GLASS_PANE.registerBlocks(registry)
		MRegistry.UNREFINED_FLOOR_TILES.registerBlocks(registry)
		MRegistry.FLOOR_TILES.registerBlocks(registry)
		MRegistry.FLOOR_TILES_STAIRS.registerBlocks(registry)
		MRegistry.FLOOR_TILES_SLAB.registerBlocks(registry)
		MRegistry.VENT.registerBlocks(registry)
		MRegistry.VENT_ALTERNATIVE.registerBlocks(registry)
		MRegistry.DECORATIVE_CRATE.registerBlocks(registry)
		MRegistry.TRITANIUM_STRIPED_BLOCK.registerBlocks(registry)
		MRegistry.TRITANIUM_STRIPED_STAIRS.registerBlocks(registry)
		MRegistry.TRITANIUM_STRIPED_SLAB.registerBlocks(registry)
		MRegistry.TRITANIUM_STRIPED_WALL.registerBlocks(registry)
	}
}
