use preserves::value::IOValue;

use preserves_schema::compiler::load_schema_or_bundle;
use preserves_schema::gen::schema::Definition;

use std::io;

#[derive(Default)]
pub struct Env(pub preserves_schema::support::interpret::Env<IOValue>);

pub struct Context<'a> {
    pub env: &'a Env,
    path: Vec<IOValue>,
}

impl<'a> Context<'a> {
    pub fn new(env: &'a Env) -> Self {
        Context {
            env,
            path: Vec::new(),
        }
    }

    pub fn with_path_step<R, F: FnOnce(&mut Self) -> R>(&mut self, v: &IOValue, f: F) -> R {
        self.path.push(v.clone());
        let result = f(self);
        self.path.pop();
        result
    }
}

impl Env {
    pub fn new() -> Self {
        Default::default()
    }

    pub fn load_bundle(&mut self, filename: &std::path::PathBuf) -> io::Result<()> {
        load_schema_or_bundle(&mut self.0, filename)
    }

    pub fn lookup_definition(&self, module: &Vec<String>, name: &str) -> Option<&Definition<IOValue>> {
        self.0.get(module).and_then(|s| s.definitions.0.get(name))
    }

    pub fn to_context(&self) -> Context {
        Context::new(self)
    }
}
