use super::*;
use crate::{
    util::{RandomField, Sampler},
    Land,
};
use common::terrain::{Block, BlockKind};
use rand::prelude::*;
use std::sync::Arc;
use vek::*;

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

impl DesertCityTemple {
    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 door_tile_pos = site.tile_center_wpos(door_tile);
        let bounds = Aabr {
            min: site.tile_wpos(tile_aabr.min),
            max: site.tile_wpos(tile_aabr.max),
        };

        Self {
            bounds,
            door_tile: door_tile_pos,
            alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32,
        }
    }
}

impl Structure for DesertCityTemple {
    #[cfg(feature = "use-dyn-lib")]
    const UPDATE_FN: &'static [u8] = b"render_desertcitytemple\0";

    #[cfg_attr(feature = "be-dyn-lib", export_name = "render_desertcitytemple")]
    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
        let sandstone = Fill::Sampling(Arc::new(|center| {
            Some(match (RandomField::new(0).get(center)) % 37 {
                0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
                9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
                18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
                27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
                _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
            })
        }));
        let sandstone_broken = Fill::Sampling(Arc::new(|center| {
            Some(match (RandomField::new(0).get(center)) % 42 {
                0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
                9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
                18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
                27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
                36..=40 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
                _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
            })
        }));
        let base = self.alt + 1;
        let center = self.bounds.center();
        let diameter =
            10 + (self.bounds.max.x - self.bounds.min.x).min(self.bounds.max.y - self.bounds.min.y);
        // Fence
        painter
            .aabb(Aabb {
                min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
                max: Vec2::new(self.bounds.min.x + 2, self.bounds.max.y).with_z(base + 2),
            })
            .union(painter.aabb(Aabb {
                min: Vec2::new(self.bounds.max.x - 1, self.bounds.min.y + 1).with_z(base - 20),
                max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
            }))
            .union(painter.aabb(Aabb {
                min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
                max: Vec2::new(self.bounds.max.x, self.bounds.min.y + 2).with_z(base + 2),
            }))
            .union(painter.aabb(Aabb {
                min: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y - 1).with_z(base - 20),
                max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
            }))
            .fill(sandstone_broken);
        painter
            .aabb(Aabb {
                min: Vec2::new(self.bounds.min.x + 1, center.y - 8).with_z(base),
                max: Vec2::new(self.bounds.max.x, center.y + 8).with_z(base + 7),
            })
            .clear();
        painter
            .aabb(Aabb {
                min: Vec2::new(center.x - 7, self.bounds.min.y + 1).with_z(base),
                max: Vec2::new(center.x + 9, self.bounds.max.y).with_z(base + 7),
            })
            .clear();
        // Foundation
        painter
            .aabb(Aabb {
                min: (self.bounds.min + 1).with_z(base - 20),
                max: (self.bounds.max).with_z(base),
            })
            .fill(sandstone.clone());

        // Temple
        let temple_color = match (RandomField::new(0).get((center - 3).with_z(base))) % 2 {
            0 => Fill::Brick(BlockKind::Rock, Rgb::new(100, 101, 250), 3),
            _ => Fill::Brick(BlockKind::Rock, Rgb::new(63, 49, 63), 24),
        };
        let temple_size = diameter / 3;
        let floaty_block = Fill::Brick(BlockKind::GlowingWeakRock, Rgb::new(253, 240, 149), 2);
        let temple = Aabb {
            min: (center - temple_size).with_z(base),
            max: (center + temple_size - 1).with_z(base + temple_size + 1),
        };

        // temple with roof carve out
        painter.aabb(temple).fill(sandstone.clone());
        // roof decoration
        painter
            .aabb(Aabb {
                min: (center - temple_size - 1).with_z(base + temple_size + 1),
                max: (center + temple_size).with_z(base + temple_size + 4),
            })
            .fill(sandstone.clone());
        painter
            .aabb(Aabb {
                min: (center - temple_size).with_z(base + temple_size + 2),
                max: (center + temple_size - 1).with_z(base + temple_size + 4),
            })
            .clear();
        painter
            .aabb(Aabb {
                min: Vec2::new(center.x - temple_size - 1, center.y - (temple_size / 4) - 2)
                    .with_z(base + temple_size + 3),
                max: Vec2::new(center.x + temple_size, center.y + (temple_size / 4) + 1)
                    .with_z(base + temple_size + 4),
            })
            .clear();
        painter
            .aabb(Aabb {
                min: Vec2::new(center.x - (temple_size / 4) - 2, center.y - temple_size - 1)
                    .with_z(base + temple_size + 3),
                max: Vec2::new(center.x + (temple_size / 4) + 1, center.y + temple_size)
                    .with_z(base + temple_size + 4),
            })
            .clear();

        // roof carve out
        painter
            .aabb(Aabb {
                min: (center - temple_size + 1).with_z(base + temple_size),
                max: (center + temple_size - 2).with_z(base + temple_size + 3),
            })
            .clear();
        //corner pillars
        for dir in SQUARE_4 {
            let corner_pos = center - temple_size;
            let pillar_center = corner_pos + (dir * ((2 * temple_size) - 1));
            // Pillar foot
            painter
                .aabb(Aabb {
                    min: (pillar_center - 4).with_z(base),
                    max: (pillar_center + 4).with_z(base + 4),
                })
                .fill(sandstone.clone());

            // Pillar
            painter
                .aabb(Aabb {
                    min: (pillar_center - 3).with_z(base + 4),
                    max: (pillar_center + 3).with_z(base + temple_size + 3),
                })
                .fill(sandstone.clone());
            // Pillar Top
            painter
                .aabb(Aabb {
                    min: (pillar_center - 4).with_z(base + temple_size + 3),
                    max: (pillar_center + 4).with_z(base + temple_size + 6),
                })
                .fill(sandstone.clone());

            painter
                .aabb(Aabb {
                    min: (pillar_center - 3).with_z(base + temple_size + 4),
                    max: (pillar_center + 3).with_z(base + temple_size + 6),
                })
                .clear();

            // clear parts of pillar top for roof carve out
            painter
                .aabb(Aabb {
                    min: (center - temple_size).with_z(base + temple_size + 3),
                    max: (center + temple_size - 1).with_z(base + temple_size + 6),
                })
                .clear()
        }

        // temple decor carve out
        // apply three carve options (horizontal, vertical, none) with two spread
        // options (for horizontal and vertical)
        for s in 0..2 {
            // decor carve out style
            let spread_select = ((RandomField::new(0).get((center - 1).with_z(base))) % 2) as i32;
            let spread = 3 * spread_select;
            let carve_style = ((RandomField::new(0).get((center - 2 - s).with_z(base))) % 3) as i32;
            match carve_style {
                //vertical
                0 => {
                    // decor carve outs1
                    for c in 0..(temple_size - 5) {
                        painter
                            .aabb(Aabb {
                                min: Vec2::new(
                                    center.x - temple_size + 5 + (2 * c),
                                    center.y - temple_size,
                                )
                                .with_z(base + 2),
                                max: Vec2::new(
                                    center.x - temple_size + 6 + (2 * c),
                                    center.y + temple_size - 1,
                                )
                                .with_z(base + temple_size - 1),
                            })
                            .clear();
                    }
                    for c in 0..(temple_size - 5) {
                        painter
                            .aabb(Aabb {
                                min: Vec2::new(
                                    center.x - temple_size,
                                    center.y - temple_size + 5 + (2 * c),
                                )
                                .with_z(base + 2),
                                max: Vec2::new(
                                    center.x + temple_size - 1,
                                    center.y - temple_size + 6 + (2 * c),
                                )
                                .with_z(base + temple_size - 1),
                            })
                            .clear();
                    }
                },
                1 => {
                    // horizontal
                    for c in 0..((temple_size / 2) - 2) {
                        painter
                            .aabb(Aabb {
                                min: Vec2::new(center.x - temple_size + 5, center.y - temple_size)
                                    .with_z(base + 2 + ((spread + 2) * c)),
                                max: Vec2::new(
                                    center.x + temple_size - 6,
                                    center.y + temple_size - 1,
                                )
                                .with_z(base + 3 + ((spread + 2) * c) + spread_select),
                            })
                            .clear();
                    }
                    for c in 0..((temple_size / 2) - 2) {
                        painter
                            .aabb(Aabb {
                                min: Vec2::new(center.x - temple_size, center.y - temple_size + 5)
                                    .with_z(base + 2 + ((spread + 2) * c)),
                                max: Vec2::new(
                                    center.x + temple_size - 1,
                                    center.y + temple_size - 6,
                                )
                                .with_z(base + 3 + ((spread + 2) * c) + spread_select),
                            })
                            .clear();
                    }
                },
                _ => {},
            }
        }
        // color inlays1
        painter
            .aabb(Aabb {
                min: Vec2::new(center.x - temple_size + 2, center.y - temple_size + 2)
                    .with_z(base + 2),
                max: Vec2::new(center.x + temple_size - 3, center.y - temple_size + 3)
                    .with_z(base + temple_size - 1),
            })
            .fill(temple_color.clone());

        painter
            .aabb(Aabb {
                min: Vec2::new(center.x - temple_size + 2, center.y + temple_size - 4)
                    .with_z(base + 2),
                max: Vec2::new(center.x + temple_size - 3, center.y + temple_size - 3)
                    .with_z(base + temple_size - 1),
            })
            .fill(temple_color.clone());
        // color inlays2
        painter
            .aabb(Aabb {
                min: Vec2::new(center.x - temple_size + 2, center.y - temple_size + 2)
                    .with_z(base + 2),
                max: Vec2::new(center.x - temple_size + 3, center.y + temple_size - 3)
                    .with_z(base + temple_size - 1),
            })
            .fill(temple_color.clone());
        painter
            .aabb(Aabb {
                min: Vec2::new(center.x + temple_size - 4, center.y - temple_size + 2)
                    .with_z(base + 2),
                max: Vec2::new(center.x + temple_size - 3, center.y + temple_size - 3)
                    .with_z(base + temple_size - 1),
            })
            .fill(temple_color.clone());
        // carve outside plane1
        painter
            .aabb(Aabb {
                min: Vec2::new(center.x - temple_size + 6, center.y - temple_size).with_z(base + 3),
                max: Vec2::new(center.x + temple_size - 7, center.y - temple_size + 1)
                    .with_z(base + temple_size - 2),
            })
            .clear();
        painter
            .aabb(Aabb {
                min: Vec2::new(center.x - temple_size + 6, center.y + temple_size - 2)
                    .with_z(base + 3),
                max: Vec2::new(center.x + temple_size - 7, center.y + temple_size - 1)
                    .with_z(base + temple_size - 2),
            })
            .clear();
        // carve outside plane2
        painter
            .aabb(Aabb {
                min: Vec2::new(center.x - temple_size, center.y - temple_size + 6).with_z(base + 3),
                max: Vec2::new(center.x - temple_size + 1, center.y + temple_size - 7)
                    .with_z(base + temple_size - 2),
            })
            .clear();
        painter
            .aabb(Aabb {
                min: Vec2::new(center.x + temple_size - 2, center.y - temple_size + 6)
                    .with_z(base + 3),
                max: Vec2::new(center.x + temple_size - 1, center.y + temple_size - 7)
                    .with_z(base + temple_size - 2),
            })
            .clear();
        // Temple Top Socket
        painter
            .cylinder(Aabb {
                min: (center - temple_size + 3).with_z(base + temple_size),
                max: (center + temple_size - 4).with_z(base + temple_size + 1),
            })
            .fill(sandstone.clone());
        // Temple Top
        let top = painter.sphere(Aabb {
            min: (center - temple_size + 3).with_z(base),
            max: (center + temple_size - 4).with_z(base + (2 * temple_size) - 7),
        });
        top.fill(sandstone.clone());

        // temple top decoration
        // apply two decoration options (horizontal, vertical, none) with two spread
        // options for hor/ver options
        for s in 0..2 {
            // decor carve out style
            let spread_select =
                ((RandomField::new(0).get((center - 1 - s).with_z(base))) % 2) as i32;
            let spread = 3 * spread_select;
            let carve_style = ((RandomField::new(0).get((center - 2 - s).with_z(base))) % 3) as i32;
            match carve_style {
                //vertical
                0 => {
                    // decor carve outs1
                    for c in 0..(temple_size - 2) {
                        painter
                            .aabb(Aabb {
                                min: Vec2::new(
                                    center.x - temple_size + 2 + ((spread + 2) * c),
                                    center.y - temple_size,
                                )
                                .with_z(base + temple_size),
                                max: Vec2::new(
                                    center.x - temple_size + 3 + ((spread + 2) * c) + spread_select,
                                    center.y + temple_size - 1,
                                )
                                .with_z(base + (2 * temple_size)),
                            })
                            .intersect(top)
                            .fill(temple_color.clone());
                    }
                },
                1 => {
                    for c in 0..(temple_size - 2) {
                        painter
                            .aabb(Aabb {
                                min: Vec2::new(
                                    center.x - temple_size,
                                    center.y - temple_size + 2 + ((spread + 2) * c),
                                )
                                .with_z(base + temple_size),
                                max: Vec2::new(
                                    center.x + temple_size - 1,
                                    center.y - temple_size + 3 + ((spread + 2) * c) + spread_select,
                                )
                                .with_z(base + (2 * temple_size)),
                            })
                            .intersect(top)
                            .fill(temple_color.clone());
                    }
                },
                _ => {},
            }
        }
        // Temple Top Socket
        painter
            .cylinder(Aabb {
                min: (center - temple_size + 3).with_z(base + temple_size),
                max: (center + temple_size - 4).with_z(base + temple_size + 1),
            })
            .fill(sandstone.clone());
        // clear top
        painter
            .sphere(Aabb {
                min: (center - temple_size + 4).with_z(base + 1),
                max: (center + temple_size - 5).with_z(base + (2 * temple_size) - 8),
            })
            .clear();

        // round or rectangle entries
        let entry_select = ((RandomField::new(0).get((center + 1).with_z(base))) % 2) as i32;
        match entry_select {
            0 => {
                //round Temple entries1
                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - 5, center.y - temple_size).with_z(base),
                        max: Vec2::new(center.x + 4, center.y + temple_size - 1).with_z(base + 4),
                    })
                    .fill(sandstone.clone());
                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - 4, center.y - temple_size).with_z(base),
                        max: Vec2::new(center.x + 3, center.y + temple_size - 1).with_z(base + 4),
                    })
                    .clear();

                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - 4, center.y - temple_size).with_z(base + 4),
                        max: Vec2::new(center.x + 3, center.y + temple_size - 1).with_z(base + 7),
                    })
                    .fill(sandstone.clone());
                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - 3, center.y - temple_size).with_z(base + 4),
                        max: Vec2::new(center.x + 2, center.y + temple_size - 1).with_z(base + 7),
                    })
                    .clear();

                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - 3, center.y - temple_size).with_z(base + 7),
                        max: Vec2::new(center.x + 2, center.y + temple_size - 1).with_z(base + 9),
                    })
                    .fill(sandstone.clone());
                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - 2, center.y - temple_size).with_z(base + 7),
                        max: Vec2::new(center.x + 1, center.y + temple_size - 1).with_z(base + 9),
                    })
                    .clear();

                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - 2, center.y - temple_size).with_z(base + 9),
                        max: Vec2::new(center.x + 1, center.y + temple_size - 1).with_z(base + 10),
                    })
                    .fill(sandstone.clone());

                // round Temple entries2
                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - temple_size, center.y - 5).with_z(base),
                        max: Vec2::new(center.x + temple_size - 1, center.y + 4).with_z(base + 4),
                    })
                    .fill(sandstone.clone());
                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - temple_size, center.y - 4).with_z(base),
                        max: Vec2::new(center.x + temple_size - 1, center.y + 3).with_z(base + 4),
                    })
                    .clear();

                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - temple_size, center.y - 4).with_z(base + 4),
                        max: Vec2::new(center.x + temple_size - 1, center.y + 3).with_z(base + 7),
                    })
                    .fill(sandstone.clone());
                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - temple_size, center.y - 3).with_z(base + 4),
                        max: Vec2::new(center.x + temple_size - 1, center.y + 2).with_z(base + 7),
                    })
                    .clear();

                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - temple_size, center.y - 3).with_z(base + 7),
                        max: Vec2::new(center.x + temple_size - 1, center.y + 2).with_z(base + 9),
                    })
                    .fill(sandstone.clone());
                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - temple_size, center.y - 2).with_z(base + 7),
                        max: Vec2::new(center.x + temple_size - 1, center.y + 1).with_z(base + 9),
                    })
                    .clear();

                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - temple_size, center.y - 2).with_z(base + 9),
                        max: Vec2::new(center.x + temple_size - 1, center.y + 1).with_z(base + 10),
                    })
                    .fill(sandstone.clone());
            },
            _ => {
                //rectangle Temple entries1
                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - 5, center.y - temple_size).with_z(base),
                        max: Vec2::new(center.x + 4, center.y + temple_size - 1).with_z(base + 8),
                    })
                    .fill(sandstone.clone());
                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - 3, center.y - temple_size).with_z(base),
                        max: Vec2::new(center.x + 2, center.y + temple_size - 1).with_z(base + 6),
                    })
                    .clear();

                //rectangle Temple entries1
                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - temple_size, center.y - 5).with_z(base),
                        max: Vec2::new(center.x + temple_size - 1, center.y + 4).with_z(base + 8),
                    })
                    .fill(sandstone.clone());
                painter
                    .aabb(Aabb {
                        min: Vec2::new(center.x - temple_size, center.y - 3).with_z(base),
                        max: Vec2::new(center.x + temple_size - 1, center.y + 2).with_z(base + 6),
                    })
                    .clear();
            },
        }

        // carve temple
        painter
            .aabb(Aabb {
                min: (center - temple_size + 4).with_z(base),
                max: (center + temple_size - 5).with_z(base + temple_size - 1),
            })
            .clear();

        // floating sculpture, placeholder for sun god

        painter
            .sphere(Aabb {
                min: (center - 3).with_z(base),
                max: (center + 2).with_z(base + 5),
            })
            .fill(sandstone.clone());
        painter
            .sphere(Aabb {
                min: (center - 3).with_z(base + 4),
                max: (center + 2).with_z(base + 9),
            })
            .fill(sandstone.clone());
        painter
            .sphere(Aabb {
                min: (center - 3).with_z(base + 8),
                max: (center + 2).with_z(base + 13),
            })
            .fill(sandstone.clone());
        painter
            .sphere(Aabb {
                min: (center - 4).with_z(base + 14),
                max: (center + 3).with_z(base + 15),
            })
            .fill(sandstone.clone());
        painter
            .sphere(Aabb {
                min: (center - 3).with_z(base + 16),
                max: (center + 2).with_z(base + 21),
            })
            .fill(floaty_block.clone());

        // floating sculptures
        for dir in SQUARE_4 {
            let corner_pos = center - temple_size / 2;
            let sclpt_center = corner_pos + (dir * (temple_size));

            painter
                .sphere(Aabb {
                    min: (sclpt_center - 2).with_z(base),
                    max: (sclpt_center + 1).with_z(base + 3),
                })
                .fill(sandstone.clone());
            painter
                .sphere(Aabb {
                    min: (sclpt_center - 2).with_z(base + 2),
                    max: (sclpt_center + 1).with_z(base + 5),
                })
                .fill(sandstone.clone());
            painter
                .sphere(Aabb {
                    min: (sclpt_center - 3).with_z(base + 5),
                    max: (sclpt_center + 2).with_z(base + 6),
                })
                .fill(sandstone.clone());
            painter
                .sphere(Aabb {
                    min: (sclpt_center - 3).with_z(base + 7),
                    max: (sclpt_center + 2).with_z(base + 8),
                })
                .fill(sandstone.clone());
            painter
                .sphere(Aabb {
                    min: (sclpt_center - 2).with_z(base + 9),
                    max: (sclpt_center + 1).with_z(base + 12),
                })
                .fill(floaty_block.clone());
        }
    }
}
