use hwloc2;

/** Core selection for multiple threads in order of those threads.
 *
 * Used in Run mode where we select cores for threads that are not running yet; after the threads 
 * are started, these selections can be applied in the same order as the threads started.
 *
 * Each item is an `Option` - of `None`, there is no explicit (non-default) core selection for the 
 * thread with that (N-th) index, so (N-th) thread will use default affinity of the process (or 
 * of previously applied thread order) If not None, the thread will use specified affinity .
 */
#[derive(Clone)]
pub struct OrderedCoreSelections(Vec<Option<hwloc2::CpuSet>>);

impl OrderedCoreSelections {
    /// Construct new selections,
    pub fn new() -> OrderedCoreSelections {
        Self(Vec::new())
    }

    /// True if all threads have default (non-explicit, to be set from process/thread order) core selection.
    pub fn all_default(&self) -> bool {
         !self.0.iter().any(|t| t.is_some())
    }

    /// Clears all explicit core selections, to use process/thread order affinity instead,
    pub fn set_all_default(&mut self) {
        for thread in &mut self.0 {
            *thread = None;
        }
    }

    /// Are the selections valid/applicable? (We can't have a thread running on 0 cores).
    pub fn validate_selections(&self) -> bool {
        self.0.iter().all(|t| t.is_none() || !t.as_ref().unwrap().is_empty())
    }

    /// Get the number of threads we have selections (even if empty) for.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Get selection for thread with specified index, or None if out of range
    pub fn get(&self, index: usize) -> Option<&hwloc2::CpuSet> {
        if index < self.len() { self.0[index].as_ref() } else { None }
    }

    /// Mutable version of `get()`.
    pub fn get_mut(&mut self, index: usize) -> &mut Option<hwloc2::CpuSet> {
        &mut self.0[index]
    }

    /// Iterate over the threads' core selections.
    pub fn iter(&self) -> std::slice::Iter<'_, Option<hwloc2::CpuSet>> {
        self.0.iter()
    }

    /// Mutable version of `iter()`.
    pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, Option<hwloc2::CpuSet>> {
        self.0.iter_mut()
    }

    /// Add selection for a new thread.
    pub fn push(&mut self, value: Option<hwloc2::CpuSet>) {
        self.0.push(value)
    }

    /// Remove selections for all threads from index `len` up.
    pub fn truncate(&mut self, len: usize) {
        self.0.truncate(len)
    }
}
