use crate::assets_manager::handle::Handle;

struct Slot<T> {
    value: Option<T>,
    generation: u32,
}

pub struct Arena<T> {
    slots: Vec<Slot<T>>,
    free: Vec<u32>,
}

impl<T> Arena<T> {
    pub fn new() -> Self {
        Self{
            slots: vec![],
            free: vec![],
        }
    }
    pub fn insert(&mut self, value: T) -> Handle<T> {
        if let Some(index) = self.free.pop() {
            let slot = &mut self.slots[index as usize];
            slot.value = Some(value);

            Handle {
                index,
                generation: slot.generation,
                _marker: std::marker::PhantomData,
            }
        } else {
            let index = self.slots.len() as u32;

            self.slots.push(Slot {
                value: Some(value),
                generation: 0,
            });

            Handle {
                index,
                generation: 0,
                _marker: std::marker::PhantomData,
            }
        }
    }

    pub fn get(&self, handle: Handle<T>) -> Option<&T> {
        let slot = self.slots.get(handle.index as usize)?;

        if slot.generation == handle.generation {
            slot.value.as_ref()
        } else {
            None
        }
    }

    pub fn get_mut(&mut self, handle: Handle<T>) -> Option<&mut T> {
        let slot = self.slots.get_mut(handle.index as usize)?;

        if slot.generation == handle.generation {
            slot.value.as_mut()
        } else {
            None
        }
    }

    pub fn remove(&mut self, handle: Handle<T>) -> Option<T> {
        let slot = self.slots.get_mut(handle.index as usize)?;

        if slot.generation != handle.generation {
            return None;
        }

        let value = slot.value.take();
        slot.generation += 1;

        self.free.push(handle.index);

        value
    }
}