/* ------------------------------------------------------------------------*
 * Copyright 2013-2014 Arne F. Claassen
 *
 * 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.
 *-------------------------------------------------------------------------*/

var Josh = Josh || {};
(function(root, $, _) {
  Josh.PathHandler = function(shell, config) {
    config = config || {};
    var _console = config.console || (Josh.Debug && root.console ? root.console : {
      log: function() {
      }
    });
    var _shell = shell;
    _shell.templates.not_found = _.template("<div><%=cmd%>: <%=path%>: No such file or directory</div>");
    _shell.templates.ls = _.template("<div><% _.each(nodes, function(node) { %><span><%=node.name%>&nbsp;</span><% }); %></div>");
    _shell.templates.pwd = _.template("<div><%=node.path %>&nbsp;</div>");
    _shell.templates.prompt = _.template("<%= node.path %> $");
    var _original_default = _shell.getCommandHandler('_default');
    var self = {
      current: null,
      pathCompletionHandler: pathCompletionHandler,
      commandAndPathCompletionHandler: commandAndPathCompletionHandler,
      getNode: function(path, callback) {
        callback();
      },
      getChildNodes: function(node, callback) {
        callback([]);
      },
      getPrompt: function() {
        return _shell.templates.prompt({node: self.current});
      }
    };

    _shell.setCommandHandler("ls", {
      exec: ls,
      completion: pathCompletionHandler
    });
    _shell.setCommandHandler("pwd", {
      exec: pwd,
      completion: pathCompletionHandler
    });
    _shell.setCommandHandler("cd", {
      exec: cd,
      completion: pathCompletionHandler
    });
    _shell.setCommandHandler("_default", {
      exec: _original_default.exec,
      completion: commandAndPathCompletionHandler
    });
    _shell.onNewPrompt(function(callback) {
      callback(self.getPrompt());
    });

    function commandAndPathCompletionHandler(cmd, arg, line, callback) {
      _console.log("calling command and path completion handler w/ cmd: '"+cmd+"', arg: '"+arg+"'");
      if(!arg) {
        arg = cmd;
      }
      if(arg[0] == '.' || arg[0] == '/') {
        return pathCompletionHandler(cmd, arg, line, callback);
      }
      return _original_default.completion(cmd, arg, line, callback);
    }

    function pathCompletionHandler(cmd, arg, line, callback) {
      _console.log("completing '" + arg + "'");
      if(!arg) {
        _console.log("completing on current");
        return completeChildren(self.current, '', callback);
      }
      if(arg[arg.length - 1] == '/') {
        _console.log("completing children w/o partial");
        return self.getNode(arg, function(node) {
          if(!node) {
            _console.log("no node for path");
            return callback();
          }
          return completeChildren(node, '', callback);
        });
      }
      var partial = "";
      var lastPathSeparator = arg.lastIndexOf("/");
      var parent = arg.substr(0, lastPathSeparator + 1);
      partial = arg.substr(lastPathSeparator + 1);
      if(partial === '..' || partial === '.') {
        return callback({
          completion: '/',
          suggestions: []
        });
      }
      _console.log("completing children via parent '" + parent+"'  w/ partial '"+partial+"'");
      return self.getNode(parent, function(node) {
        if(!node) {
          _console.log("no node for parent path");
          return callback();
        }
        return completeChildren(node, partial, function(completion) {
          if(completion && completion.completion == '' && completion.suggestions.length == 1) {
            return callback({
              completion: '/',
              suggestions: []
            });
          }
          return callback(completion);
        });
      });
    }

    function completeChildren(node, partial, callback) {
      self.getChildNodes(node, function(childNodes) {
        callback(_shell.bestMatch(partial, _.map(childNodes, function(x) {
          return x.name;
        })));
      });
    }

    function cd(cmd, args, callback) {
      self.getNode(args[0], function(node) {
        if(!node) {
          return callback(_shell.templates.not_found({cmd: 'cd', path: args[0]}));
        }
        self.current = node;
        return callback();
      });
    }

    function pwd(cmd, args, callback) {
      callback(_shell.templates.pwd({node: self.current}));
    }

    function ls(cmd, args, callback) {
      _console.log('ls');
      if(!args || !args[0]) {
        return render_ls(self.current, self.current.path, callback);
      }
      return self.getNode(args[0], function(node) {
        render_ls(node, args[0], callback);
      });
    }

    function render_ls(node, path, callback) {
      if(!node) {
        return callback(_shell.templates.not_found({cmd: 'ls', path: path}));
      }
      return self.getChildNodes(node, function(children) {
        _console.log("finish render: " + node.name);
        callback(_shell.templates.ls({nodes: children}));
      });
    }

    return self;
  };
})(this, $, _);