(function() {
  var T, assert, createApiTree, createOptions, find, findDir;

  createApiTree = require('../lib/apitree').createApiTree;

  assert = require('assert');

  find = function(tree, path) {
    var component, _i, _len, _ref;
    _ref = path.split('/');
    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
      component = _ref[_i];
      if (!(component !== '.')) continue;
      tree = tree[component];
      if (!tree) throw new Error("Path not found");
    }
    return tree;
  };

  findDir = function(tree, path) {
    var item;
    item = find(tree, path);
    if (item.length == null) return item;
  };

  createOptions = function(tree, options) {
    if (options == null) options = {};
    options.readdirSync = function(path) {
      var item;
      if (item = findDir(tree, path)) {
        return Object.keys(item).sort();
      } else {
        throw new Error("Not a directory");
      }
    };
    options.isDirectory = function(path) {
      return !!findDir(tree, path);
    };
    options.loadItem = function(path) {
      var index, name, result, _len, _ref;
      result = {};
      _ref = find(tree, path);
      for (index = 0, _len = _ref.length; index < _len; index++) {
        name = _ref[index];
        result[name] = "i" + index;
      }
      return result;
    };
    return options;
  };

  T = function(tree, options) {
    if (options == null) options = {};
    return createApiTree('.', createOptions(tree, options));
  };

  describe("API tree", function() {
    describe("when given an empty folder", function() {
      var tree;
      tree = T({});
      return it("should return an empty tree", function() {
        return assert.deepEqual(tree, {});
      });
    });
    describe("when given a folder with a single file", function() {
      var tree;
      tree = T({
        'foo.js': ['bar', 'boz']
      });
      it("should put the file node under the tree root", function() {
        return assert.equal(Object.keys(tree).length, 1);
      });
      it("should strip the extension when naming the tree node", function() {
        return assert.deepEqual(Object.keys(tree), ['foo']);
      });
      return it("should put the file's contents under its node", function() {
        return assert.deepEqual(Object.keys(tree.foo).sort(), ['bar', 'boz']);
      });
    });
    describe("when given a file and a subfolder", function() {
      var tree;
      tree = T({
        'foo.js': ['bar', 'boz'],
        fold: {
          'fil.js': ['fubar']
        }
      });
      return it("should put the file and subfolder nodes together under the tree root", function() {
        return assert.deepEqual(Object.keys(tree).sort(), ['fold', 'foo']);
      });
    });
    describe("when given a file and a subfolder which have the same name after stripping extensions", function() {
      var tree;
      tree = T({
        'foo.js': ['bar', 'boz'],
        foo: {
          'fil.js': ['fubar']
        }
      });
      return it("should merge the file and the subfolder into a single node under the tree root", function() {
        assert.deepEqual(Object.keys(tree), ['foo']);
        return assert.deepEqual(Object.keys(tree.foo).sort(), ['bar', 'boz', 'fil']);
      });
    });
    describe("when given a folder hierarchy with nested subfolders", function() {
      var tree;
      tree = T({
        foo: {
          bar: {
            'boz.js': ['fubar']
          }
        }
      });
      return it("should reproduce the folder hierarachy inside the API tree", function() {
        assert.deepEqual(Object.keys(tree), ['foo']);
        assert.deepEqual(Object.keys(tree.foo), ['bar']);
        assert.deepEqual(Object.keys(tree.foo.bar), ['boz']);
        return assert.deepEqual(Object.keys(tree.foo.bar.boz), ['fubar']);
      });
    });
    describe("loadItem callback", function() {
      return it("should be used to obtain file contents", function() {
        var tree;
        tree = T({
          'foo.js': ['bar', 'boz']
        });
        assert.equal(tree.foo.bar, 'i0');
        return assert.equal(tree.foo.boz, 'i1');
      });
    });
    describe("nameToKey callback", function() {
      var args, nameToKey;
      args = null;
      nameToKey = function(name) {
        args = arguments;
        return "A" + (name.split('.')[0]) + "Z";
      };
      it("should accept file name as the only argument", function() {
        var tree;
        tree = T({
          'foo.js': ['bar', 'boz']
        }, {
          nameToKey: nameToKey
        });
        assert.equal(args.length, 1);
        return assert.equal(args[0], 'foo.js');
      });
      it("should be used to translate file names into tree keys", function() {
        var tree;
        tree = T({
          'foo.js': ['bar', 'boz']
        }, {
          nameToKey: nameToKey
        });
        return assert.deepEqual(Object.keys(tree), ['AfooZ']);
      });
      it("should be used to translate subfolder names into tree keys", function() {
        var tree;
        tree = T({
          foo: {
            'bar.js': ['bar', 'boz']
          }
        }, {
          nameToKey: nameToKey
        });
        return assert.deepEqual(Object.keys(tree), ['AfooZ']);
      });
      return it("should not be used to modify keys returned by loadItem", function() {
        var tree;
        tree = T({
          'foo.js': ['bar', 'boz']
        }, {
          nameToKey: nameToKey
        });
        return assert.deepEqual(Object.keys(tree.AfooZ).sort(), ['bar', 'boz']);
      });
    });
    describe("default nameToKey callback", function() {
      it("should strip file extension", function() {
        var tree;
        tree = T({
          'foo.js': ['bar']
        });
        return assert.deepEqual(Object.keys(tree), ['foo']);
      });
      it("should replace any non-identifier characters with underscores", function() {
        var tree;
        tree = T({
          'foo-bar.js': ['bar']
        });
        return assert.deepEqual(Object.keys(tree), ['foo_bar']);
      });
      return it("should replace runs of multiple non-identifier characters with a single underscore", function() {
        var tree;
        tree = T({
          'foo!!bar.js': ['bar']
        });
        return assert.deepEqual(Object.keys(tree), ['foo_bar']);
      });
    });
    describe("filter callback", function() {
      var args, filter;
      args = null;
      filter = function(name, names) {
        args = arguments;
        return name === 'foo.js';
      };
      it("should accept file name as the first argument", function() {
        var tree;
        tree = T({
          'foo.js': ['fu']
        }, {
          filter: filter
        });
        return assert.equal(args[0], 'foo.js');
      });
      it("should accept the list of all file names in the same folder as the second argument", function() {
        var tree;
        tree = T({
          'foo.js': ['fu'],
          'bar.js': ['ba']
        }, {
          filter: filter
        });
        assert.deepEqual(args[1].sort(), ['bar.js', 'foo.js']);
        return assert.equal(args.length, 2);
      });
      it("should be used to choose which files to process", function() {
        var tree;
        tree = T({
          'foo.js': ['fu'],
          'bar.js': ['ba']
        }, {
          filter: filter
        });
        return assert.deepEqual(Object.keys(tree), ['foo']);
      });
      return it("should have no effect on which folders are processed", function() {
        var tree;
        tree = T({
          'foo.js': ['fu'],
          'fold': {
            'foo.js': ['uf'],
            'bar.js': ['ba']
          }
        }, {
          filter: filter
        });
        assert.deepEqual(Object.keys(tree).sort(), ['fold', 'foo']);
        return assert.deepEqual(Object.keys(tree.fold), ['foo']);
      });
    });
    return describe("default filter callback", function() {
      var tree;
      tree = T({
        'foo.js': ['xx'],
        'bar.js': ['yy'],
        'foo.txt': ['zz'],
        'Rakefile': ['rr'],
        'boz.coffee': ['cc'],
        'both.js': ['bb'],
        'both.coffee': ['BB']
      }, {
        nameToKey: function(name) {
          return name;
        }
      });
      it("should include .js files", function() {
        assert.ok('foo.js' in tree);
        return assert.ok('bar.js' in tree);
      });
      it("should include .coffee files that don't have corresponding .js files", function() {
        return assert.ok('boz.coffee' in tree);
      });
      it("should only include .js file when both .js and .coffee files exist", function() {
        assert.ok('both.js' in tree);
        return assert.ok(!('both.coffee' in tree));
      });
      return it("should not include any other files", function() {
        assert.ok(!('Rakefile' in tree));
        return assert.ok(!('foo.txt' in tree));
      });
    });
  });

}).call(this);
