use l2::ast::*;

use std::vec::Vec;

#[derive(Clone, Copy)]
pub enum HeapCellView<'a> {
    Str(usize, &'a Atom),
    Var(usize)
}

pub struct HeapCellViewer<'a> {
    heap: &'a Heap,
    state_stack: Vec<(usize, &'a HeapCellValue)>
}

impl<'a> HeapCellViewer<'a> {
    pub fn new(heap: &'a Heap, focus: usize) -> Self {
        HeapCellViewer {
            heap: heap,
            state_stack: vec![(focus, &heap[focus])]
        }
    }

    fn follow(&self, value: &'a HeapCellValue) -> &'a HeapCellValue {
        match value {
            &HeapCellValue::NamedStr(_, _) => value,
            &HeapCellValue::Ref(cell_num) | &HeapCellValue::Str(cell_num) =>
                &self.heap[cell_num],
        }
    }
}

impl<'a> Iterator for HeapCellViewer<'a> {
    type Item = HeapCellView<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(hcv) = self.state_stack.pop() {
            match hcv {
                (focus, &HeapCellValue::NamedStr(arity, ref name)) => {
                    return Some(HeapCellView::Str(arity, name));
                },
                (_, &HeapCellValue::Ref(cell_num)) => {
                    let new_hcv = self.follow(hcv.1);

                    if hcv.1 == new_hcv {
                        return Some(HeapCellView::Var(cell_num));
                    } else {
                        self.state_stack.push((cell_num, new_hcv));
                    }
                },
                (_, &HeapCellValue::Str(cell_num)) => {
                    let new_hcv = self.follow(hcv.1);
                    self.state_stack.push((cell_num, new_hcv));
                }
            }
        }

        None
    }
}
                    for i in (1 .. arity + 1).rev() {
                        self.state_stack.push((focus + i, &self.heap[focus + i]));
                    }                            
                    