ohm 0.0.0

High performance 2D graphics library
Documentation
use std::hash::Hash;
use std::marker::PhantomData;
use std::ops::{Index, Range};

use fnv::FnvHashMap;
use gfx_hal::Backend;

use crate::factory::Factory;

#[derive(derivative::Derivative)]
#[derivative(Hash(bound = ""), Clone(bound = ""))]
pub struct ResourceId<B: Backend, R: Resource<B>>(R::Plan, u16); // TODO: more lightweight id

pub trait Resource<B: Backend> {
    type Plan: Hash + Eq + Clone;

    // TODO: Result
    fn create(factory: &mut Factory<B>, plan: &Self::Plan) -> Self;
    fn destroy(self, factory: &mut Factory<B>);
}

#[derive(derivative::Derivative)]
#[derivative(Default(bound = ""))]
pub struct ResourceGroup<B: Backend, R: Resource<B>> {
    #[derivative(Default(value = "0..0"))]
    visit_range: Range<usize>,

    resources: Vec<R>,
    _marker: PhantomData<B>,
}

#[derive(derivative::Derivative)]
#[derivative(Default(bound = ""))]
pub struct Resources<B: Backend, R: Resource<B>> {
    groups: FnvHashMap<R::Plan, ResourceGroup<B, R>>,
}

impl<B: Backend, R: Resource<B>> Resources<B, R> {
    pub fn add(&mut self, factory: &mut Factory<B>, plan: R::Plan) -> ResourceId<B, R> {
        let group = self
            .groups
            .entry(plan.clone())
            .or_insert_with(Default::default);

        if group.visit_range.start != group.visit_range.end {
            group.visit_range.start += 1;
            ResourceId(plan, (group.visit_range.start - 1) as _)
        } else {
            let new = R::create(factory, &plan);
            group.resources.push(new);
            ResourceId(plan, (group.resources.len() - 1) as _)
        }
    }

    pub fn reset(&mut self, factory: &mut Factory<B>) {
        for group in self.groups.values_mut() {
            for node in group.resources.drain(group.visit_range.start..) {
                node.destroy(factory);
            }

            group.visit_range = 0..group.resources.len();
        }
    }

    pub fn get(&self, id: &ResourceId<B, R>) -> Option<&R> {
        self.groups
            .get(&id.0)
            .and_then(|group| group.resources.get(id.1 as usize))
    }

    pub fn destroy(mut self, factory: &mut Factory<B>) {
        for (_, mut group) in self.groups.drain() {
            for resource in group.resources.drain(..) {
                resource.destroy(factory);
            }
        }
    }
}

impl<'a, B: Backend, R: Resource<B>> Index<&'a ResourceId<B, R>> for Resources<B, R> {
    type Output = R;

    fn index(&self, id: &'a ResourceId<B, R>) -> &R {
        self.get(id).unwrap()
    }
}