use super::*;
use crate::{
    util::{RandomField, Sampler, LOCALITY},
    Land,
};
use common::{
    generation::EntityInfo,
    terrain::{BlockKind, SpriteKind},
};
use rand::prelude::*;
use std::{mem, sync::Arc};
use vek::*;

/// Represents house data generated by the `generate()` method
pub struct CliffTower {
    /// Axis aligned bounding region for the house
    bounds: Aabr<i32>,
    /// Approximate altitude of the door tile
    pub(crate) alt: i32,
}

impl CliffTower {
    pub fn generate(
        land: &Land,
        _rng: &mut impl Rng,
        site: &Site,
        door_tile: Vec2<i32>,
        door_dir: Vec2<i32>,
        tile_aabr: Aabr<i32>,
    ) -> Self {
        let bounds = Aabr {
            min: site.tile_wpos(tile_aabr.min),
            max: site.tile_wpos(tile_aabr.max),
        };
        Self {
            bounds,
            alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32,
        }
    }
}

impl Structure for CliffTower {
    fn render(&self, _site: &Site, _land: &Land, painter: &Painter) {
        let base = self.alt + 1;
        let center = self.bounds.center();
        let variant_pos = center.with_z(base);
        let variant = RandomField::new(0).get(variant_pos) as i32 % 10;
        // common superquadric degree for rooms
        let sq_type = 2.5;
        let storeys = 5 + (variant / 2);
        let mut length = 16 + (variant / 2);
        let mut width = 7 * length / 8;
        let mut height = 18 + variant / 2;
        let (mut stair_pos1, mut stair_pos2) = (center - 3, center + 3);
        let mut floor_level = base - 40;
        let brick = Fill::Sampling(Arc::new(|variant_pos| {
            Some(
                match (RandomField::new(0).get(Vec3::new(variant_pos.z, 0, 0))) % 15 {
                    0 => Block::new(BlockKind::Rock, Rgb::new(51, 89, 118)),
                    1 => Block::new(BlockKind::Rock, Rgb::new(57, 96, 126)),
                    2 => Block::new(BlockKind::Rock, Rgb::new(59, 103, 136)),
                    3 => Block::new(BlockKind::Rock, Rgb::new(61, 109, 145)),
                    4 => Block::new(BlockKind::Rock, Rgb::new(42, 66, 87)),
                    5 => Block::new(BlockKind::Rock, Rgb::new(47, 76, 101)),
                    6 => Block::new(BlockKind::Rock, Rgb::new(50, 84, 110)),
                    7 => Block::new(BlockKind::Rock, Rgb::new(52, 85, 112)),
                    8 => Block::new(BlockKind::Rock, Rgb::new(51, 60, 66)),
                    9 => Block::new(BlockKind::Rock, Rgb::new(58, 74, 87)),
                    10 => Block::new(BlockKind::Rock, Rgb::new(53, 104, 111)),
                    11 => Block::new(BlockKind::Rock, Rgb::new(52, 63, 72)),
                    12 => Block::new(BlockKind::Rock, Rgb::new(52, 63, 72)),
                    13 => Block::new(BlockKind::Rock, Rgb::new(74, 128, 168)),
                    _ => Block::new(BlockKind::Rock, Rgb::new(69, 123, 162)),
                },
            )
        }));
        let wood = Fill::Brick(BlockKind::Wood, Rgb::new(106, 83, 51), 12);
        let color = Fill::Block(Block::air(SpriteKind::CliffDecorBlock));
        let window = Fill::Block(Block::air(SpriteKind::WindowArabic));
        let window2 = Fill::Block(Block::air(SpriteKind::WindowArabic).with_ori(2).unwrap());
        for s in 0..storeys {
            let x_offset = RandomField::new(0).get((center - length).with_z(base)) as i32 % 10;
            let y_offset = RandomField::new(0).get((center + length).with_z(base)) as i32 % 10;
            let super_center = Vec2::new(center.x - 3 + x_offset / 2, center.y - 3 + y_offset / 2);
            let room1_type = RandomField::new(0).get((center - length).with_z(base)) as i32 % 2;
            let room2_type = RandomField::new(0).get((center - length - 1).with_z(base)) as i32 % 2;
            // CliffTower Hoodoo Overlay
            painter
                .cubic_bezier(
                    super_center.with_z(floor_level + (height / 2)),
                    (super_center - x_offset).with_z(floor_level + height),
                    (super_center - y_offset).with_z(floor_level + (height) + (height / 2)),
                    super_center.with_z(floor_level + (2 * height)),
                    (length - 1) as f32,
                )
                .fill(brick.clone());
            // only inhabit towers with enough storeys to have entries above ground
            if storeys > 3 {
                // wood or rocky platforms
                // only spawn on upper storeys
                if floor_level > base + 35 {
                    match RandomField::new(0).get((super_center - floor_level).with_z(base)) as i32
                        % 2
                    {
                        0 => {
                            painter
                                .superquadric(
                                    Aabb {
                                        min: (super_center - (5 * (length / 3)) + 3)
                                            .with_z(floor_level),
                                        max: (super_center + (5 * (length / 3)) - 3)
                                            .with_z(floor_level + 2),
                                    },
                                    6.0,
                                )
                                .fill(wood.clone());
                            painter
                                .prim(Primitive::without(
                                    painter.superquadric(
                                        Aabb {
                                            min: (super_center - (5 * (length / 3)) + 2)
                                                .with_z(floor_level + 1),
                                            max: (super_center + (5 * (length / 3)) - 1)
                                                .with_z(floor_level + 5),
                                        },
                                        6.0,
                                    ),
                                    painter.superquadric(
                                        Aabb {
                                            min: (super_center - (5 * (length / 3)) + 2)
                                                .with_z(floor_level + 3),
                                            max: (super_center + (5 * (length / 3)) - 2)
                                                .with_z(floor_level + 5),
                                        },
                                        6.0,
                                    ),
                                ))
                                .fill(wood.clone());
                            // lanterns & random sprites for wood platform corners
                            for dir in SQUARE_4 {
                                let corner_pos = super_center - (5 * (length / 4));
                                let sprite_pos = (corner_pos + (dir * (10 * (length / 4))))
                                    .with_z(floor_level + 4);
                                painter.sprite(
                                    sprite_pos,
                                    match (RandomField::new(0).get(sprite_pos)) % 10 {
                                        0 => SpriteKind::FireBowlGround,
                                        1 => SpriteKind::Bowl,
                                        3 => SpriteKind::VialEmpty,
                                        4 => SpriteKind::Crate,
                                        5 => SpriteKind::Pot,
                                        _ => SpriteKind::Lantern,
                                    },
                                );
                            }
                            // planters for larger wood platforms
                            if length > 11 {
                                for r in 0..2 {
                                    for p in 0..((length / 2) - 2) {
                                        let planter_pos = Vec2::new(
                                            super_center.x - (2 * (length / 3))
                                                + (p * (length / 3)),
                                            super_center.y - ((4 * (length / 3)) + 2)
                                                + (r * ((8 * (length / 3)) + 4)),
                                        );
                                        painter
                                            .aabb(Aabb {
                                                min: Vec2::new(planter_pos.x - 1, planter_pos.y)
                                                    .with_z(floor_level + 4),
                                                max: Vec2::new(
                                                    planter_pos.x + 2,
                                                    planter_pos.y + 1,
                                                )
                                                .with_z(floor_level + 6),
                                            })
                                            .clear();
                                        painter.rotated_sprite(
                                            planter_pos.with_z(floor_level + 4),
                                            SpriteKind::Planter,
                                            (4 - (r * 4)) as u8,
                                        );
                                    }
                                }
                            }
                        },
                        _ => {
                            painter
                                .superquadric(
                                    Aabb {
                                        min: (center - length + 1).with_z(floor_level),
                                        max: (center + length - 1).with_z(floor_level + 2),
                                    },
                                    sq_type,
                                )
                                .fill(brick.clone());
                            painter
                                .prim(Primitive::without(
                                    painter.superquadric(
                                        Aabb {
                                            min: (center - length).with_z(floor_level + 1),
                                            max: (center + length).with_z(floor_level + 5),
                                        },
                                        sq_type,
                                    ),
                                    painter.superquadric(
                                        Aabb {
                                            min: (center - length + 3).with_z(floor_level + 3),
                                            max: (center + length - 3).with_z(floor_level + 5),
                                        },
                                        sq_type,
                                    ),
                                ))
                                .fill(brick.clone());
                        },
                    }
                }
                // room
                painter
                    .superquadric(
                        Aabb {
                            min: Vec2::new(super_center.x - length - 1, super_center.y - width - 1)
                                .with_z(floor_level),
                            max: Vec2::new(super_center.x + length + 1, super_center.y + width + 1)
                                .with_z(floor_level + height),
                        },
                        sq_type,
                    )
                    .fill(brick.clone());
                // clear room - leave some floor
                painter
                    .prim(Primitive::without(
                        painter.superquadric(
                            Aabb {
                                min: Vec2::new(
                                    super_center.x - length + 1,
                                    super_center.y + 1 - width,
                                )
                                .with_z(floor_level + 1),
                                max: Vec2::new(
                                    super_center.x + length - 1,
                                    super_center.y - 1 + width,
                                )
                                .with_z(floor_level + height - 1),
                            },
                            sq_type,
                        ),
                        painter.aabb(Aabb {
                            min: Vec2::new(super_center.x - length + 1, super_center.y + 1 - width)
                                .with_z(floor_level + 1),
                            max: Vec2::new(super_center.x + length - 1, super_center.y - 1 + width)
                                .with_z(floor_level + 4),
                        }),
                    ))
                    .clear();
                // entries
                painter
                    .aabb(Aabb {
                        min: Vec2::new(super_center.x - length, super_center.y - 2)
                            .with_z(floor_level + 3),
                        max: Vec2::new(super_center.x - length + 6, super_center.y + 2)
                            .with_z(floor_level + 4),
                    })
                    .fill(brick.clone());
                painter
                    .aabb(Aabb {
                        min: Vec2::new(super_center.x + length - 6, super_center.y - 2)
                            .with_z(floor_level + 3),
                        max: Vec2::new(super_center.x + length, super_center.y + 2)
                            .with_z(floor_level + 4),
                    })
                    .fill(brick.clone());
                // colored sills
                painter
                    .aabb(Aabb {
                        min: Vec2::new(super_center.x - length - 1, super_center.y - 2)
                            .with_z(floor_level + 3),
                        max: Vec2::new(super_center.x - length, super_center.y + 2)
                            .with_z(floor_level + 4),
                    })
                    .fill(color.clone());
                painter
                    .aabb(Aabb {
                        min: Vec2::new(super_center.x + length, super_center.y - 2)
                            .with_z(floor_level + 3),
                        max: Vec2::new(super_center.x + length + 1, super_center.y + 2)
                            .with_z(floor_level + 4),
                    })
                    .fill(color.clone());
                if floor_level > base {
                    // clear entries
                    painter
                        .aabb(Aabb {
                            min: Vec2::new(super_center.x - length - 12, super_center.y - 2)
                                .with_z(floor_level + 4),
                            max: Vec2::new(super_center.x + length + 12, super_center.y + 2)
                                .with_z(floor_level + 7),
                        })
                        .clear();
                    // door sprites
                    painter
                        .prim(Primitive::without(
                            painter.aabb(Aabb {
                                min: Vec2::new(super_center.x - length + 1, super_center.y - 2)
                                    .with_z(floor_level + 4),
                                max: Vec2::new(super_center.x - length + 2, super_center.y + 2)
                                    .with_z(floor_level + 7),
                            }),
                            painter.aabb(Aabb {
                                min: Vec2::new(super_center.x - length + 1, super_center.y - 1)
                                    .with_z(floor_level + 4),
                                max: Vec2::new(super_center.x - length + 2, super_center.y + 1)
                                    .with_z(floor_level + 7),
                            }),
                        ))
                        .fill(window.clone());
                    painter
                        .prim(Primitive::without(
                            painter.aabb(Aabb {
                                min: Vec2::new(super_center.x + length - 1, super_center.y - 2)
                                    .with_z(floor_level + 4),
                                max: Vec2::new(super_center.x + length, super_center.y + 2)
                                    .with_z(floor_level + 7),
                            }),
                            painter.aabb(Aabb {
                                min: Vec2::new(super_center.x + length - 1, super_center.y - 1)
                                    .with_z(floor_level + 4),
                                max: Vec2::new(super_center.x + length, super_center.y + 1)
                                    .with_z(floor_level + 7),
                            }),
                        ))
                        .fill(window.clone());
                    // windows
                    painter
                        .aabb(Aabb {
                            min: Vec2::new(super_center.x - 4, super_center.y - width)
                                .with_z(floor_level + 5),
                            max: Vec2::new(super_center.x + 4, super_center.y - width + 3)
                                .with_z(floor_level + 6),
                        })
                        .fill(brick.clone());
                    painter
                        .aabb(Aabb {
                            min: Vec2::new(super_center.x - 4, super_center.y - width + 1)
                                .with_z(floor_level + 4),
                            max: Vec2::new(super_center.x + 4, super_center.y - width + 3)
                                .with_z(floor_level + 5),
                        })
                        .fill(brick.clone());
                    painter
                        .aabb(Aabb {
                            min: Vec2::new(super_center.x - 4, super_center.y + width - 3)
                                .with_z(floor_level + 5),
                            max: Vec2::new(super_center.x + 4, super_center.y + width + 1)
                                .with_z(floor_level + 6),
                        })
                        .fill(brick.clone());
                    painter
                        .aabb(Aabb {
                            min: Vec2::new(super_center.x - 4, super_center.y + width - 3)
                                .with_z(floor_level + 4),
                            max: Vec2::new(super_center.x + 4, super_center.y + width)
                                .with_z(floor_level + 5),
                        })
                        .fill(brick.clone());
                    painter
                        .aabb(Aabb {
                            min: Vec2::new(super_center.x - 3, super_center.y - width - 1)
                                .with_z(floor_level + 5),
                            max: Vec2::new(super_center.x + 3, super_center.y - width)
                                .with_z(floor_level + 6),
                        })
                        .fill(color.clone());
                    painter
                        .aabb(Aabb {
                            min: Vec2::new(super_center.x - 3, super_center.y + width + 1)
                                .with_z(floor_level + 5),
                            max: Vec2::new(super_center.x + 3, super_center.y + width + 2)
                                .with_z(floor_level + 6),
                        })
                        .fill(color.clone());
                    // clear windows
                    painter
                        .aabb(Aabb {
                            min: Vec2::new(super_center.x - 4, super_center.y - width - 12)
                                .with_z(floor_level + 6),
                            max: Vec2::new(super_center.x + 4, super_center.y + width + 12)
                                .with_z(floor_level + 9),
                        })
                        .clear();
                    // window sprites
                    painter
                        .prim(Primitive::without(
                            painter.aabb(Aabb {
                                min: Vec2::new(super_center.x - 4, super_center.y - width + 1)
                                    .with_z(floor_level + 6),
                                max: Vec2::new(super_center.x + 4, super_center.y - width + 2)
                                    .with_z(floor_level + 9),
                            }),
                            painter.aabb(Aabb {
                                min: Vec2::new(super_center.x - 1, super_center.y - width + 1)
                                    .with_z(floor_level + 6),
                                max: Vec2::new(super_center.x + 1, super_center.y - width + 2)
                                    .with_z(floor_level + 9),
                            }),
                        ))
                        .fill(window2.clone());
                    painter
                        .prim(Primitive::without(
                            painter.aabb(Aabb {
                                min: Vec2::new(super_center.x - 4, super_center.y + width - 1)
                                    .with_z(floor_level + 6),
                                max: Vec2::new(super_center.x + 4, super_center.y + width)
                                    .with_z(floor_level + 9),
                            }),
                            painter.aabb(Aabb {
                                min: Vec2::new(super_center.x - 1, super_center.y + width - 1)
                                    .with_z(floor_level + 6),
                                max: Vec2::new(super_center.x + 1, super_center.y + width)
                                    .with_z(floor_level + 9),
                            }),
                        ))
                        .fill(window2.clone());
                }
                // room wall lamps
                for d in 0..2 {
                    let door_lamp_pos = Vec2::new(
                        super_center.x - length + 2 + (d * ((2 * length) - 4)),
                        super_center.y,
                    )
                    .with_z(floor_level + 9);
                    painter.rotated_sprite(
                        door_lamp_pos,
                        SpriteKind::WallLampSmall,
                        2 + ((d * 4) as u8),
                    );

                    let window_lamp_pos = Vec2::new(
                        super_center.x,
                        super_center.y - width + 2 + (d * ((2 * width) - 4)),
                    )
                    .with_z(floor_level + 9);
                    painter.rotated_sprite(
                        window_lamp_pos,
                        SpriteKind::WallLampSmall,
                        4 - ((d * 4) as u8),
                    );
                }
                // furniture sprites in room1(living_room, workshop), room2(kitchen, bath)
                // dont spawn sprites on stairways
                let stairway_clear_1 = Aabb {
                    min: (stair_pos1 - 5).with_z(floor_level + 3),
                    max: (stair_pos1 + 5).with_z(floor_level + 5),
                };
                let stairway_clear_2 = Aabb {
                    min: (stair_pos2 - 5).with_z(floor_level + 3),
                    max: (stair_pos2 + 5).with_z(floor_level + 5),
                };
                if length > width {
                    match room1_type {
                        0 => {
                            // living room
                            // distribute small sprites
                            let mut liv_sprites = vec![
                                SpriteKind::DrawerSmall,
                                SpriteKind::CoatRack,
                                SpriteKind::TableArabicSmall,
                                SpriteKind::CushionArabic,
                                SpriteKind::JugArabic,
                                SpriteKind::SpinningWheel,
                                SpriteKind::TanningRack,
                                SpriteKind::Loom,
                            ];
                            for dir in LOCALITY {
                                let pos = super_center + dir * (width / 3);
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                    && !liv_sprites.is_empty()
                                {
                                    let sprite = liv_sprites.swap_remove(
                                        RandomField::new(0).get(pos.with_z(base)) as usize
                                            % liv_sprites.len(),
                                    );
                                    painter.sprite(pos.with_z(floor_level + 4), sprite);
                                }
                            }
                            // bookshelfs
                            for d in 0..2 {
                                let pos = Vec2::new(
                                    super_center.x,
                                    super_center.y + width - 3 + (d * ((-2 * width) + 6)),
                                );
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                {
                                    painter
                                        .aabb(Aabb {
                                            min: Vec2::new(pos.x - 2, pos.y - 2 + (2 * d))
                                                .with_z(floor_level + 4),
                                            max: Vec2::new(pos.x + 3, pos.y + 1 + (2 * d))
                                                .with_z(floor_level + 6),
                                        })
                                        .clear();
                                    painter.rotated_sprite(
                                        pos.with_z(floor_level + 6),
                                        SpriteKind::BookshelfArabic,
                                        (4 * d) as u8,
                                    );
                                }
                            }
                            // canapes
                            for d in 0..2 {
                                let pos = Vec2::new(
                                    super_center.x - length + 10 + (d * ((2 * length) - 20)),
                                    super_center.y - width + 4 + (d * ((2 * width) - 8)),
                                );
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                {
                                    painter
                                        .aabb(Aabb {
                                            min: Vec2::new(
                                                pos.x - 1 - (3 * d),
                                                pos.y - 1 - (3 * d),
                                            )
                                            .with_z(floor_level + 4),
                                            max: Vec2::new(
                                                pos.x + 5 - (3 * d),
                                                pos.y + 5 - (3 * d),
                                            )
                                            .with_z(floor_level + 8),
                                        })
                                        .clear();
                                    painter.rotated_sprite(
                                        pos.with_z(floor_level + 4),
                                        SpriteKind::CanapeArabic,
                                        (4 * d) as u8,
                                    );
                                }
                            }
                            // decor set / separe / table large
                            for d in 0..2 {
                                let pos = Vec2::new(
                                    super_center.x - length + 8 + (d * ((2 * length) - 16)),
                                    super_center.y + width - 8 + (d * ((-2 * width) + 16)),
                                );
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                {
                                    painter
                                        .aabb(Aabb {
                                            min: Vec2::new(pos.x - 2, pos.y - 1)
                                                .with_z(floor_level + 4),
                                            max: Vec2::new(pos.x + 3, pos.y + 2)
                                                .with_z(floor_level + 6),
                                        })
                                        .clear();
                                    painter.sprite(
                                        pos.with_z(floor_level + 4),
                                        match (RandomField::new(0).get(pos.with_z(floor_level - d)))
                                            % 3
                                        {
                                            0 => SpriteKind::TableArabicLarge,
                                            1 => SpriteKind::DecorSetArabic,
                                            _ => SpriteKind::SepareArabic,
                                        },
                                    )
                                };
                            }
                        },
                        _ => {
                            // workshop
                            for d in 0..2 {
                                // forge tools
                                let ft_pos = Vec2::new(
                                    super_center.x - 4 + (d * 6),
                                    super_center.y - width + 3 + (d * ((2 * width) - 6)),
                                );
                                if !stairway_clear_1.contains_point(ft_pos.with_z(floor_level + 4))
                                    && !stairway_clear_2
                                        .contains_point(ft_pos.with_z(floor_level + 4))
                                {
                                    painter
                                        .aabb(Aabb {
                                            min: Vec2::new(ft_pos.x - 2 + d, ft_pos.y - (3 * d))
                                                .with_z(floor_level + 4),
                                            max: Vec2::new(
                                                ft_pos.x + 2 + d,
                                                ft_pos.y + 4 - (3 * d),
                                            )
                                            .with_z(floor_level + 7),
                                        })
                                        .clear();
                                    painter.rotated_sprite(
                                        ft_pos.with_z(floor_level + 4),
                                        SpriteKind::ForgeTools,
                                        (4 * d) as u8,
                                    );
                                }
                                // hearth
                                let pos = Vec2::new(
                                    super_center.x + length - 12 + (d * ((-2 * length) + 24)),
                                    super_center.y - width + 3 + (d * ((2 * width) - 6)),
                                );
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                {
                                    painter
                                        .aabb(Aabb {
                                            min: Vec2::new(pos.x - 2, pos.y - (4 * d))
                                                .with_z(floor_level + 4),
                                            max: Vec2::new(pos.x + 3, pos.y + 5 - (4 * d))
                                                .with_z(floor_level + 6),
                                        })
                                        .clear();
                                    painter.rotated_sprite(
                                        pos.with_z(floor_level + 4),
                                        SpriteKind::Hearth,
                                        (4 * d) as u8,
                                    );
                                }
                            }
                            // crafting stations
                            let mut ws_sprites = vec![
                                SpriteKind::CraftingBench,
                                SpriteKind::Forge,
                                SpriteKind::DismantlingBench,
                                SpriteKind::Anvil,
                            ];
                            for dir in LOCALITY {
                                let pos = super_center + dir * (width / 3);
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                    && !ws_sprites.is_empty()
                                {
                                    let sprite = ws_sprites.swap_remove(
                                        RandomField::new(0).get(pos.with_z(base)) as usize
                                            % ws_sprites.len(),
                                    );
                                    painter.sprite(pos.with_z(floor_level + 4), sprite);
                                }
                            }
                        },
                    }
                } else {
                    match room2_type {
                        0 => {
                            // bath
                            // wall tables with varying items
                            for d in 0..2 {
                                let pos = Vec2::new(
                                    super_center.x,
                                    super_center.y - width + 3 + (d * ((2 * width) - 7)),
                                );
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                {
                                    painter
                                        .aabb(Aabb {
                                            min: Vec2::new(pos.x - 2, pos.y - (d * 3))
                                                .with_z(floor_level + 4),
                                            max: Vec2::new(pos.x + 3, pos.y + 4 - (d * 3))
                                                .with_z(floor_level + 7),
                                        })
                                        .clear();
                                    painter.rotated_sprite(
                                        pos.with_z(floor_level + 4),
                                        SpriteKind::WallTableArabic,
                                        (4 * d) as u8,
                                    );
                                    painter.rotated_sprite(
                                        pos.with_z(floor_level + 5),
                                        match (RandomField::new(0)
                                            .get((pos - d).with_z(floor_level)))
                                            % 4
                                        {
                                            0 => SpriteKind::Bowl,
                                            1 => SpriteKind::VialEmpty,
                                            2 => SpriteKind::JugArabic,
                                            _ => SpriteKind::JugAndBowlArabic,
                                        },
                                        (4 * d) as u8,
                                    );
                                }
                            }
                            // distribute smaller sprites
                            let mut ba_sprites = vec![
                                SpriteKind::DrawerSmall,
                                SpriteKind::CoatRack,
                                SpriteKind::Crate,
                                SpriteKind::TableArabicSmall,
                                SpriteKind::SepareArabic,
                                SpriteKind::DecorSetArabic,
                            ];
                            for dir in LOCALITY {
                                let pos = super_center + dir * (width / 3);
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                    && !ba_sprites.is_empty()
                                {
                                    let sprite = ba_sprites.swap_remove(
                                        RandomField::new(0).get(pos.with_z(base)) as usize
                                            % ba_sprites.len(),
                                    );
                                    painter.sprite(pos.with_z(floor_level + 4), sprite)
                                }
                            }
                            // fountains
                            for d in 0..2 {
                                let pos = Vec2::new(
                                    super_center.x - length + 8 + (d * ((2 * length) - 16)),
                                    super_center.y + width - 8 + (d * ((-2 * width) + 16)),
                                );
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                {
                                    painter
                                        .aabb(Aabb {
                                            min: (pos - 1).with_z(floor_level + 4),
                                            max: (pos + 2).with_z(floor_level + 5),
                                        })
                                        .clear();
                                    painter.sprite(
                                        pos.with_z(floor_level + 4),
                                        SpriteKind::FountainArabic,
                                    )
                                };
                            }
                        },
                        _ => {
                            // kitchen
                            // cupboards / ovens / cushions / jugs
                            for d in 0..2 {
                                let pos = Vec2::new(
                                    super_center.x + 3 - (d * 6),
                                    super_center.y - width + 3,
                                );
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                {
                                    painter
                                        .aabb(Aabb {
                                            min: Vec2::new(pos.x - 1, pos.y)
                                                .with_z(floor_level + 4),
                                            max: Vec2::new(pos.x + 2, pos.y + 4)
                                                .with_z(floor_level + 7),
                                        })
                                        .clear();
                                    painter.rotated_sprite(
                                        pos.with_z(floor_level + 4),
                                        match (RandomField::new(0).get(pos.with_z(floor_level))) % 2
                                        {
                                            0 => SpriteKind::CupboardArabic,
                                            _ => SpriteKind::OvenArabic,
                                        },
                                        4,
                                    );
                                }
                            }
                            for d in 0..2 {
                                let pos = Vec2::new(
                                    super_center.x + 3 - (d * 6),
                                    super_center.y + width - 3,
                                );
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                {
                                    painter
                                        .aabb(Aabb {
                                            min: Vec2::new(pos.x - 1, pos.y - 3)
                                                .with_z(floor_level + 4),
                                            max: Vec2::new(pos.x + 2, pos.y + 1)
                                                .with_z(floor_level + 7),
                                        })
                                        .clear();
                                    painter.rotated_sprite(
                                        pos.with_z(floor_level + 4),
                                        match (RandomField::new(0).get(pos.with_z(floor_level))) % 4
                                        {
                                            0 => SpriteKind::CupboardArabic,
                                            1 => SpriteKind::OvenArabic,
                                            2 => SpriteKind::CushionArabic,
                                            _ => SpriteKind::JugArabic,
                                        },
                                        0,
                                    );
                                }
                            }
                            // wall tables with varying items
                            for d in 0..2 {
                                let pos = Vec2::new(
                                    super_center.x,
                                    super_center.y - width + 3 + (d * ((2 * width) - 6)),
                                );
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                {
                                    painter
                                        .aabb(Aabb {
                                            min: Vec2::new(pos.x - 2, pos.y - (3 * d))
                                                .with_z(floor_level + 4),
                                            max: Vec2::new(pos.x + 2, pos.y + 4 - (3 * d))
                                                .with_z(floor_level + 7),
                                        })
                                        .clear();
                                    painter.rotated_sprite(
                                        pos.with_z(floor_level + 4),
                                        SpriteKind::WallTableArabic,
                                        (4 * d) as u8,
                                    );
                                    painter.rotated_sprite(
                                        pos.with_z(floor_level + 5),
                                        match (RandomField::new(0).get(pos.with_z(floor_level))) % 5
                                        {
                                            0 => SpriteKind::MelonCut,
                                            1 => SpriteKind::JugAndBowlArabic,
                                            2 => SpriteKind::Bowl,
                                            3 => SpriteKind::JugArabic,
                                            _ => SpriteKind::VialEmpty,
                                        },
                                        (4 * d) as u8,
                                    );
                                }
                            }
                            // distribute small sprites
                            let mut kit_sprites = vec![
                                SpriteKind::DrawerSmall,
                                SpriteKind::Crate,
                                SpriteKind::VialEmpty,
                                SpriteKind::Bowl,
                                SpriteKind::TableArabicSmall,
                                SpriteKind::JugArabic,
                                SpriteKind::CookingPot,
                                SpriteKind::Cauldron,
                            ];
                            for dir in LOCALITY {
                                let pos = super_center + dir * (width / 3);
                                if !stairway_clear_1.contains_point(pos.with_z(floor_level + 4))
                                    && !stairway_clear_2.contains_point(pos.with_z(floor_level + 4))
                                    && !kit_sprites.is_empty()
                                {
                                    let sprite = kit_sprites.swap_remove(
                                        RandomField::new(0).get(pos.with_z(base)) as usize
                                            % kit_sprites.len(),
                                    );
                                    painter.sprite(pos.with_z(floor_level + 4), sprite);
                                }
                            }
                        },
                    }
                };
                // stairways starting from 2nd storey
                if s > 0 {
                    //clear stairway
                    painter
                        .cylinder(Aabb {
                            min: (stair_pos1 - 3).with_z(floor_level - height + 4),
                            max: (stair_pos1 + 3).with_z(floor_level + 7),
                        })
                        .clear();
                    //stairway
                    let stair_radius1 = 4.0;
                    let stairs_clear1 = painter.prim(Primitive::Cylinder(Aabb {
                        min: (stair_pos1 - stair_radius1 as i32).with_z(floor_level - height),
                        max: (stair_pos1 + stair_radius1 as i32).with_z(floor_level + 4),
                    }));
                    painter
                        .prim(Primitive::sampling(
                            stairs_clear1,
                            crate::site2::plot::dungeon::spiral_staircase(
                                stair_pos1.with_z(floor_level + 4),
                                stair_radius1,
                                0.5,
                                7.0,
                            ),
                        ))
                        .fill(brick.clone());
                }
                // spawn mountaineers in each room
                let spawn_pos = super_center.with_z(floor_level + 4);
                let npc_amount = RandomField::new(0).get(spawn_pos) % 4;
                for _ in 0..npc_amount {
                    let mut rng = rand::thread_rng();
                    painter.spawn(
                        EntityInfo::at(spawn_pos.map(|e| e as f32))
                            .with_asset_expect("common.entity.village.mountaineer", &mut rng),
                    );
                }
            }
            // vary next storey
            length += -1;
            width += -1;
            height += -1;
            floor_level += height;
            mem::swap(&mut length, &mut width);
            mem::swap(&mut stair_pos1, &mut stair_pos2);
        }
    }
}
