package com.tiedup.remake.worldgen;

import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.LanternBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.levelgen.structure.BoundingBox;

enum RoomTheme {
    OUBLIETTE {
        @Override
        public BlockState wallBlock(RandomSource r, int ry) {
            if (
                ry == 1 && r.nextFloat() < 0.30f
            ) return Blocks.MOSSY_COBBLESTONE.defaultBlockState();
            return r.nextFloat() < 0.20f
                ? Blocks.CRACKED_DEEPSLATE_BRICKS.defaultBlockState()
                : Blocks.DEEPSLATE_BRICKS.defaultBlockState();
        }

        @Override
        public BlockState floorBlock(
            RandomSource r,
            int rx,
            int rz,
            boolean isEdge
        ) {
            boolean isCorner = (rx == 1 || rx == 11) && (rz == 1 || rz == 11);
            if (isCorner) return Blocks.MOSSY_COBBLESTONE.defaultBlockState();
            return r.nextFloat() < 0.15f
                ? Blocks.COBBLESTONE.defaultBlockState()
                : Blocks.DEEPSLATE_TILES.defaultBlockState();
        }

        @Override
        public BlockState ceilingBlock(RandomSource r) {
            return r.nextFloat() < 0.20f
                ? Blocks.CRACKED_DEEPSLATE_BRICKS.defaultBlockState()
                : Blocks.DEEPSLATE_BRICKS.defaultBlockState();
        }

        @Override
        public BlockState wallShellBlock() {
            return Blocks.DEEPSLATE_BRICKS.defaultBlockState();
        }

        @Override
        public void placeDecorations(
            WorldGenLevel level,
            RandomSource random,
            int baseX,
            int baseZ,
            int floorY,
            RoomLayout layout,
            BoundingBox chunkBB
        ) {
            int[][] corners = layout.innerCorners();
            // Cobwebs at corners (low + high)
            for (int[] c : corners) {
                if (
                    layout.isInShape(c[0], c[1]) && !layout.isWall(c[0], c[1])
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + c[0], floorY + 1, baseZ + c[1]),
                        Blocks.COBWEB.defaultBlockState(),
                        chunkBB
                    );
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + c[0], floorY + 9, baseZ + c[1]),
                        Blocks.COBWEB.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // Water cauldron near first corner
            int cx = corners[0][0] + 1,
                cz = corners[0][1] + 1;
            if (layout.isInShape(cx, cz) && !layout.isWall(cx, cz)) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + cx, floorY + 1, baseZ + cz),
                    Blocks.WATER_CAULDRON.defaultBlockState().setValue(
                        BlockStateProperties.LEVEL_CAULDRON,
                        3
                    ),
                    chunkBB
                );
            }
            // Soul lanterns on wall midpoints
            for (int[] wallPos : new int[][] {
                { 6, 1 },
                { 6, 11 },
                { 1, 6 },
                { 11, 6 },
            }) {
                if (layout.isWall(wallPos[0], wallPos[1])) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(
                            baseX + wallPos[0],
                            floorY + 3,
                            baseZ + wallPos[1]
                        ),
                        Blocks.SOUL_LANTERN.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // Corner furniture: barrel + brewing_stand + chain
            int[] fc = corners[1];
            int fcx = fc[0] + (fc[0] < 6 ? 1 : -1);
            int fcz = fc[1] + (fc[1] < 6 ? 1 : -1);
            if (layout.isInShape(fcx, fcz) && !layout.isWall(fcx, fcz)) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + fcx, floorY + 1, baseZ + fcz),
                    Blocks.BARREL.defaultBlockState(),
                    chunkBB
                );
                int fcx2 = fcx + (fcx < 6 ? 1 : -1);
                if (layout.isInShape(fcx2, fcz) && !layout.isWall(fcx2, fcz)) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + fcx2, floorY + 1, baseZ + fcz),
                        Blocks.BREWING_STAND.defaultBlockState(),
                        chunkBB
                    );
                }
                // Chain above barrel
                for (int cy = 8; cy <= 10; cy++) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + fcx, floorY + cy, baseZ + fcz),
                        Blocks.CHAIN.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // Side chains
            placeSharedChains(level, baseX, baseZ, floorY, chunkBB);
            // Hanging lanterns
            placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB);
        }

        @Override
        public BlockState pillarBlock(RandomSource r, int ry) {
            if (
                ry == 1 || ry == 10
            ) return Blocks.POLISHED_DEEPSLATE.defaultBlockState();
            return Blocks.DEEPSLATE_BRICK_WALL.defaultBlockState();
        }

        @Override
        @Nullable
        public BlockState scatterBlock(RandomSource r) {
            float f = r.nextFloat();
            if (f < 0.40f) return Blocks.COBWEB.defaultBlockState();
            if (f < 0.70f) return Blocks.CANDLE.defaultBlockState()
                .setValue(BlockStateProperties.CANDLES, 1 + r.nextInt(3))
                .setValue(BlockStateProperties.LIT, true);
            return Blocks.MOSS_CARPET.defaultBlockState();
        }

        @Override
        public BlockState wallAccentBlock() {
            return Blocks.POLISHED_DEEPSLATE.defaultBlockState();
        }
    },
    INFERNO {
        @Override
        public BlockState wallBlock(RandomSource r, int ry) {
            return r.nextFloat() < 0.20f
                ? Blocks.CRACKED_NETHER_BRICKS.defaultBlockState()
                : Blocks.NETHER_BRICKS.defaultBlockState();
        }

        @Override
        public BlockState floorBlock(
            RandomSource r,
            int rx,
            int rz,
            boolean isEdge
        ) {
            if (isEdge) return Blocks.MAGMA_BLOCK.defaultBlockState();
            return r.nextFloat() < 0.08f
                ? Blocks.GILDED_BLACKSTONE.defaultBlockState()
                : Blocks.BLACKSTONE.defaultBlockState();
        }

        @Override
        public BlockState ceilingBlock(RandomSource r) {
            return r.nextFloat() < 0.20f
                ? Blocks.CRACKED_NETHER_BRICKS.defaultBlockState()
                : Blocks.NETHER_BRICKS.defaultBlockState();
        }

        @Override
        public BlockState wallShellBlock() {
            return Blocks.NETHER_BRICKS.defaultBlockState();
        }

        @Override
        public void placeDecorations(
            WorldGenLevel level,
            RandomSource random,
            int baseX,
            int baseZ,
            int floorY,
            RoomLayout layout,
            BoundingBox chunkBB
        ) {
            int[][] corners = layout.innerCorners();
            // Soul fire at corners: soul_sand at ry=0, soul_fire at ry=1
            for (int[] c : corners) {
                if (
                    layout.isInShape(c[0], c[1]) && !layout.isWall(c[0], c[1])
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + c[0], floorY, baseZ + c[1]),
                        Blocks.SOUL_SAND.defaultBlockState(),
                        chunkBB
                    );
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + c[0], floorY + 1, baseZ + c[1]),
                        Blocks.SOUL_FIRE.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // Crying obsidian accents at wall midpoints
            for (int[] wallPos : new int[][] {
                { 6, 1 },
                { 6, 11 },
                { 1, 6 },
                { 11, 6 },
            }) {
                if (layout.isWall(wallPos[0], wallPos[1])) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(
                            baseX + wallPos[0],
                            floorY + 2,
                            baseZ + wallPos[1]
                        ),
                        Blocks.CRYING_OBSIDIAN.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // Soul lanterns on wall midpoints
            for (int[] wallPos : new int[][] {
                { 6, 1 },
                { 6, 11 },
                { 1, 6 },
                { 11, 6 },
            }) {
                if (layout.isWall(wallPos[0], wallPos[1])) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(
                            baseX + wallPos[0],
                            floorY + 3,
                            baseZ + wallPos[1]
                        ),
                        Blocks.SOUL_LANTERN.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // Corner furniture: soul_campfire + cauldron(lava) + gilded_blackstone
            int[][] icorners = layout.innerCorners();
            int[] ifc = icorners[1];
            int ifcx = ifc[0] + (ifc[0] < 6 ? 1 : -1);
            int ifcz = ifc[1] + (ifc[1] < 6 ? 1 : -1);
            if (layout.isInShape(ifcx, ifcz) && !layout.isWall(ifcx, ifcz)) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + ifcx, floorY + 1, baseZ + ifcz),
                    Blocks.SOUL_CAMPFIRE.defaultBlockState(),
                    chunkBB
                );
                int ifcx2 = ifcx + (ifcx < 6 ? 1 : -1);
                if (
                    layout.isInShape(ifcx2, ifcz) && !layout.isWall(ifcx2, ifcz)
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + ifcx2, floorY + 1, baseZ + ifcz),
                        Blocks.LAVA_CAULDRON.defaultBlockState(),
                        chunkBB
                    );
                }
                if (
                    layout.isInShape(ifcx, ifcz + (ifcz < 6 ? 1 : -1)) &&
                    !layout.isWall(ifcx, ifcz + (ifcz < 6 ? 1 : -1))
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(
                            baseX + ifcx,
                            floorY + 1,
                            baseZ + ifcz + (ifcz < 6 ? 1 : -1)
                        ),
                        Blocks.GILDED_BLACKSTONE.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // Side chains
            placeSharedChains(level, baseX, baseZ, floorY, chunkBB);
            // Hanging lanterns
            placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB);
        }

        @Override
        public BlockState pillarBlock(RandomSource r, int ry) {
            if (
                ry == 1 || ry == 10
            ) return Blocks.QUARTZ_PILLAR.defaultBlockState();
            return Blocks.NETHER_BRICK_WALL.defaultBlockState();
        }

        @Override
        @Nullable
        public BlockState scatterBlock(RandomSource r) {
            float f = r.nextFloat();
            if (f < 0.40f) return Blocks.SOUL_SAND.defaultBlockState();
            if (f < 0.70f) return Blocks.NETHER_WART_BLOCK.defaultBlockState();
            return Blocks.BONE_BLOCK.defaultBlockState();
        }

        @Override
        public BlockState wallAccentBlock() {
            return Blocks.RED_NETHER_BRICKS.defaultBlockState();
        }
    },
    CRYPT {
        @Override
        public BlockState wallBlock(RandomSource r, int ry) {
            float f = r.nextFloat();
            if (
                ry == 1 && f < 0.20f
            ) return Blocks.MOSSY_COBBLESTONE.defaultBlockState();
            if (f < 0.15f) return Blocks.MOSSY_STONE_BRICKS.defaultBlockState();
            if (
                f < 0.30f
            ) return Blocks.CRACKED_STONE_BRICKS.defaultBlockState();
            return Blocks.STONE_BRICKS.defaultBlockState();
        }

        @Override
        public BlockState floorBlock(
            RandomSource r,
            int rx,
            int rz,
            boolean isEdge
        ) {
            boolean isCorner = (rx == 1 || rx == 11) && (rz == 1 || rz == 11);
            if (isCorner) return Blocks.MOSSY_COBBLESTONE.defaultBlockState();
            return r.nextFloat() < 0.20f
                ? Blocks.COBBLESTONE.defaultBlockState()
                : Blocks.STONE_BRICKS.defaultBlockState();
        }

        @Override
        public BlockState ceilingBlock(RandomSource r) {
            return r.nextFloat() < 0.20f
                ? Blocks.CRACKED_STONE_BRICKS.defaultBlockState()
                : Blocks.STONE_BRICKS.defaultBlockState();
        }

        @Override
        public BlockState wallShellBlock() {
            return Blocks.STONE_BRICKS.defaultBlockState();
        }

        @Override
        public void placeDecorations(
            WorldGenLevel level,
            RandomSource random,
            int baseX,
            int baseZ,
            int floorY,
            RoomLayout layout,
            BoundingBox chunkBB
        ) {
            int[][] corners = layout.innerCorners();
            // Cobwebs at corners (low + high)
            for (int[] c : corners) {
                if (
                    layout.isInShape(c[0], c[1]) && !layout.isWall(c[0], c[1])
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + c[0], floorY + 1, baseZ + c[1]),
                        Blocks.COBWEB.defaultBlockState(),
                        chunkBB
                    );
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + c[0], floorY + 9, baseZ + c[1]),
                        Blocks.COBWEB.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // Wall torches at midpoints
            for (int[] wallPos : new int[][] {
                { 6, 0 },
                { 6, 12 },
                { 0, 6 },
                { 12, 6 },
            }) {
                if (layout.isWall(wallPos[0], wallPos[1])) {
                    Direction torchDir = getTorchDirection(
                        wallPos[0],
                        wallPos[1]
                    );
                    if (torchDir != null) {
                        HangingCagePiece.safeSetBlock(
                            level,
                            new BlockPos(
                                baseX + wallPos[0],
                                floorY + 3,
                                baseZ + wallPos[1]
                            ),
                            Blocks.WALL_TORCH.defaultBlockState().setValue(
                                net.minecraft.world.level.block.WallTorchBlock.FACING,
                                torchDir
                            ),
                            chunkBB
                        );
                    }
                }
            }
            // Skull in first corner
            int[] sc = corners[0];
            if (
                layout.isInShape(sc[0], sc[1]) && !layout.isWall(sc[0], sc[1])
            ) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + sc[0], floorY + 1, baseZ + sc[1]),
                    Blocks.SKELETON_SKULL.defaultBlockState(),
                    chunkBB
                );
            }
            // Corner furniture: lectern + candles on floor
            int[] cfc = corners[1];
            int cfcx = cfc[0] + (cfc[0] < 6 ? 1 : -1);
            int cfcz = cfc[1] + (cfc[1] < 6 ? 1 : -1);
            if (layout.isInShape(cfcx, cfcz) && !layout.isWall(cfcx, cfcz)) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + cfcx, floorY + 1, baseZ + cfcz),
                    Blocks.LECTERN.defaultBlockState(),
                    chunkBB
                );
                int cfcx2 = cfcx + (cfcx < 6 ? 1 : -1);
                if (
                    layout.isInShape(cfcx2, cfcz) && !layout.isWall(cfcx2, cfcz)
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + cfcx2, floorY + 1, baseZ + cfcz),
                        Blocks.CANDLE.defaultBlockState()
                            .setValue(BlockStateProperties.CANDLES, 4)
                            .setValue(BlockStateProperties.LIT, true),
                        chunkBB
                    );
                }
            }
            placeSharedChains(level, baseX, baseZ, floorY, chunkBB);
            placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB);
        }

        @Override
        public BlockState pillarBlock(RandomSource r, int ry) {
            if (
                ry == 1 || ry == 10
            ) return Blocks.CHISELED_STONE_BRICKS.defaultBlockState();
            return Blocks.STONE_BRICK_WALL.defaultBlockState();
        }

        @Override
        @Nullable
        public BlockState scatterBlock(RandomSource r) {
            float f = r.nextFloat();
            if (f < 0.40f) return Blocks.COBWEB.defaultBlockState();
            if (f < 0.70f) return Blocks.CANDLE.defaultBlockState()
                .setValue(BlockStateProperties.CANDLES, 1 + r.nextInt(3))
                .setValue(BlockStateProperties.LIT, true);
            return Blocks.BONE_BLOCK.defaultBlockState();
        }

        @Override
        public BlockState wallAccentBlock() {
            return Blocks.MOSSY_STONE_BRICKS.defaultBlockState();
        }
    },
    ICE {
        @Override
        public BlockState wallBlock(RandomSource r, int ry) {
            float f = r.nextFloat();
            if (f < 0.10f) return Blocks.ICE.defaultBlockState();
            if (f < 0.30f) return Blocks.BLUE_ICE.defaultBlockState();
            return Blocks.PACKED_ICE.defaultBlockState();
        }

        @Override
        public BlockState floorBlock(
            RandomSource r,
            int rx,
            int rz,
            boolean isEdge
        ) {
            if (isEdge) return Blocks.BLUE_ICE.defaultBlockState();
            return r.nextFloat() < 0.20f
                ? Blocks.SNOW_BLOCK.defaultBlockState()
                : Blocks.PACKED_ICE.defaultBlockState();
        }

        @Override
        public BlockState ceilingBlock(RandomSource r) {
            return r.nextFloat() < 0.20f
                ? Blocks.BLUE_ICE.defaultBlockState()
                : Blocks.PACKED_ICE.defaultBlockState();
        }

        @Override
        public BlockState wallShellBlock() {
            return Blocks.PACKED_ICE.defaultBlockState();
        }

        @Override
        public void placeDecorations(
            WorldGenLevel level,
            RandomSource random,
            int baseX,
            int baseZ,
            int floorY,
            RoomLayout layout,
            BoundingBox chunkBB
        ) {
            int[][] corners = layout.innerCorners();
            // Snow layers in corners
            for (int[] c : corners) {
                if (
                    layout.isInShape(c[0], c[1]) && !layout.isWall(c[0], c[1])
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + c[0], floorY + 1, baseZ + c[1]),
                        Blocks.SNOW.defaultBlockState().setValue(
                            net.minecraft.world.level.block.SnowLayerBlock.LAYERS,
                            2 + random.nextInt(3)
                        ),
                        chunkBB
                    );
                }
            }
            // Lanterns on wall midpoints
            for (int[] wallPos : new int[][] {
                { 6, 1 },
                { 6, 11 },
                { 1, 6 },
                { 11, 6 },
            }) {
                if (layout.isWall(wallPos[0], wallPos[1])) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(
                            baseX + wallPos[0],
                            floorY + 3,
                            baseZ + wallPos[1]
                        ),
                        Blocks.LANTERN.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // Ice stalactites near ceiling corners
            for (int[] c : corners) {
                int cx = c[0],
                    cz = c[1];
                if (layout.isInShape(cx, cz) && !layout.isWall(cx, cz)) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + cx, floorY + 10, baseZ + cz),
                        Blocks.ICE.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // Corner furniture: cauldron(powder_snow) + blue_ice seat + lantern
            int[] ifc2 = corners[1];
            int ifc2x = ifc2[0] + (ifc2[0] < 6 ? 1 : -1);
            int ifc2z = ifc2[1] + (ifc2[1] < 6 ? 1 : -1);
            if (
                layout.isInShape(ifc2x, ifc2z) && !layout.isWall(ifc2x, ifc2z)
            ) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + ifc2x, floorY + 1, baseZ + ifc2z),
                    Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(
                        BlockStateProperties.LEVEL_CAULDRON,
                        3
                    ),
                    chunkBB
                );
                int ifc2x2 = ifc2x + (ifc2x < 6 ? 1 : -1);
                if (
                    layout.isInShape(ifc2x2, ifc2z) &&
                    !layout.isWall(ifc2x2, ifc2z)
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + ifc2x2, floorY + 1, baseZ + ifc2z),
                        Blocks.BLUE_ICE.defaultBlockState(),
                        chunkBB
                    );
                }
                if (
                    layout.isInShape(ifc2x, ifc2z + (ifc2z < 6 ? 1 : -1)) &&
                    !layout.isWall(ifc2x, ifc2z + (ifc2z < 6 ? 1 : -1))
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(
                            baseX + ifc2x,
                            floorY + 2,
                            baseZ + ifc2z + (ifc2z < 6 ? 1 : -1)
                        ),
                        Blocks.LANTERN.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            placeSharedChains(level, baseX, baseZ, floorY, chunkBB);
            placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB);
        }

        @Override
        public BlockState pillarBlock(RandomSource r, int ry) {
            if (ry == 1 || ry == 10) return Blocks.BLUE_ICE.defaultBlockState();
            return r.nextFloat() < 0.5f
                ? Blocks.PACKED_ICE.defaultBlockState()
                : Blocks.BLUE_ICE.defaultBlockState();
        }

        @Override
        @Nullable
        public BlockState scatterBlock(RandomSource r) {
            float f = r.nextFloat();
            if (f < 0.50f) return Blocks.SNOW.defaultBlockState().setValue(
                net.minecraft.world.level.block.SnowLayerBlock.LAYERS,
                1 + r.nextInt(2)
            );
            if (f < 0.70f) return Blocks.POWDER_SNOW.defaultBlockState();
            return Blocks.ICE.defaultBlockState();
        }

        @Override
        public BlockState wallAccentBlock() {
            return Blocks.BLUE_ICE.defaultBlockState();
        }
    },
    SCULK {
        @Override
        public BlockState wallBlock(RandomSource r, int ry) {
            float f = r.nextFloat();
            if (
                f < 0.10f
            ) return Blocks.CRACKED_DEEPSLATE_BRICKS.defaultBlockState();
            if (f < 0.40f) return Blocks.SCULK.defaultBlockState();
            return Blocks.DEEPSLATE_BRICKS.defaultBlockState();
        }

        @Override
        public BlockState floorBlock(
            RandomSource r,
            int rx,
            int rz,
            boolean isEdge
        ) {
            if (isEdge) return Blocks.SCULK.defaultBlockState();
            return r.nextFloat() < 0.30f
                ? Blocks.SCULK.defaultBlockState()
                : Blocks.DEEPSLATE_TILES.defaultBlockState();
        }

        @Override
        public BlockState ceilingBlock(RandomSource r) {
            float f = r.nextFloat();
            if (
                f < 0.10f
            ) return Blocks.CRACKED_DEEPSLATE_BRICKS.defaultBlockState();
            if (f < 0.30f) return Blocks.SCULK.defaultBlockState();
            return Blocks.DEEPSLATE_BRICKS.defaultBlockState();
        }

        @Override
        public BlockState wallShellBlock() {
            return Blocks.DEEPSLATE_BRICKS.defaultBlockState();
        }

        @Override
        public void placeDecorations(
            WorldGenLevel level,
            RandomSource random,
            int baseX,
            int baseZ,
            int floorY,
            RoomLayout layout,
            BoundingBox chunkBB
        ) {
            // Sculk veins on wall midpoints at ry=2
            for (int[] wallPos : new int[][] {
                { 6, 1 },
                { 6, 11 },
                { 1, 6 },
                { 11, 6 },
            }) {
                if (layout.isWall(wallPos[0], wallPos[1])) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(
                            baseX + wallPos[0],
                            floorY + 2,
                            baseZ + wallPos[1]
                        ),
                        Blocks.SCULK_VEIN.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // Soul lanterns on wall midpoints at ry=3
            for (int[] wallPos : new int[][] {
                { 6, 1 },
                { 6, 11 },
                { 1, 6 },
                { 11, 6 },
            }) {
                if (layout.isWall(wallPos[0], wallPos[1])) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(
                            baseX + wallPos[0],
                            floorY + 3,
                            baseZ + wallPos[1]
                        ),
                        Blocks.SOUL_LANTERN.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // Sculk catalyst in first corner
            int[][] corners = layout.innerCorners();
            int[] sc = corners[0];
            if (
                layout.isInShape(sc[0], sc[1]) && !layout.isWall(sc[0], sc[1])
            ) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + sc[0], floorY + 1, baseZ + sc[1]),
                    Blocks.SCULK_CATALYST.defaultBlockState(),
                    chunkBB
                );
            }
            // Corner furniture: sculk_shrieker + sculk_sensor + candle
            int[][] scorners = layout.innerCorners();
            int[] sfc = scorners[1];
            int sfcx = sfc[0] + (sfc[0] < 6 ? 1 : -1);
            int sfcz = sfc[1] + (sfc[1] < 6 ? 1 : -1);
            if (layout.isInShape(sfcx, sfcz) && !layout.isWall(sfcx, sfcz)) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + sfcx, floorY + 1, baseZ + sfcz),
                    Blocks.SCULK_SHRIEKER.defaultBlockState(),
                    chunkBB
                );
                int sfcx2 = sfcx + (sfcx < 6 ? 1 : -1);
                if (
                    layout.isInShape(sfcx2, sfcz) && !layout.isWall(sfcx2, sfcz)
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + sfcx2, floorY + 1, baseZ + sfcz),
                        Blocks.SCULK_SENSOR.defaultBlockState(),
                        chunkBB
                    );
                }
                if (
                    layout.isInShape(sfcx, sfcz + (sfcz < 6 ? 1 : -1)) &&
                    !layout.isWall(sfcx, sfcz + (sfcz < 6 ? 1 : -1))
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(
                            baseX + sfcx,
                            floorY + 1,
                            baseZ + sfcz + (sfcz < 6 ? 1 : -1)
                        ),
                        Blocks.CANDLE.defaultBlockState()
                            .setValue(BlockStateProperties.CANDLES, 3)
                            .setValue(BlockStateProperties.LIT, true),
                        chunkBB
                    );
                }
            }
            placeSharedChains(level, baseX, baseZ, floorY, chunkBB);
            placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB);
        }

        @Override
        public BlockState pillarBlock(RandomSource r, int ry) {
            if (ry == 1 || ry == 10) return Blocks.SCULK.defaultBlockState();
            return r.nextFloat() < 0.3f
                ? Blocks.SCULK.defaultBlockState()
                : Blocks.DEEPSLATE_TILE_WALL.defaultBlockState();
        }

        @Override
        @Nullable
        public BlockState scatterBlock(RandomSource r) {
            float f = r.nextFloat();
            if (f < 0.50f) return Blocks.SCULK.defaultBlockState();
            if (f < 0.80f) return Blocks.SCULK_VEIN.defaultBlockState();
            return Blocks.MOSS_CARPET.defaultBlockState();
        }

        @Override
        public BlockState wallAccentBlock() {
            return Blocks.SCULK_CATALYST.defaultBlockState();
        }
    },
    SANDSTONE {
        @Override
        public BlockState wallBlock(RandomSource r, int ry) {
            float f = r.nextFloat();
            if (f < 0.10f) return Blocks.CHISELED_SANDSTONE.defaultBlockState();
            if (f < 0.30f) return Blocks.SANDSTONE.defaultBlockState();
            return Blocks.CUT_SANDSTONE.defaultBlockState();
        }

        @Override
        public BlockState floorBlock(
            RandomSource r,
            int rx,
            int rz,
            boolean isEdge
        ) {
            return r.nextFloat() < 0.15f
                ? Blocks.SAND.defaultBlockState()
                : Blocks.SMOOTH_SANDSTONE.defaultBlockState();
        }

        @Override
        public BlockState ceilingBlock(RandomSource r) {
            return r.nextFloat() < 0.20f
                ? Blocks.SANDSTONE.defaultBlockState()
                : Blocks.CUT_SANDSTONE.defaultBlockState();
        }

        @Override
        public BlockState wallShellBlock() {
            return Blocks.CUT_SANDSTONE.defaultBlockState();
        }

        @Override
        public void placeDecorations(
            WorldGenLevel level,
            RandomSource random,
            int baseX,
            int baseZ,
            int floorY,
            RoomLayout layout,
            BoundingBox chunkBB
        ) {
            // Wall torches at midpoints
            for (int[] wallPos : new int[][] {
                { 6, 0 },
                { 6, 12 },
                { 0, 6 },
                { 12, 6 },
            }) {
                if (layout.isWall(wallPos[0], wallPos[1])) {
                    Direction torchDir = getTorchDirection(
                        wallPos[0],
                        wallPos[1]
                    );
                    if (torchDir != null) {
                        HangingCagePiece.safeSetBlock(
                            level,
                            new BlockPos(
                                baseX + wallPos[0],
                                floorY + 3,
                                baseZ + wallPos[1]
                            ),
                            Blocks.WALL_TORCH.defaultBlockState().setValue(
                                net.minecraft.world.level.block.WallTorchBlock.FACING,
                                torchDir
                            ),
                            chunkBB
                        );
                    }
                }
            }
            // Orange terracotta accents at wall midpoints ry=2
            for (int[] wallPos : new int[][] {
                { 6, 1 },
                { 6, 11 },
                { 1, 6 },
                { 11, 6 },
            }) {
                if (layout.isWall(wallPos[0], wallPos[1])) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(
                            baseX + wallPos[0],
                            floorY + 2,
                            baseZ + wallPos[1]
                        ),
                        Blocks.ORANGE_TERRACOTTA.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            // TNT hidden in first corner
            int[][] corners = layout.innerCorners();
            int[] tc = corners[0];
            if (
                layout.isInShape(tc[0], tc[1]) && !layout.isWall(tc[0], tc[1])
            ) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + tc[0], floorY + 1, baseZ + tc[1]),
                    Blocks.TNT.defaultBlockState(),
                    chunkBB
                );
            }
            // Corner furniture: barrel + flower_pot + orange_terracotta bench
            int[] ssfc = corners[1];
            int ssfcx = ssfc[0] + (ssfc[0] < 6 ? 1 : -1);
            int ssfcz = ssfc[1] + (ssfc[1] < 6 ? 1 : -1);
            if (
                layout.isInShape(ssfcx, ssfcz) && !layout.isWall(ssfcx, ssfcz)
            ) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + ssfcx, floorY + 1, baseZ + ssfcz),
                    Blocks.BARREL.defaultBlockState(),
                    chunkBB
                );
                int ssfcx2 = ssfcx + (ssfcx < 6 ? 1 : -1);
                if (
                    layout.isInShape(ssfcx2, ssfcz) &&
                    !layout.isWall(ssfcx2, ssfcz)
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(baseX + ssfcx2, floorY + 1, baseZ + ssfcz),
                        Blocks.FLOWER_POT.defaultBlockState(),
                        chunkBB
                    );
                }
                if (
                    layout.isInShape(ssfcx, ssfcz + (ssfcz < 6 ? 1 : -1)) &&
                    !layout.isWall(ssfcx, ssfcz + (ssfcz < 6 ? 1 : -1))
                ) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(
                            baseX + ssfcx,
                            floorY + 1,
                            baseZ + ssfcz + (ssfcz < 6 ? 1 : -1)
                        ),
                        Blocks.ORANGE_TERRACOTTA.defaultBlockState(),
                        chunkBB
                    );
                }
            }
            placeSharedChains(level, baseX, baseZ, floorY, chunkBB);
            placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB);
        }

        @Override
        public BlockState pillarBlock(RandomSource r, int ry) {
            if (
                ry == 1 || ry == 10
            ) return Blocks.CHISELED_SANDSTONE.defaultBlockState();
            return Blocks.SANDSTONE_WALL.defaultBlockState();
        }

        @Override
        @Nullable
        public BlockState scatterBlock(RandomSource r) {
            float f = r.nextFloat();
            if (f < 0.40f) return Blocks.SAND.defaultBlockState();
            if (f < 0.70f) return Blocks.DEAD_BUSH.defaultBlockState();
            return Blocks.CANDLE.defaultBlockState()
                .setValue(BlockStateProperties.CANDLES, 1 + r.nextInt(3))
                .setValue(BlockStateProperties.LIT, true);
        }

        @Override
        public BlockState wallAccentBlock() {
            return Blocks.CHISELED_SANDSTONE.defaultBlockState();
        }
    };

    public abstract BlockState wallBlock(RandomSource r, int ry);

    public abstract BlockState floorBlock(
        RandomSource r,
        int rx,
        int rz,
        boolean isEdge
    );

    public abstract BlockState ceilingBlock(RandomSource r);

    /** Solid block used for wall positions on the floor layer. */
    public abstract BlockState wallShellBlock();

    public abstract void placeDecorations(
        WorldGenLevel level,
        RandomSource random,
        int baseX,
        int baseZ,
        int floorY,
        RoomLayout layout,
        BoundingBox chunkBB
    );

    /** Block used for pillars (may vary by height). */
    public abstract BlockState pillarBlock(RandomSource r, int ry);

    /** Random scatter block for floor decoration, or null to skip. */
    @Nullable
    public abstract BlockState scatterBlock(RandomSource r);

    /** Accent block for decorative wall bands. */
    public abstract BlockState wallAccentBlock();

    // ── Shared structural features ──────────────────────────────────

    /** Pillar positions -- verified to be inside all 4 layouts. */
    private static final int[][] PILLAR_POSITIONS = {
        { 4, 4 },
        { 4, 8 },
        { 8, 4 },
        { 8, 8 },
    };

    /** Place 4 full-height pillars at verified positions. */
    static void placeSharedPillars(
        WorldGenLevel level,
        RandomSource random,
        int baseX,
        int baseZ,
        int floorY,
        RoomLayout layout,
        RoomTheme theme,
        BoundingBox chunkBB
    ) {
        for (int[] p : PILLAR_POSITIONS) {
            if (
                !layout.isInShape(p[0], p[1]) || layout.isWall(p[0], p[1])
            ) continue;
            for (int ry = 1; ry <= 10; ry++) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + p[0], floorY + ry, baseZ + p[1]),
                    theme.pillarBlock(random, ry),
                    chunkBB
                );
            }
        }
    }

    /** Place random floor scatter (~12% of interior positions). */
    static void placeSharedFloorScatter(
        WorldGenLevel level,
        RandomSource random,
        int baseX,
        int baseZ,
        int floorY,
        RoomLayout layout,
        RoomTheme theme,
        BoundingBox chunkBB
    ) {
        for (int rx = 1; rx <= 11; rx++) {
            for (int rz = 1; rz <= 11; rz++) {
                if (
                    !layout.isInShape(rx, rz) || layout.isWall(rx, rz)
                ) continue;
                // Skip pillar positions
                if ((rx == 4 || rx == 8) && (rz == 4 || rz == 8)) continue;
                // Skip cage center area (5-7, 5-7)
                if (rx >= 5 && rx <= 7 && rz >= 5 && rz <= 7) continue;
                // Skip corner positions (used by decorations/chests)
                if ((rx <= 2 || rx >= 10) && (rz <= 2 || rz >= 10)) continue;
                if (random.nextFloat() < 0.12f) {
                    BlockState scatter = theme.scatterBlock(random);
                    if (scatter != null) {
                        HangingCagePiece.safeSetBlock(
                            level,
                            new BlockPos(baseX + rx, floorY + 1, baseZ + rz),
                            scatter,
                            chunkBB
                        );
                    }
                }
            }
        }
    }

    /** Place ceiling cobwebs and extra hanging chains. */
    static void placeSharedCeilingDecor(
        WorldGenLevel level,
        RandomSource random,
        int baseX,
        int baseZ,
        int floorY,
        RoomLayout layout,
        BoundingBox chunkBB
    ) {
        // Cobwebs along walls at ceiling level
        int[][] cobwebCandidates = {
            { 2, 1 },
            { 4, 1 },
            { 8, 1 },
            { 10, 1 },
            { 2, 11 },
            { 4, 11 },
            { 8, 11 },
            { 10, 11 },
            { 1, 4 },
            { 1, 8 },
            { 11, 4 },
            { 11, 8 },
        };
        for (int[] pos : cobwebCandidates) {
            if (
                layout.isInShape(pos[0], pos[1]) &&
                !layout.isWall(pos[0], pos[1]) &&
                random.nextFloat() < 0.45f
            ) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + pos[0], floorY + 10, baseZ + pos[1]),
                    Blocks.COBWEB.defaultBlockState(),
                    chunkBB
                );
            }
        }
        // Extra hanging chains at random interior positions
        int[][] chainCandidates = { { 5, 3 }, { 7, 9 }, { 3, 7 } };
        for (int[] pos : chainCandidates) {
            if (
                layout.isInShape(pos[0], pos[1]) &&
                !layout.isWall(pos[0], pos[1]) &&
                random.nextFloat() < 0.6f
            ) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + pos[0], floorY + 10, baseZ + pos[1]),
                    Blocks.CHAIN.defaultBlockState(),
                    chunkBB
                );
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + pos[0], floorY + 9, baseZ + pos[1]),
                    Blocks.CHAIN.defaultBlockState(),
                    chunkBB
                );
            }
        }
    }

    /** Place additional wall-mounted lighting. */
    static void placeSharedWallLighting(
        WorldGenLevel level,
        RandomSource random,
        int baseX,
        int baseZ,
        int floorY,
        RoomLayout layout,
        BoundingBox chunkBB
    ) {
        // Lanterns on pillar sides (facing center) at ry=1 (on the floor)
        int[][] pillarLanternPositions = {
            { 5, 4 },
            { 4, 7 },
            { 8, 5 },
            { 7, 8 },
        };
        for (int[] pos : pillarLanternPositions) {
            if (
                layout.isInShape(pos[0], pos[1]) &&
                !layout.isWall(pos[0], pos[1])
            ) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + pos[0], floorY + 1, baseZ + pos[1]),
                    Blocks.LANTERN.defaultBlockState(),
                    chunkBB
                );
            }
        }
        // Extra wall sconces at quarter-points
        int[][] wallSconces = {
            { 4, 0 },
            { 8, 0 },
            { 4, 12 },
            { 8, 12 },
            { 0, 4 },
            { 0, 8 },
            { 12, 4 },
            { 12, 8 },
        };
        for (int[] pos : wallSconces) {
            if (layout.isWall(pos[0], pos[1]) && random.nextFloat() < 0.5f) {
                Direction torchDir = getTorchDirection(pos[0], pos[1]);
                if (torchDir != null) {
                    HangingCagePiece.safeSetBlock(
                        level,
                        new BlockPos(
                            baseX + pos[0],
                            floorY + 3,
                            baseZ + pos[1]
                        ),
                        Blocks.WALL_TORCH.defaultBlockState().setValue(
                            net.minecraft.world.level.block.WallTorchBlock.FACING,
                            torchDir
                        ),
                        chunkBB
                    );
                }
            }
        }
    }

    /** Place wall accent bands at ry=5 and ry=8. */
    static void placeSharedWallBands(
        WorldGenLevel level,
        int baseX,
        int baseZ,
        int floorY,
        RoomLayout layout,
        RoomTheme theme,
        BoundingBox chunkBB
    ) {
        int[][] bandPositions = {
            { 6, 1 },
            { 6, 11 },
            { 1, 6 },
            { 11, 6 },
            { 4, 1 },
            { 8, 1 },
            { 4, 11 },
            { 8, 11 },
            { 1, 4 },
            { 1, 8 },
            { 11, 4 },
            { 11, 8 },
        };
        for (int[] pos : bandPositions) {
            if (layout.isWall(pos[0], pos[1])) {
                // Band at ry=5
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + pos[0], floorY + 5, baseZ + pos[1]),
                    theme.wallAccentBlock(),
                    chunkBB
                );
                // Optional band at ry=8
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(baseX + pos[0], floorY + 8, baseZ + pos[1]),
                    theme.wallAccentBlock(),
                    chunkBB
                );
            }
        }
    }

    /** Chains on cage flanks and ceiling corners -- shared by all themes. */
    static void placeSharedChains(
        WorldGenLevel level,
        int baseX,
        int baseZ,
        int floorY,
        BoundingBox chunkBB
    ) {
        for (int[] chainPos : new int[][] { { 3, 6 }, { 9, 6 } }) {
            for (int ry = 5; ry <= 10; ry++) {
                HangingCagePiece.safeSetBlock(
                    level,
                    new BlockPos(
                        baseX + chainPos[0],
                        floorY + ry,
                        baseZ + chainPos[1]
                    ),
                    Blocks.CHAIN.defaultBlockState(),
                    chunkBB
                );
            }
        }
        for (int[] chainPos : new int[][] {
            { 3, 3 },
            { 3, 9 },
            { 9, 3 },
            { 9, 9 },
        }) {
            HangingCagePiece.safeSetBlock(
                level,
                new BlockPos(
                    baseX + chainPos[0],
                    floorY + 10,
                    baseZ + chainPos[1]
                ),
                Blocks.CHAIN.defaultBlockState(),
                chunkBB
            );
        }
    }

    /** Determine torch facing direction for a wall position (torch faces inward). */
    static Direction getTorchDirection(int rx, int rz) {
        if (rz == 0) return Direction.SOUTH;
        if (rz == 12) return Direction.NORTH;
        if (rx == 0) return Direction.EAST;
        if (rx == 12) return Direction.WEST;
        return null;
    }

    /** Hanging lanterns -- shared by all themes. */
    static void placeSharedHangingLanterns(
        WorldGenLevel level,
        int baseX,
        int baseZ,
        int floorY,
        BoundingBox chunkBB
    ) {
        for (int[] pos : new int[][] { { 3, 3 }, { 9, 9 } }) {
            HangingCagePiece.safeSetBlock(
                level,
                new BlockPos(baseX + pos[0], floorY + 9, baseZ + pos[1]),
                Blocks.LANTERN.defaultBlockState().setValue(
                    LanternBlock.HANGING,
                    true
                ),
                chunkBB
            );
        }
    }
}
