use quick_error::quick_error;

/// A simple-minded NonEmptyVec implementation
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct NonEmptyVec<T> {
    first: T,
    rest: Vec<T>,
}
quick_error! {
    #[derive(Debug)]
    pub enum NonEmptyVecError {
        EmptyVectorErr {
            display("NonEmptyVec cannot be empty.")
        }
    }
}

#[allow(clippy::len_without_is_empty)]
impl<T> NonEmptyVec<T>
where
    T: Clone,
{
    pub fn new_first(first: T) -> Self {
        NonEmptyVec {
            first,
            rest: vec![],
        }
    }
    pub fn new(first: T, rest: Vec<T>) -> Self {
        NonEmptyVec { first, rest }
    }
    pub fn new_last(first_v: &[T], tail: NonEmptyVec<T>) -> Self {
        NonEmptyVec::try_from(first_v)
            .map(|nev| nev.concat(&tail))
            .unwrap_or_else(|_e| tail)
    }

    pub fn first(&self) -> &T {
        &self.first
    }
    pub fn rest(&self) -> &Vec<T> {
        &self.rest
    }

    pub fn to_vec(&self) -> Vec<T> {
        self.clone().into()
    }

    pub fn try_from(v: &[T]) -> Result<NonEmptyVec<T>, NonEmptyVecError> {
        if v.is_empty() {
            Err(NonEmptyVecError::EmptyVectorErr)
        } else {
            let first = v[0].clone();
            let rest = v[1..].to_vec();
            Ok(NonEmptyVec { first, rest })
        }
    }

    pub fn concat(&self, b: &NonEmptyVec<T>) -> NonEmptyVec<T> {
        let mut c = self.rest.clone();
        c.append(&mut b.to_vec());
        NonEmptyVec::new(self.first.clone(), c)
    }

    pub fn last(&self) -> &T {
        self.rest.last().unwrap_or(&self.first)
    }

    pub fn len(&self) -> usize {
        self.rest.len() + 1
    }

    pub fn as_vec(self) -> Vec<T> {
        self.into()
    }
}

impl<T> From<NonEmptyVec<T>> for Vec<T> {
    fn from(mut n: NonEmptyVec<T>) -> Self {
        let mut result: Vec<T> = Vec::with_capacity(n.rest.len() + 1);
        result.push(n.first);
        result.append(&mut n.rest);
        result
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn nonempty_construct_good() {
        let nev = NonEmptyVec::new_first(1);
        assert_eq!(1, nev.first);
        let expected1: Vec<i32> = vec![];
        assert_eq!(expected1, nev.rest);

        let nev2 = NonEmptyVec::new(1, vec![2, 3]);
        assert_eq!(1, nev2.first);
        assert_eq!(vec![2, 3], nev2.rest);
    }

    #[test]
    fn nonempty_vec_to_vec() {
        let nev = NonEmptyVec::new_first(1);
        let v1: Vec<i32> = nev.into();
        assert_eq!(vec![1], v1);

        let nev2 = NonEmptyVec::new(1, vec![2, 3, 4]);
        assert_eq!(vec![1, 2, 3, 4], Into::<Vec<i32>>::into(nev2));
    }
}
