/** On Nick Change */
  @Override
  protected void onNickChange(String oldNick, String login, String hostname, String newNick) {
    if (getNick().equalsIgnoreCase(newNick)) {
      this.updateNickMatchPattern();

      // Send message about own change to server info window
      Message message = new Message(service.getString(R.string.message_self_rename, newNick));
      message.setColor(Message.COLOR_GREEN);
      server.getConversation(ServerInfo.DEFAULT_NAME).addMessage(message);

      Intent intent =
          Broadcast.createConversationIntent(
              Broadcast.CONVERSATION_MESSAGE, server.getId(), ServerInfo.DEFAULT_NAME);

      service.sendBroadcast(intent);
    }

    Vector<String> channels = getChannelsByNickname(newNick);

    for (String target : channels) {
      Message message = new Message(service.getString(R.string.message_rename, oldNick, newNick));
      message.setColor(Message.COLOR_GREEN);
      server.getConversation(target).addMessage(message);

      Intent intent =
          Broadcast.createConversationIntent(
              Broadcast.CONVERSATION_MESSAGE, server.getId(), target);
      service.sendBroadcast(intent);
    }
  }
  /** On Invite */
  @Override
  protected void onInvite(
      String targetNick,
      String sourceNick,
      String sourceLogin,
      String sourceHostname,
      String target) {
    if (targetNick.equals(this.getNick())) {
      // We are invited
      Message message =
          new Message(service.getString(R.string.message_invite_you, sourceNick, target));
      server.getConversation(server.getSelectedConversation()).addMessage(message);

      Intent intent =
          Broadcast.createConversationIntent(
              Broadcast.CONVERSATION_MESSAGE, server.getId(), server.getSelectedConversation());
      service.sendBroadcast(intent);
    } else {
      // Someone is invited
      Message message =
          new Message(
              service.getString(R.string.message_invite_someone, sourceNick, targetNick, target));
      server.getConversation(target).addMessage(message);

      Intent intent =
          Broadcast.createConversationIntent(
              Broadcast.CONVERSATION_MESSAGE, server.getId(), target);
      service.sendBroadcast(intent);
    }
  }
  /** On connect */
  @Override
  public void onConnect() {
    server.setStatus(Status.CONNECTED);

    server.setMayReconnect(true);

    ignoreMOTD = service.getSettings().isIgnoreMOTDEnabled();

    service.sendBroadcast(Broadcast.createServerIntent(Broadcast.SERVER_UPDATE, server.getId()));

    service.notifyConnected(server.getTitle());

    Message message = new Message(service.getString(R.string.message_connected, server.getTitle()));
    message.setColor(Message.COLOR_GREEN);
    server.getConversation(ServerInfo.DEFAULT_NAME).addMessage(message);

    Message infoMessage = new Message(service.getString(R.string.message_now_login));
    infoMessage.setColor(Message.COLOR_GREY);
    server.getConversation(ServerInfo.DEFAULT_NAME).addMessage(infoMessage);

    Intent intent =
        Broadcast.createConversationIntent(
            Broadcast.CONVERSATION_MESSAGE, server.getId(), ServerInfo.DEFAULT_NAME);

    if (server.getAuthentication().hasNickservCredentials()) {
      identify(server.getAuthentication().getNickservPassword());
    }

    service.sendBroadcast(intent);
  }
  /** On Kick */
  @Override
  protected void onKick(
      String target,
      String kickerNick,
      String kickerLogin,
      String kickerHostname,
      String recipientNick,
      String reason) {
    if (recipientNick.equals(getNick())) {
      // We are kicked
      service.ackNewMentions(server.getId(), target);
      server.removeConversation(target);

      Intent intent =
          Broadcast.createConversationIntent(Broadcast.CONVERSATION_REMOVE, server.getId(), target);
      service.sendBroadcast(intent);
    } else {
      Message message =
          new Message(service.getString(R.string.message_kick, kickerNick, recipientNick));
      message.setColor(Message.COLOR_GREEN);
      server.getConversation(target).addMessage(message);

      Intent intent =
          Broadcast.createConversationIntent(
              Broadcast.CONVERSATION_MESSAGE, server.getId(), target);
      service.sendBroadcast(intent);
    }
  }
  /** On Join */
  @Override
  protected void onJoin(String target, String sender, String login, String hostname) {
    if (sender.equalsIgnoreCase(getNick()) && server.getConversation(target) == null) {
      // We joined a new channel
      Conversation conversation = new Channel(target);
      conversation.setHistorySize(service.getSettings().getHistorySize());
      server.addConversation(conversation);

      Intent intent =
          Broadcast.createConversationIntent(Broadcast.CONVERSATION_NEW, server.getId(), target);
      service.sendBroadcast(intent);
    } else if (service.getSettings().showJoinPartAndQuit()) {
      Message message =
          new Message(service.getString(R.string.message_join, sender), Message.TYPE_MISC);

      message.setIcon(R.drawable.join);
      message.setColor(Message.COLOR_GREEN);
      server.getConversation(target).addMessage(message);

      Intent intent =
          Broadcast.createConversationIntent(
              Broadcast.CONVERSATION_MESSAGE, server.getId(), target);
      service.sendBroadcast(intent);
    }
  }
  /** On Quit */
  @Override
  protected void onQuit(
      String sourceNick, String sourceLogin, String sourceHostname, String reason) {
    if (sourceNick.equals(this.getNick())) {
      return;
    }

    if (service.getSettings().showJoinPartAndQuit()) {
      Vector<String> channels = getChannelsByNickname(sourceNick);

      for (String target : channels) {
        Message message =
            new Message(
                service.getString(R.string.message_quit, sourceNick, reason), Message.TYPE_MISC);

        message.setColor(Message.COLOR_GREEN);
        message.setIcon(R.drawable.quit);
        server.getConversation(target).addMessage(message);

        Intent intent =
            Broadcast.createConversationIntent(
                Broadcast.CONVERSATION_MESSAGE, server.getId(), target);
        service.sendBroadcast(intent);
      }

      // Look if there's a query to update
      Conversation conversation = server.getConversation(sourceNick);

      if (conversation != null) {
        Message message =
            new Message(
                service.getString(R.string.message_quit, sourceNick, reason), Message.TYPE_MISC);

        message.setColor(Message.COLOR_GREEN);
        message.setIcon(R.drawable.quit);
        conversation.addMessage(message);

        Intent intent =
            Broadcast.createConversationIntent(
                Broadcast.CONVERSATION_MESSAGE, server.getId(), conversation.getName());
        service.sendBroadcast(intent);
      }
    }
  }
  /** On remove moderated */
  @Override
  protected void onRemoveModerated(
      String target, String sourceNick, String sourceLogin, String sourceHostname) {
    Message message = new Message(service.getString(R.string.message_remove_moderated, sourceNick));
    message.setColor(Message.COLOR_BLUE);
    server.getConversation(target).addMessage(message);

    service.sendBroadcast(
        Broadcast.createConversationIntent(Broadcast.CONVERSATION_MESSAGE, server.getId(), target));
  }
  /** On set channel limit */
  @Override
  protected void onSetChannelLimit(
      String target, String sourceNick, String sourceLogin, String sourceHostname, int limit) {
    Message message =
        new Message(service.getString(R.string.message_set_channel_limit, sourceNick, limit));
    message.setColor(Message.COLOR_BLUE);
    server.getConversation(target).addMessage(message);

    service.sendBroadcast(
        Broadcast.createConversationIntent(Broadcast.CONVERSATION_MESSAGE, server.getId(), target));
  }
  /** On Topic */
  @Override
  public void onTopic(String target, String topic, String setBy, long date, boolean changed) {
    if (changed) {
      Message message = new Message(service.getString(R.string.message_topic_set, setBy, topic));
      message.setColor(Message.COLOR_YELLOW);
      server.getConversation(target).addMessage(message);
    } else {
      Message message = new Message(service.getString(R.string.message_topic, topic));
      message.setColor(Message.COLOR_YELLOW);
      server.getConversation(target).addMessage(message);
    }

    // remember channel's topic
    ((Channel) server.getConversation(target)).setTopic(topic);

    Intent intent =
        Broadcast.createConversationIntent(Broadcast.CONVERSATION_MESSAGE, server.getId(), target);
    service.sendBroadcast(intent);

    // update the displayed conversation title if necessary
    intent =
        Broadcast.createConversationIntent(Broadcast.CONVERSATION_TOPIC, server.getId(), target);
    service.sendBroadcast(intent);
  }
  /** On Voice */
  @Override
  protected void onVoice(
      String target,
      String sourceNick,
      String sourceLogin,
      String sourceHostname,
      String recipient) {
    Message message = new Message(service.getString(R.string.message_voice, sourceNick, recipient));
    message.setIcon(R.drawable.voice);
    message.setColor(Message.COLOR_BLUE);
    server.getConversation(target).addMessage(message);

    Intent intent =
        Broadcast.createConversationIntent(Broadcast.CONVERSATION_MESSAGE, server.getId(), target);
    service.sendBroadcast(intent);
  }
  /** On register */
  @Override
  public void onRegister() {
    // Call parent method to ensure "register" status is tracked
    super.onRegister();

    // execute commands
    CommandParser parser = CommandParser.getInstance();

    this.updateNickMatchPattern();
    for (String command : server.getConnectCommands()) {
      parser.parse(command, server, server.getConversation(ServerInfo.DEFAULT_NAME), service);
    }

    // TODO: Detect "You are now identified for <nick>" notices from NickServ and handle
    //       auto joins in onNotice instead if the user has chosen to wait for NickServ
    //       identification before auto joining channels.

    // delay 1 sec before auto joining channels
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      // do nothing
    }

    // join channels
    if (autojoinChannels != null) {
      for (String channel : autojoinChannels) {
        // Add support for channel keys
        joinChannel(channel);
      }
    } else {
      for (String channel : server.getAutoJoinChannels()) {
        joinChannel(channel);
      }
    }

    Message infoMessage = new Message(service.getString(R.string.message_login_done));
    infoMessage.setColor(Message.COLOR_GREY);
    server.getConversation(ServerInfo.DEFAULT_NAME).addMessage(infoMessage);

    Intent intent =
        Broadcast.createConversationIntent(
            Broadcast.CONVERSATION_MESSAGE, server.getId(), ServerInfo.DEFAULT_NAME);

    service.sendBroadcast(intent);
  }
  /** On server response */
  @Override
  protected void onServerResponse(int code, String response) {
    if (code == 4) {
      // User has registered with the server
      onRegister();
      return;
    }
    if ((code == 372 || code == 375) && ignoreMOTD) {
      return;
    }
    if (code == 376 && ignoreMOTD) {
      Message motdMessage = new Message(service.getString(R.string.message_motd_suppressed));
      motdMessage.setColor(Message.COLOR_GREY);
      server.getConversation(ServerInfo.DEFAULT_NAME).addMessage(motdMessage);
      ignoreMOTD = false;
      return;
    }

    if (code >= 200 && code < 300) {
      // Skip 2XX responses
      return;
    }

    if (code == 353 || code == 366 || code == 332 || code == 333) {
      return;
    }

    if (code < 10) {
      // Skip server info
      return;
    }

    // Currently disabled... to much text
    Message message = new Message(response);
    message.setColor(Message.COLOR_GREY);
    server.getConversation(ServerInfo.DEFAULT_NAME).addMessage(message);

    Intent intent =
        Broadcast.createConversationIntent(
            Broadcast.CONVERSATION_MESSAGE, server.getId(), ServerInfo.DEFAULT_NAME);
    service.sendBroadcast(intent);
  }
  /** On disconnect */
  @Override
  public void onDisconnect() {
    // Call parent method to ensure "register" status is tracked
    super.onDisconnect();

    if (service.getSettings().isReconnectEnabled() && server.getStatus() != Status.DISCONNECTED) {
      setAutojoinChannels(server.getCurrentChannelNames());

      server.setStatus(Status.CONNECTING);
      service.connect(server);
    } else {
      server.setStatus(Status.DISCONNECTED);
    }

    service.notifyDisconnected(server.getTitle());

    Intent sIntent = Broadcast.createServerIntent(Broadcast.SERVER_UPDATE, server.getId());
    service.sendBroadcast(sIntent);

    Collection<Conversation> conversations = server.getConversations();

    for (Conversation conversation : conversations) {
      Message message = new Message(service.getString(R.string.message_disconnected));
      message.setIcon(R.drawable.error);
      message.setColor(Message.COLOR_RED);
      server.getConversation(conversation.getName()).addMessage(message);

      Intent cIntent =
          Broadcast.createConversationIntent(
              Broadcast.CONVERSATION_MESSAGE, server.getId(), conversation.getName());
      service.sendBroadcast(cIntent);
    }

    synchronized (isQuittingLock) {
      isQuitting = false;
      if (disposeRequested) {
        super.dispose();
      }
    }
  }
  /** On Part */
  @Override
  protected void onPart(String target, String sender, String login, String hostname) {
    if (sender.equals(getNick())) {
      // We parted a channel
      service.ackNewMentions(server.getId(), target);
      server.removeConversation(target);

      Intent intent =
          Broadcast.createConversationIntent(Broadcast.CONVERSATION_REMOVE, server.getId(), target);
      service.sendBroadcast(intent);
    } else if (service.getSettings().showJoinPartAndQuit()) {
      Message message =
          new Message(service.getString(R.string.message_part, sender), Message.TYPE_MISC);

      message.setColor(Message.COLOR_GREEN);
      message.setIcon(R.drawable.part);
      server.getConversation(target).addMessage(message);

      Intent intent =
          Broadcast.createConversationIntent(
              Broadcast.CONVERSATION_MESSAGE, server.getId(), target);
      service.sendBroadcast(intent);
    }
  }