/**
 * @license
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.classloader',
  name: 'NodeModelFileDAO',
  extends: 'foam.dao.AbstractDAO',

  properties: [
    {
      name: 'classpath'
    },
    {
      name: 'loadedModels',
      documentation: `Retain a copy of loaded models in case they are requested
        later. This can happen, for example, if a modl is loaded twice in
        different contexts. Subsequent require() calls will not re-invoke
        foam.CLASS(), hence the model loaded the first time must be retained.`,
      value: {}
    }
  ],

  methods: [
    function find(id) {
      var foamCLASS = foam.CLASS;
      var self = this;
      var model = this.loadedModels[id] || null;

      if ( model ) return Promise.resolve(model);

      foam.CLASS = function(clsDef) {
        var classOfModel = clsDef.class ? foam.lookup(clsDef.class) :
              foam.core.Model;
        var modelOfClass = classOfModel.create(clsDef, self);
        if ( modelOfClass.id === id ) {
          model = modelOfClass;
        } else {
          // TODO(markdittmer): We should do something more reasonable here, but
          // the DAO API only allows us to deliver one model in response to
          // find().
          console.warn(
            'Class', id, 'created via arequire, but never built or registered');
        }
        self.loadedModels[modelOfClass.id] = modelOfClass;
      };

      var sep = require('path').sep;
      var path = this.classpath + sep + id.replace(/\./g, sep) + '.js';

      try {
        require(path);
      } catch(e) {
        console.warn('Unable to load at ' + path + '. Error: ' + e.stack);
        return Promise.resolve(null);
      } finally {
        foam.CLASS = foamCLASS;
      }

      return Promise.resolve(model);
    }
  ]
});

@@ -748,30 +748,41 @@
 ies: %5B%0A    %7B
-%0A     
+ class: 'String',
  name: 'clas
@@ -791,35 +791,26 @@
 ath'
-%0A   
  %7D,%0A
+%0A
     %7B
-%0A     
  name: '
 load
@@ -809,350 +809,213 @@
 e: '
-loadedModels',%0A      documentation: %60Retain a copy of loaded models in case they are requested%0A        later. This can happen, for example, if a modl is loaded twice in%0A        different contexts. Subsequent require() calls will not re-invoke%0A        foam.CLASS(), hence the model loaded the first time must be retained.%60,%0A      value: %7B%7D%0A   
+sep', factory: function() %7B return require('path').sep; %7D %7D,%0A    %7B name: 'fs',  factory: function() %7B return require('fs');       %7D %7D,%0A    %7B name: 'vm',  factory: function() %7B return require('vm');       %7D
  %7D%0A 
@@ -1135,88 +1135,12 @@
 l = 
-this.loadedModels%5Bid%5D %7C%7C null;%0A%0A      if ( model ) return Promise.resolve(model)
+null
 ;%0A%0A 
@@ -1166,22 +1166,17 @@
 unction(
-clsDef
+m
 ) %7B%0A    
@@ -1189,27 +1189,13 @@
 r cl
-assOfModel = clsDef
+s = m
 .cla
@@ -1211,22 +1211,17 @@
 .lookup(
-clsDef
+m
 .class) 
@@ -1270,42 +1270,24 @@
 ar m
-odelOfClass = classOfModel
+dl = cls
 .create(
 clsD
@@ -1286,14 +1286,9 @@
 ate(
-clsDef
+m
 , se
@@ -1306,27 +1306,18 @@
   if ( m
-odelOfClass
+dl
 .id === 
@@ -1338,35 +1338,26 @@
    model = m
-odelOfClass
+dl
 ;%0A        %7D 
@@ -1666,107 +1666,11 @@
     
-  self.loadedModels%5BmodelOfClass.id%5D = modelOfClass;%0A      %7D;%0A%0A      var sep = require('path').sep;
+%7D;%0A
 %0A   
@@ -1700,16 +1700,21 @@
 spath + 
+this.
 sep + id
@@ -1733,22 +1733,39 @@
 /g, 
+this.
 sep) +
+%0A           
  '.js';%0A
 %0A   
@@ -1764,194 +1764,430 @@
 s';%0A
-%0A
       
-try %7B%0A        require(path);%0A      %7D catch(e) %7B%0A        console.warn('Unable to load at ' + path + '. Error: ' + e.stack);%0A        return Promise.resolve(null);%0A      %7D finally %7B%0A
+return new Promise(function(resolve, reject) %7B%0A        self.fs.readFile(path, 'utf8', function(error, data) %7B%0A          if ( error ) %7B%0A            console.warn('Unable to load at ' + path + '. Error: ' +%0A                         error.message + '%5Cn' + error.stack);%0A            foam.CLASS = foamCLASS;%0A            resolve(null);%0A          %7D%0A%0A          self.vm.runInThisContext(data.toString(), %7Bfilename: path%7D);%0A%0A  
     
@@ -2222,21 +2222,16 @@
     
-  %7D%0A%0A
       re
 turn
@@ -2230,34 +2230,41 @@
   re
-turn Promise.resolve(model
+solve(model);%0A        %7D);%0A      %7D
 );%0A 

Implement NodeModelFileDAO over 'fs' and 'vm' Node APIs, rather than over require()