#[derive(Clone)]
pub struct Chip8Stack {
    items: Vec<u16>
}

impl Default for Chip8Stack {
    fn default() -> Self {
        Chip8Stack  {
            items: vec![],
        }
    }
}

impl Chip8Stack {
    pub fn push(&mut self, new_value: &u16) {
        if self.depth() == 16 {
           // println!("Deep deep stack?");
           panic!("Stack Overflow");
        }
        self.items.push(*new_value );
    }
    pub fn pop(&mut self) -> u16 {
        if self.items.is_empty() {
            panic!("Stack Underflow");
        }
        self.items.pop().unwrap()
    }
    pub fn depth(&self) -> u16 {
        self.items.len() as u16
    }

    pub fn new() -> Self {
        Chip8Stack {
            items: vec![]
        }
    }
}

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

    #[test]
    fn smoke() { assert!(true) }

    #[test]
    fn push_pop_test() {
        let mut x = Chip8Stack::new();

        // lets see if we can push and pop a bunch
        x.push(&0xabcu16);
        x.push(&0xcdeu16);
        x.pop();
        assert_eq!(x.depth(), 1);
    }

    #[test]
    #[should_panic]
    fn stack_overflow_test() {
        let mut x = Chip8Stack::new();
        for i  in 0..17 {
            x.push(&i);
        }
    }

    #[test]
    #[should_panic]
    fn stack_underflow_test() {
        let mut x = Chip8Stack::new();
        x.pop();
    }

    #[test]
    fn lots_of_subs() {
        let mut x = Chip8Stack::new();
        let stack_contents = [0x123, 0x321, 0xabc, 0xdef,
                                      0xbad, 0xbef, 0xfed, 0xcab,
                                      0xbed, 0xcad, 0xfeb, 0xcab,
                                      0xfff, 0x000, 0x001];
        for i in stack_contents {
            x.push(&i);
        }

        assert_eq!(x.depth(), 15);

        // up to 50 random loops
        let num_loops: u8 = random::<u8>() % 50;
        for i in 0..num_loops {
            let start_count = x.depth();
            let num_pop = random::<u8>() % x.depth() as u8;
            for current_pop in 0..num_pop {
                x.pop();
            }

            let post_pop_count = x.depth();
            assert_eq!(post_pop_count as u8, start_count as u8 - num_pop);
            for current_push in 0..num_pop {
                x.push(&stack_contents[(current_push + post_pop_count as u8) as usize]);
            }
            assert_eq!(x.depth(), 15);
        }

    }
}