use crate::prelude::*;
use sorted_vec::SortedSet;
use super::utils::local_to_global;

/// Trait for selection definitions. 
/// Implementors could be used as argumnets for selection creation methods.
pub trait SelectionDef 
{
    /// All errors, including bounds checks, have to be captured, 
    /// caller assumes that returned index is correct and does no additional checks..
    fn into_sel_index (
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider), // Something System-like
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError>;
}

impl SelectionDef for &SelectionExpr 

{
    fn into_sel_index (
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider),
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError> {        
        let ind = match subset {
            None => self.apply_whole(sys)?,
            Some(_) => {
                // We can't apply existing expr to a subset
                Err(SelectionError::SelDefInSubsel)?
            }
        };
        if ind.is_empty() {
            Err(SelectionError::EmptyExpr(self.get_str().to_string()))
        } else {
            Ok(ind)
        }
    }
}

impl SelectionDef for &str 

{
    fn into_sel_index (
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider),
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError> {
        let expr = SelectionExpr::new(self)?;
        let ind = match subset {
            None => expr.apply_whole(sys)?,
            Some(sub) => expr.apply_subset(sys, sub)?,
        };
        if ind.is_empty() {
            Err(SelectionError::EmptyExpr(expr.get_str().to_string()))
        } else {
            Ok(ind)
        }
    }
}

impl SelectionDef for String 

{
    fn into_sel_index(
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider),
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError> {
        self.as_str().into_sel_index(sys, subset)
    }
}

impl SelectionDef for &String 

{
    fn into_sel_index (
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider),
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError> {
        self.as_str().into_sel_index(sys, subset)
    }
}

impl SelectionDef for std::ops::Range<usize> 

{
    fn into_sel_index(
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider),
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError> {
        let n = match subset {
            None => sys.len(),
            Some(sub) => sub.len(),
        };

        if self.start >= n || self.end > n {
            return Err(SelectionError::IndexValidation(self.start, self.end, n-1));
        }

        match subset {
            None => Ok(unsafe { SortedSet::from_sorted(self.collect::<Vec<_>>()) }),
            Some(sub) => {                
                Ok(self.map(|i| sub[i]).collect::<Vec<_>>().into())
            }
        }
    }
}

impl SelectionDef for std::ops::RangeInclusive<usize> {
    fn into_sel_index(
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider),
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError> {
        if self.is_empty() {
            Err(SelectionError::EmptyRange)
        } else {
            let (b, e) = self.into_inner();
            (b..e + 1).into_sel_index(sys, subset)
        }
    }
}

impl SelectionDef for &[usize] 

{
    fn into_sel_index(
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider),
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError> {
        if self.is_empty() {
            Err(SelectionError::EmptySlice)
        } else {            
            match subset {
                None => {
                    let v: SVec = self.to_vec().into();
                    let n = sys.len();
                    if v[0] >= n || v[v.len()-1] >= n {
                        Err(SelectionError::IndexValidation(v[0], v[v.len()-1], n-1))
                    } else {
                        Ok(v)
                    }
                },
                Some(sub) => local_to_global(self.into_iter().cloned(), sub),
            }            
        }
    }
}

impl SelectionDef for &Vec<usize> 

{
    fn into_sel_index(
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider),
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError> {
        self.as_slice().into_sel_index(sys, subset)
    }
}

impl SelectionDef for Vec<usize> 

{
    fn into_sel_index (
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider),
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError> {
        self.as_slice().into_sel_index(sys, subset)
    }
}

impl SelectionDef for SVec 

{
    fn into_sel_index(
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider),
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError> {
        if self.is_empty() {
            Err(SelectionError::EmptySlice)
        } else {            
            match subset {
                None => {                    
                    let n = sys.len();
                    if self[self.len()-1] >= n {
                        Err(SelectionError::IndexValidation(self[0], self[self.len()-1], n-1))
                    } else {
                        Ok(self) // No copying!
                    }
                },
                Some(sub) => local_to_global(self.iter().cloned(), sub),
            }            
        }
    }
}

impl SelectionDef for &SVec 

{
    fn into_sel_index(
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider),
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError> {
        self.clone().into_sel_index(sys, subset)
    }
}

impl SelectionDef for Sel 

{
    fn into_sel_index(
        self,
        sys: &(impl AtomPosAnalysis + BoxProvider),
        subset: Option<&[usize]>,
    ) -> Result<SVec, SelectionError> {
        self.0.into_sel_index(sys, subset)
    }
}
