use super::TreeChromosome;
use radiate_core::{AlterContext, AlterResult, Mutate, Rate, Valid, random_provider};

#[derive(Clone, Debug)]
pub struct HoistMutator {
    rate: Rate,
}

impl HoistMutator {
    pub fn new(rate: impl Into<Rate>) -> Self {
        let rate = rate.into();
        if !rate.is_valid() {
            panic!("rate {:?} is not valid", rate);
        }

        HoistMutator { rate }
    }
}

impl<T> Mutate<TreeChromosome<T>> for HoistMutator
where
    T: Clone + PartialEq,
{
    fn rate(&self) -> Rate {
        self.rate.clone()
    }

    fn mutate_chromosome(
        &self,
        chromosome: &mut TreeChromosome<T>,
        _: &mut AlterContext,
    ) -> AlterResult {
        let root = chromosome.root_mut();
        let root_size = root.size();
        let rand_index = random_provider::range(0..root_size);

        if rand_index < 1 {
            return AlterResult::empty();
        }

        if let Some(rand_node) = root.get_mut(rand_index) {
            if rand_node.children().is_none() {
                return AlterResult::empty();
            }

            let child_idx = random_provider::range(0..rand_node.children().map_or(0, |c| c.len()));
            let mut child = rand_node.detach(child_idx);

            return if let Some(child) = child.as_mut() {
                std::mem::swap(rand_node, child);
                AlterResult::from(1)
            } else {
                AlterResult::empty()
            };
        }

        AlterResult::empty()
    }
}
