use crate::binxml::deserializer::Context;
use crate::binxml::tokens::read_template_definition;

use crate::model::deserialized::BinXMLTemplateDefinition;
use crate::Offset;
pub use byteorder::{LittleEndian, ReadBytesExt};
use std::collections::HashMap;
use std::io::{Cursor, Seek, SeekFrom};
use std::rc::Rc;

pub type CachedTemplate<'c> = (Rc<BinXMLTemplateDefinition<'c>>);

#[derive(Debug)]
pub struct TemplateCache<'c>(HashMap<Offset, CachedTemplate<'c>>);

impl<'c> TemplateCache<'c> {
    pub fn new() -> Self {
        TemplateCache(HashMap::new())
    }

    pub fn populate(&mut self, data: &'c [u8], offsets: &[Offset]) -> Result<(), failure::Error> {
        let mut cursor = Cursor::new(data);
        for offset in offsets.iter().filter(|&&offset| offset > 0) {
            cursor.seek(SeekFrom::Start(*offset as u64))?;
            let definition = read_template_definition(&mut cursor, Context::default())?;
            self.0.insert(*offset, Rc::new(definition));
        }

        Ok(())
    }

    pub fn get_template(&self, offset: Offset) -> Option<CachedTemplate<'c>> {
        self.0.get(&offset).and_then(|t| Some(Rc::clone(t)))
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }
}
