use spin::Once;

static AREAS: Once<Areas> = Once::new();

#[derive(Debug)]
struct Areas {
    areas: [Area; 16],
    count: usize,
}

//TODO Do we align the base and length here, 
//     provide methods for it,
//     or leave it up to the user
#[derive(Copy, Clone, Debug)]
pub struct Area {
    pub base: u64,
    pub length: u64,
}

#[derive(Debug, Clone)]
pub struct AreaIter {
    areas: &'static Areas,
    i: usize,
}

impl Areas {
    const fn 
() -> Areas {
        Areas {
            areas: [Area{base:0, length: 0};16],
            count: 0,
        }
    }

    fn add_area(&mut self, base: u64, length: u64) {
        self.areas[self.count] = Area{base:base, length: length};
        self.count += 1;
    }

    fn iter(&'static self) -> AreaIter {
        AreaIter {i: 0, areas: self}
    }
}

impl Iterator for AreaIter {
    type Item = &'static Area;
    fn next(&mut self) -> Option<Self::Item> {
        if self.i < self.areas.count {
            let item = &self.areas.areas[self.i];
            self.i += 1;
            Some(item)
        } else {
            None
        }
    }
}

fn areas() -> &'static Areas {
    AREAS.try().expect("AREAS not initialized yet")
}

pub fn memory_areas() -> AreaIter {
    areas().iter()
}

pub fn init(boot_info: &::bootinfo::BootInfo) {
    AREAS.call_once(|| {
        let mut areas = Areas::new();
        for area in boot_info.memory_areas() {
            // Only copy free areas
            if area.usage == ::bootinfo::Usage::Free {
                areas.add_area(area.start, area.length);
            }
        }
        areas
    });

    //TODO I'm not sure if I want this here
    //     We should probably move this to a print function, or something.
    println!("Areas");
    for area in memory_areas() {
        println!("    start: 0x{:x}, length: 0x{:x}", area.base, area.length);
    }
}
new