use super::ModuleRef;
use crate::{Atom, AtomRef, ContextRef, Error, Result};
use std::path::Path;

pub enum ModuleSource<'a> {
    Native(&'a ModuleRef),
    JavaScript(String),
}

pub trait ModuleLoader {
    fn load<'a>(&mut self, context: &'a ContextRef, name: &str) -> Result<ModuleSource<'a>>;

    // Return true if this loader also supports source maps, otherwise false.
    // NOTE: This must return `true` if you want to have a non-trivial
    //       implementation of `map_source`; if this returns `false` (the
    //       default) then your object will never even receive a map_source
    //       call.
    fn support_source_map(&self) -> bool {
        false
    }

    // Map a given file/line pair to the source file/line pair. This is only
    // called if `support_source_map` returns true.
    fn map_source(&self, context: &ContextRef, file: &AtomRef, line: i32) -> Result<(Atom, i32)> {
        Ok((file.dup(context), line))
    }
}

pub struct DefaultModuleLoader {}

impl DefaultModuleLoader {
    pub fn new() -> DefaultModuleLoader {
        DefaultModuleLoader {}
    }
}

impl ModuleLoader for DefaultModuleLoader {
    fn load<'a>(&mut self, _context: &'a ContextRef, name: &str) -> Result<ModuleSource<'a>> {
        // Attempt to open the file.
        let path = Path::new(name);
        match std::fs::read_to_string(path) {
            Ok(str) => Ok(ModuleSource::JavaScript(str)),
            Err(e) => Err(Error::IOError(e)),
        }
    }
}
