use crate::steps::EngineStep;
use radiate_core::{Chromosome, Ecosystem, Front, MetricSet, Phenotype, metric_names};
use radiate_error::Result;
use std::sync::{Arc, RwLock};

pub struct FrontStep<C: Chromosome> {
    pub(crate) front: Arc<RwLock<Front<Phenotype<C>>>>,
}

impl<C> EngineStep<C> for FrontStep<C>
where
    C: Chromosome + PartialEq + Clone + 'static,
{
    #[inline]
    fn execute(
        &mut self,
        generation: usize,
        ecosystem: &mut Ecosystem<C>,
        metrics: &mut MetricSet,
    ) -> Result<()> {
        let phenotypes = ecosystem
            .population()
            .iter()
            .filter(|ind| ind.age(generation) == 0)
            .cloned()
            .collect::<Vec<Phenotype<C>>>();

        let add_result = self.front.write().unwrap().add_all(phenotypes);

        metrics.upsert((metric_names::FRONT_ADDITIONS, add_result.added_count));
        metrics.upsert((metric_names::FRONT_REMOVALS, add_result.removed_count));
        metrics.upsert((metric_names::FRONT_COMPARISONS, add_result.comparisons));
        metrics.upsert((metric_names::FRONT_FILTERS, add_result.filter_count));
        metrics.upsert((metric_names::FRONT_SIZE, add_result.size));

        if add_result.added_count > 0 {
            // Update entropy metric every 10 generations - this is an expensive operation so we
            // don't want to do it every generation.
            if generation % 10 == 0 {
                let mut reader = self.front.write().unwrap();
                if let Some(entropy) = reader.entropy() {
                    metrics.upsert((metric_names::FRONT_ENTROPY, entropy));
                }
            }
        }

        Ok(())
    }
}
