var ArgsParser = require('./args-parser');
var FS = require('./fs');
var File = require('./file');
var stream = require('stream');

var path = FS.open('/usr/bin');
var load = (function () {
  var cache = {};
  return function (cmd) {
    if (typeof cache[cmd] !== 'undefined') {
      return cache[cmd];
    } else {
      var source = path[cmd + '.js'];
      var fn;
      if (!source) {
        fn = false;
      } else {
        fn = eval('(function () { ' + source.content + '})')();
      }
      cache[cmd] = fn;
      return fn;
    }
  };
})();

var CommandManager = {
  commands: {},
  aliases: {},
};

for (var key in path) {
  CommandManager.commands[key.replace('.js' ,'')] = true;
}

CommandManager.isValid = function (cmd) {
  return !!(this.commands[cmd] || this.aliases[cmd]);
};

CommandManager.autocomplete = function (cmd) {
  var matches = [];
  cmd = cmd.toLowerCase();

  (Object.keys(this.commands).concat(Object.keys(this.aliases))).forEach(function (command) {
    if (command.substr(0, cmd.length).toLowerCase() === cmd) {
      matches.push(command);
    }
  });

  return matches;
};
CommandManager.parse = function (cmd, stdin, stdout, stderr, next) {
  if (~cmd.indexOf('|')) {
    cmd = cmd.split('|');
    cmd.forEach(CommandManager.parse);
  }

  cmd = cmd.split(' ');
  var command = cmd.shift();
  var args = cmd.join(' ');

  var index;

  if (~(index = args.indexOf('>'))) {
    var prev = args[index-1];
    var append = args[index+1] === '>';
    var init = index;

    if (~(['1','2','&']).indexOf(prev)) {
      init--;
    }
    
    var _args = args.substr(0, init);
    args = args.substr(index+append+1).split(' ').filter(String);
    var path = args.shift();
    args = _args + args.join(' ');

    if (!path) {
      stdout.write('zsh: parse error near `\\n\'');
      return;
    }

    var file = new File(path);

    if (!file.parentExists()) {
      stdout.write('zsh: no such file or directory: ' + file.path);
      return;
    } else if (!file.isValid()) {
      stdout.write('zsh: not a directory: ' + file.path);
      return;
    } else if (file.isDir()) {
      stdout.write('zsh: is a directory: ' + file.path);
      return;
    }

    if (!append) {
      file.clear();
    }

    var _stdout = new stream.PassThrough();
    _stdout.on('data', function(data) {
      file.write(data + '\n', true, true);
    });

    if (prev !== '2') {
      stdout = _stdout;
    }

    if (prev === '2' || prev === '&') {
      stderr = _stdout;
    }

    var _next = next;
    next = function () {
      FS.writeFS();
      _next();
    };
  }

  CommandManager.exec(command, args, stdin, stdout, stderr, next);
};

CommandManager.exec = function (cmd, args, stdin, stdout, stderr, next) {
  if (this.aliases[cmd]) {
    var line = (this.aliases[cmd] + ' ' + args).trim().split(' ');
    return this.exec(line.shift(), line.join(' '), stdin, stdout, stderr, next);
  }

  var fn;
  if (typeof this.commands[cmd] === 'function') {
    fn = this.commands[cmd];
  } else if ((fn = load(cmd))) {
  } else {
    stderr.write('zsh: command not found: ' + cmd);
    next();
    return;
  }

  try {
    args = ArgsParser.parse(args);
    fn.call(undefined, args, stdin, stdout, stderr, next);
  } catch (err) {
    stderr.write(err.stack);
    next();
  }
};

CommandManager.register = function (cmd, fn) {
  this.commands[cmd] = fn;
};

CommandManager.alias = function (cmd, original) {
  if (arguments.length === 0) {
    return this.aliases;
  }
  this.aliases[cmd] = original;
};

CommandManager.unalias = function (cmd) {
  delete this.aliases[cmd];
};

CommandManager.get = function(cmd) {
  return this.commands[cmd];
};

module.exports = CommandManager;

@@ -172,154 +172,25 @@
 d = 
-(function () %7B%0A  var cache = %7B%7D;%0A  return function (cmd) %7B%0A    if (typeof cache%5Bcmd%5D !== 'undefined') %7B%0A      return cache%5Bcmd%5D;%0A    %7D else %7B%0A    
+function (cmd) %7B%0A
   va
@@ -219,20 +219,16 @@
 '.js'%5D;%0A
-    
   var fn
@@ -229,20 +229,16 @@
 var fn;%0A
-    
   if (!s
@@ -246,20 +246,16 @@
 urce) %7B%0A
-    
     fn =
@@ -262,20 +262,16 @@
  false;%0A
-    
   %7D else
@@ -277,20 +277,16 @@
 e %7B%0A    
- 
-   
 fn = eva
@@ -339,213 +339,83 @@
 ;%0A  
-    %7D%0A      cache%5Bcmd%5D = fn;%0A      return fn;%0A    %7D%0A  %7D;%0A%7D)();%0A%0Avar CommandManager = %7B%0A  commands: %7B%7D,%0A  aliases: %7B%7D,%0A%7D;%0A%0Afor (var key in path) %7B%0A  CommandManager.commands%5Bkey.replace('.js' ,'')%5D = true;%0A%7D
+%7D%0A  return fn;%0A%7D;%0A%0Avar CommandManager = %7B%0A  commands: %7B%7D,%0A  aliases: %7B%7D,%0A%7D;
 %0A%0ACo
@@ -505,16 +505,29 @@
 ses%5Bcmd%5D
+ %7C%7C load(cmd)
 );%0A%7D;%0A%0AC

Update isValid + remove cache