/**
 * From https://github.com/felixge/nodelog/
 */

var sys = require('sys');
var tcp = require('net');
var irc = exports;

function bind(fn, scope) {
  var bindArgs = Array.prototype.slice.call(arguments);
  bindArgs.shift();
  bindArgs.shift();

  return function() {
    var args = Array.prototype.slice.call(arguments);
    fn.apply(scope, bindArgs.concat(args));
  };
}

function each(set, iterator) {
  for (var i = 0; i < set.length; i++) {
    var r = iterator(set[i], i);
    if (r === false) {
      return;
    }
  }
}

var Client = irc.Client = function(host, port) {
  this.host = host || 'localhost';
  this.port = port || 6667;

  this.connection = null;
  this.buffer = '';
  this.encoding = 'utf8';
  this.timeout = 10 * 60 * 60 * 1000;

  this.nick = null;
  this.user = null;
  this.real = null;
}
sys.inherits(Client, process.EventEmitter);

Client.prototype.connect = function(nick, user, real) {
  var connection = tcp.createConnection(this.port, this.host);
  connection.setEncoding(this.encoding);
  connection.setTimeout(this.timeout);
  connection.addListener('connect', bind(this.onConnect, this));
  connection.addListener('data', bind(this.onReceive, this));
  connection.addListener('end', bind(this.onEof, this));
  connection.addListener('timeout', bind(this.onTimeout, this));
  connection.addListener('close', bind(this.onClose, this));

  this.nick = nick;
  this.user = user || 'guest';
  this.real = real || 'Guest';

  this.connection = connection;
};

Client.prototype.disconnect = function(why) {
  if (this.connection.readyState !== 'closed') {
    this.connection.close();
    sys.puts('disconnected (reason: '+why+')');
    this.emit('DISCONNECT', why);
  }
};

Client.prototype.send = function(arg1) {
  if (this.connection.readyState !== 'open') {
    return this.disconnect('cannot send with readyState: '+this.connection.readyState);
  }

  var message = [];
  for (var i = 0; i< arguments.length; i++) {
    if (arguments[i]) {
      message.push(arguments[i]);
    }
  }
  message = message.join(' ');

  sys.puts('> '+message);
  message = message + "\r\n";
  this.connection.write(message, this.encoding);
};

Client.prototype.parse = function(message) {
  var match = message.match(/(?:(:[^\s]+) )?([^\s]+) (.+)/);
  var parsed = {
    prefix: match[1],
    command: match[2]
  };

  var params = match[3].match(/(.*?) ?:(.*)/);
  if (params) {
    // Params before :
    params[1] = (params[1])
      ? params[1].split(' ')
      : [];
    // Rest after :
    params[2] = params[2]
      ? [params[2]]
      : [];

    params = params[1].concat(params[2]);
  } else {
    params = match[3].split(' ');
  }

  parsed.params = params;
  return parsed;
};

Client.prototype.onConnect = function() {
  this.send('NICK', this.nick);
  this.send('USER', this.user, '0', '*', ':'+this.real);
};

Client.prototype.onReceive = function(chunk) {
  this.buffer = this.buffer + chunk;
  
  while (this.buffer) {
    var offset = this.buffer.indexOf("\r\n");
    if (offset < 0) {
      return;
    }
  
    var message = this.buffer.substr(0, offset);
    this.buffer = this.buffer.substr(offset + 2);
    sys.puts('< '+message);

    message = this.parse(message);

    this.emit.apply(this, [message.command, message.prefix].concat(message.params));

    if (message !== false) {
      this.onMessage(message);
    }
  }
}; 

Client.prototype.onMessage = function(message) {
  switch (message.command) {
    case 'PING':
      this.send('PONG', ':'+message.params[0]);
      break;
  }
};

Client.prototype.onEof = function() {
  this.disconnect('eof');
};

Client.prototype.onTimeout = function() {
  this.disconnect('timeout');
};

Client.prototype.onClose = function() {
  this.disconnect('close');
};

exports.user = function(prefix) {
  return prefix.match(/:([^!]+)!/)[1]
};
