var Command = function (command) {
  this.command = [];
  this.callback = {};
  return this.setCommand(command);
};

Command.scope = window;
Command.timeout = 60000;
Command.frequency = 10;

Command.prototype.setCommand = function (command) {
  if (command instanceof Array) { // ex. [ ['cmd1', 'arg1'], ['cmd2', 'arg1', 'arg2'], ['cmd3'] ]
    this.command = command;
  } else if (typeof command == 'string' || command instanceof String) {
    command = command.replace(/\s/g, ' ');

    var escaped = [], quote1 = [], quote2 = [];
    command = command.replace(/\\(.)/g, function (match, s1) {
      return (escaped.push(s1), '\t0'); // for pasing after
    }).replace(/"(.*?)"/g, function (match, s1) {
      return (quote1.push(s1), '\t1'); // for pasing after
    }).replace(/'(.*?)'/g, function (match, s1) {
      return (quote2.push(s1), '\t2'); // for pasing after
    });

    var by_pipe = this.command = command.split(/ *\| */);
    for (var i = 0; i < by_pipe.length; ++i) {
      var args = by_pipe[i] = by_pipe[i].split(/ +/);
      for (var j = 0; j < args.length; ++j) {
        args[j] = args[j].replace(/\t2/g, function () {
          return quote2.shift();
        }).replace(/\t1/g, function () {
          return quote1.shift();
        }).replace(/\t0/g, function () {
          return escaped.shift();
        });
      }
    }
  }
  return this;
};

Command.prototype.addEventListener = function (event, callback) {
  this.callback[event] = (this.callback[event] || []).concat([callback]);
};

Command.prototype.removeEventListener = function (event, callback) {
  for (var i = 0; i < this.callback[event].length; ++i) {
    if (this.callback[event][i] == callback) {
      this.callback[event].splice(i, 1);
      --i;
    }
  }
};

Command.prototype.loadCommand = function () {
  for (var i = 0; i < this.command.length; ++i) {
    var cmd = this.command[i][0];
    if (Command.scope[cmd.replace(/^.*?([0-9A-Za-z_]+)$/, '$1')] == null) {
      var s = document.createElement('script');
      s.src = cmd + '.js';
      document.body.appendChild(s);
    }
  }
};

Command.prototype.execute = function (arg) {
  this.loadCommand();
  this.nextCommand({ index: 0, start: new Date, result: arg || [], command: '' });
};

Command.prototype.nextCommand = function (state) {
  if ((new Date).getTime() - state.start.getTime() > Command.timeout) {
    return this.event('timeout', state);
  }

  var self = this, i = state.index == this.command.length ? 0 : state.index,
    cmd = this.command[i][0], args = this.command[i].slice(1);

  cmd = cmd.replace(/^.*?([0-9A-Za-z_]+)$/, '$1');
  if (Command.scope[cmd] != null && !state.result.loading) {
    if (state.command) {
      this.event(state.command, state);
      this.event('execute', state);
      if (this.command.length == state.index) {
        return this.event('result', state);
      }
    }
    state.command = cmd;
    state.arguments = args;
    state.result = Command.scope[cmd].apply(null, [state.result].concat(args));
    ++state.index;
  }

  window.setTimeout(function () {
    self.nextCommand(state);
  }, Command.frequency);
};

Command.prototype.event = function (event, state) {
  if (!this.callback[event] && !this['on' + event]) {
    return;
  }

  if (typeof this['on' + event] == 'function') {
    this['on' + event].call(this, state);
  }

  for (var i = 0; i < this.callback[event].length; ++i) {
    this.callback[event][i].call(this, state);
  }
};
