//! Data structure and definition of a genericsingle-linked LIFO list.

use std::ptr;

#[derive(PartialEq)]
pub struct List<T: PartialEq> {
    head: Link<T>,
    tail: *mut Node<T>,
}

type Link<T> = *mut Node<T>;


#[derive(PartialEq)]
struct Node<T> {
    elem: T,
    next: Link<T>,
}
/// Iterator structure for use in a for loop, pop elements before returning it
pub struct IntoIter<T: PartialEq>(List<T>);

/// Iterator structure for use in a for loop, dereference before returning it 
pub struct Iter<'a, T> {
    next: Option<&'a Node<T>>,
}

/// Same as Iter structure, returned item are mutable
pub struct IterMut<'a, T> {
    next: Option<&'a mut Node<T>>,
}

impl<T: PartialEq> List<T> {

    /// Push an item at the end of the list
    pub fn push(&mut self, elem: T) {
        unsafe {
            let new_tail = Box::into_raw(Box::new(Node {
                elem,
                next: ptr::null_mut(),
            }));

            if !self.tail.is_null() {
                (*self.tail).next = new_tail;
            } else {
                self.head = new_tail;
            }

            self.tail = new_tail;
        }
    }

    /// Retrieve and remove the item at the head of the list.
    /// 
    /// Return None if list is empty
    pub fn pop(&mut self) -> Option<T> {
        unsafe {
            if self.head.is_null() {
                None
            } else {
                let head = Box::from_raw(self.head);
                self.head = head.next;
                if self.head.is_null() {
                    self.tail = ptr::null_mut();
                }
                Some(head.elem)
            }
        }
    }

    /// Retrieve without removing the item at the head of the list
    /// 
    /// Return None if list is empty
    pub fn peek(&self) -> Option<&T> {
        unsafe {
            self.head.as_ref().map(|node| &node.elem)
        }
    }

    /// Retrieve without removing the item at the head of the list as mutable
    /// 
    /// Return None if lsit is empty
    pub fn peek_mut(&mut self) -> Option<&mut T> {
        unsafe {
            self.head.as_mut().map(|node| &mut node.elem)
        }
    }

    /// Search for an element in the list
    /// 
    /// Return **bool** true if the list contains the element, false otherwise
    /// 
    /// Worst case complexity of this function is O(n)
    pub fn contains(&self, elem: &T) -> bool {
        let mut iter = self.iter();
        let mut element = iter.next();
        while element.is_some() {
            if element.unwrap() == elem {
                return true;
            } else {
                element = iter.next();
            }
        }
        false
    }

    /// Remove the item from the list
    /// 
    /// Return true if the item has been found, otherwise return false
    /// 
    /// Worst-case complexity is O(n)
    pub fn remove(&mut self, item: T)-> bool {
        unsafe {
            let mut current: *mut Node<T> = self.head;
            let mut previous: *mut Node<T> = ptr::null_mut();
            while !current.is_null() {
                if (*current).elem == item {
                    (*previous).next = (*current).next;
                    drop(Box::from_raw(current).elem);
                    return true;
                } else {
                    previous = current;
                    current = (*current).next;
                }
            }
        }
        false
    }

    /// Return true if the list is empty, false otherwise
    pub fn is_empty(&self) -> bool {
        self.head.is_null()
    }

    /// Turn the list into an iterator for use in a for loop per example.
    /// 
    /// When you iter using into_iter, elements are remove from the list
    pub fn into_iter(self) -> IntoIter<T> {
        IntoIter(self)
    }

    /// Turn the list into an iterator for use in a for loop
    /// 
    /// When you iter using this method, elements are dereferenced
    pub fn iter(&self) -> Iter<'_, T> {
        unsafe {
            Iter { next: self.head.as_ref() }
        }
        
    }

    /// Same as iter but make the iterator mutable
    pub fn iter_mut(&mut self) -> IterMut<'_, T> {
        unsafe {
            IterMut { next: self.head.as_mut() }
        }
        
    }
}

impl<T: PartialEq> Default for List<T> {
    /// Create an empty list
    fn default() -> Self {
        Self { head: ptr::null_mut(), tail: ptr::null_mut() }
    }
}

impl<T: PartialEq> Drop for List<T> {
    fn drop(&mut self) {
        while self.pop().is_some() {} // removing every item from list (necessary as we using unsafe function)
    }
}

impl<T: PartialEq> Iterator for IntoIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        // access fields of a tuple struct numerically
        self.0.pop()
    }
}


impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            self.next.map(|node| {
                self.next = node.next.as_ref();
                &node.elem
            })
        }
    }
}

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            self.next.take().map(|node| {
                self.next = node.next.as_mut();
                &mut node.elem
            })
        }
        
    }
}

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

    #[test]
    fn basics() {
        let mut list = List::default();

        // Check empty list behaves right
        assert_eq!(list.pop(), None);

        // Populate list
        list.push(1);
        list.push(2);
        list.push(3);

        // Check normal removal
        assert_eq!(list.pop(), Some(1));
        assert_eq!(list.pop(), Some(2));

        // Push some more just to make sure nothing's corrupted
        list.push(4);
        list.push(5);

        // Check normal removal
        assert_eq!(list.pop(), Some(3));
        assert_eq!(list.pop(), Some(4));

        // Check exhaustion
        assert_eq!(list.pop(), Some(5));
        assert_eq!(list.pop(), None);
    }

    #[test]
    fn peek() {
        let mut list = List::default();
        assert_eq!(list.peek(), None);
        assert_eq!(list.peek_mut(), None);
        list.push(1); 
        list.push(2); 
        list.push(3);

        assert_eq!(list.peek(), Some(&1));
        assert_eq!(list.peek_mut(), Some(&mut 1));
    }

    #[test]
    fn into_iter() {
        let mut list = List::default();
        list.push(1);
        list.push(2);
        list.push(3);

        let mut iter = list.into_iter();
        assert_eq!(iter.next(), Some(1));
        assert_eq!(iter.next(), Some(2));
        assert_eq!(iter.next(), Some(3));
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn iter() {
        let mut list = List::default();
        list.push(1);
        list.push(2);
        list.push(3);

        let mut iter = list.iter();
        assert_eq!(iter.next(), Some(&1));
        assert_eq!(iter.next(), Some(&2));
        assert_eq!(iter.next(), Some(&3));
    }

    #[test]
    fn iter_mut() {
        let mut list = List::default();
        list.push(1);
        list.push(2);
        list.push(3);

        let mut iter = list.iter_mut();
        assert_eq!(iter.next(), Some(&mut 1));
        assert_eq!(iter.next(), Some(&mut 2));
        assert_eq!(iter.next(), Some(&mut 3));
    }

    #[test]
    fn contains_test() {
        let mut list = List::default();
        assert_eq!(list.peek(), None);
        list.push(1);
        list.push(2);
        list.push(3);

        assert_eq!(list.contains(&1), true);
        assert_eq!(list.contains(&4), false);
    }

    #[test]
    fn remove_test() {
        let mut list = List::default();
        assert_eq!(list.peek(), None);
        list.push(1);
        list.push(2);
        list.push(3);

        assert_eq!(list.contains(&2), true);
        list.remove(2);
        assert_eq!(list.contains(&2), false);
        assert_eq!(list.pop(), Option::Some(1));
        assert_eq!(list.pop(), Option::Some(3));
        assert_eq!(list.peek(), Option::None);
    }

    #[test]
    fn miri_test() {
        let mut list = List::default();

        list.push(1);
        list.push(2);
        list.push(3);

        assert!(list.pop() == Some(1));
        list.push(4);
        assert!(list.pop() == Some(2));
        list.push(5);

        assert!(list.peek() == Some(&3));
        list.push(6);
        list.peek_mut().map(|x| *x *= 10);
        assert!(list.peek() == Some(&30));
        assert!(list.pop() == Some(30));

        for elem in list.iter_mut() {
            *elem *= 100;
        }

        let mut iter = list.iter();
        assert_eq!(iter.next(), Some(&400));
        assert_eq!(iter.next(), Some(&500));
        assert_eq!(iter.next(), Some(&600));
        assert_eq!(iter.next(), None);
        assert_eq!(iter.next(), None);

        assert!(list.pop() == Some(400));
        list.peek_mut().map(|x| *x *= 10);
        assert!(list.peek() == Some(&5000));
        list.push(7);
    }
}