// Generated by CoffeeScript 2.5.0
(function() {
  var ConfigNode, Root, expandItem, initTree, itemDependencies, normalizePath, toposort;

  ConfigNode = require("./config-node");

  toposort = require("toposort");

  normalizePath = function(segments) {
    var i, len, result, segment;
    result = [];
    for (i = 0, len = segments.length; i < len; i++) {
      segment = segments[i];
      switch (segment) {
        case '/':
          result = [];
          break;
        case '.':
          result = result;
          break;
        case '..':
          if (result.length < 1) {
            throw new Error("unexpected ..");
          }
          result.pop();
          break;
        default:
          result.push(segment);
      }
    }
    return result;
  };

  expandItem = function(root, {node, path}) {
    var dict, key, results, value;
    if ((node == null) || typeof node !== "object") {
      return [];
    }
    dict = node instanceof ConfigNode ? node._expand(root, path) : node;
    results = [];
    for (key in dict) {
      value = dict[key];
      results.push({
        node: value,
        path: [...path, key]
      });
    }
    return results;
  };

  itemDependencies = function(root, {node, path}) {
    var depPath, deps, dict, key;
    if ((node == null) || typeof node !== "object") {
      ({});
    }
    dict = node instanceof ConfigNode ? node.dependencies(root, path) : {};
    deps = {};
    for (key in dict) {
      depPath = dict[key];
      deps[key] = normalizePath([...path, ...depPath]);
    }
    return deps;
  };

  // This method is intended to be called *once* by the configuration subsystem
  // after all configuration files, cli options, etc have been processed.  It
  // will recursively traverse the configuration tree assuming this node as its
  // root. While doing so, it will create a dependency graph which is then used
  // to initialize all nodes in topological order.
  initTree = function(root) {
    var alias, child, children, depPath, dependencies, deps, depvals, i, item, j, key, len, len1, lookup, node, path, ref, ref1, results, sortedKeys, todo;
    todo = [
      {
        node: root,
        path: []
      }
    ];
    lookup = {};
    deps = [];
    while (todo.length > 0) {
      ({node, path} = item = todo.pop());
      lookup[path] = item;
      children = expandItem(root, item);
      dependencies = item.deps = itemDependencies(root, item);
      for (i = 0, len = children.length; i < len; i++) {
        child = children[i];
        deps.push([path.toString(), child.path.toString()]);
        todo.push(child);
      }
      for (alias in dependencies) {
        depPath = dependencies[alias];
        deps.push([path.toString(), depPath.toString()]);
      }
    }
    sortedKeys = toposort(deps).reverse();
    results = [];
    for (j = 0, len1 = sortedKeys.length; j < len1; j++) {
      key = sortedKeys[j];
      ({node, path, deps} = (ref = lookup[key]) != null ? ref : {});
      //if not node?
      //  console.error "Warning: no node for path "+key
      if ((node != null) && node instanceof ConfigNode) {
        depvals = {};
        for (alias in deps) {
          depPath = deps[alias];
          depvals[alias] = (ref1 = lookup[depPath]) != null ? ref1.node : void 0;
        }
        results.push(node.init(depvals));
      } else {
        results.push(void 0);
      }
    }
    return results;
  };

  module.exports = Root = class Root extends ConfigNode {
    constructor(options) {
      var key, value;
      super(options);
      for (key in options) {
        value = options[key];
        // make all options available as member variables
        this[key] = value;
      }
      initTree(this);
    }

  };

  module.exports.normalizePath = normalizePath;

}).call(this);
