use super::{Chromosome, Genotype, Population, random_provider};
use std::sync::Arc;

/// Trait for replacement strategies in the algorithms.
///
/// This trait defines a method for replacing a member of the [Population] with a new individual
/// after the current individual has been determined to be invalid. Typically, this is done by
/// replacing the individual with a new one generated by the encoder. But in some cases, it may
/// be desirable to replace the individual in a different way, such as by sampling from the
/// [Population].
pub trait ReplacementStrategy<C: Chromosome>: Send + Sync {
    fn replace(
        &self,
        population: &Population<C>,
        encoder: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
    ) -> Genotype<C>;
}

/// Replacement strategy that replaces the individual with a new one generated by the encoder.
/// This is the default replacement strategy used in genetic algorithms.
pub struct EncodeReplace;

impl<C: Chromosome> ReplacementStrategy<C> for EncodeReplace {
    fn replace(
        &self,
        _: &Population<C>,
        encoder: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
    ) -> Genotype<C> {
        encoder()
    }
}

/// Replacement strategy that replaces the individual with a random member of the [Population].
/// This can be useful in cases where the [Population] is large and diverse or when the
/// chromosome grows or changes in size, thus encoding a new individual can result
/// in a member that that lacks significant diversity.
pub struct PopulationSampleReplace;

impl<C: Chromosome + Clone> ReplacementStrategy<C> for PopulationSampleReplace {
    fn replace(
        &self,
        population: &Population<C>,
        _: Arc<dyn Fn() -> Genotype<C> + Send + Sync>,
    ) -> Genotype<C> {
        let random_member = random_provider::range(0..population.len());
        population[random_member].genotype().clone()
    }
}
