#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Face {
    PosX,
    NegX,
    PosY,
    NegY,
    PosZ,
    NegZ,
}

pub struct QuadTreeNode {
    pub face: Face,
    pub level: u32,
    pub x: u32,
    pub y: u32,
    pub children: Option<Box<[QuadTreeNode; 4]>>,
}

pub struct LodConfig {
    pub maxlevel: u32,
    pub splitdistancefactor: f64,
    pub baseresolution: u32,
}

impl Default for LodConfig {
    fn default() -> Self {
        Self {
            maxlevel: 15,
            splitdistancefactor: 2.0,
            baseresolution: 64,
        }
    }
}

impl QuadTreeNode {
    pub fn new(face: Face, level: u32, x: u32, y: u32) -> Self {
        Self {
            face,
            level,
            x,
            y,
            children: None,
        }
    }

    pub fn shouldsplit(&self, cameradistance: f64, config: &LodConfig) -> bool {
        if self.level >= config.maxlevel {
            return false;
        }
        let nodesize = 1.0 / (1u64 << self.level) as f64;
        cameradistance < nodesize * config.splitdistancefactor
    }

    pub fn split(&mut self) {
        let nl = self.level + 1;
        let nx = self.x * 2;
        let ny = self.y * 2;
        self.children = Some(Box::new([
            QuadTreeNode::new(self.face, nl, nx, ny),
            QuadTreeNode::new(self.face, nl, nx + 1, ny),
            QuadTreeNode::new(self.face, nl, nx, ny + 1),
            QuadTreeNode::new(self.face, nl, nx + 1, ny + 1),
        ]));
    }

    pub fn isleaf(&self) -> bool {
        self.children.is_none()
    }
}

pub struct LodTerrain {
    pub roots: Vec<QuadTreeNode>,
    pub config: LodConfig,
}

impl LodTerrain {
    pub fn new(config: LodConfig) -> Self {
        let faces = [
            Face::PosX,
            Face::NegX,
            Face::PosY,
            Face::NegY,
            Face::PosZ,
            Face::NegZ,
        ];
        let roots = faces
            .iter()
            .map(|&f| QuadTreeNode::new(f, 0, 0, 0))
            .collect();
        Self { roots, config }
    }

    pub fn update(&mut self, camerapos: [f64; 3]) {
        let camdist = (camerapos[0].powi(2) + camerapos[1].powi(2) + camerapos[2].powi(2)).sqrt();
        for root in &mut self.roots {
            Self::updatenode(root, camdist, &self.config);
        }
    }

    fn updatenode(node: &mut QuadTreeNode, cameradistance: f64, config: &LodConfig) {
        if node.shouldsplit(cameradistance, config) {
            if node.isleaf() {
                node.split();
            }
            if let Some(ref mut children) = node.children {
                for child in children.iter_mut() {
                    Self::updatenode(child, cameradistance, config);
                }
            }
        }
    }
}
