/** 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 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 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 channel action */
  @Override
  protected void onAction(
      String sender, String login, String hostname, String target, String action) {
    Conversation conversation;

    Message message = new Message(sender + " " + action);
    message.setIcon(R.drawable.action);

    String queryNick = target;
    if (queryNick.equals(this.getNick())) {
      // We are the target - this is an action in a query
      queryNick = sender;
    }
    conversation = server.getConversation(queryNick);

    if (conversation == null) {
      // Open a query if there's none yet
      conversation = new Query(queryNick);
      conversation.setHistorySize(service.getSettings().getHistorySize());
      server.addConversation(conversation);
      conversation.addMessage(message);

      Intent intent =
          Broadcast.createConversationIntent(Broadcast.CONVERSATION_NEW, server.getId(), queryNick);
      service.sendBroadcast(intent);
    } else {
      conversation.addMessage(message);

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

    if (sender.equals(this.getNick())) {
      // Don't notify for something sent in our name
      return;
    }

    boolean mentioned = isMentioned(action);
    if (mentioned || target.equals(this.getNick())) {
      if (conversation.getStatus() != Conversation.STATUS_SELECTED || !server.getIsForeground()) {
        service.addNewMention(
            server.getId(),
            conversation,
            conversation.getName() + ": " + sender + " " + action,
            service.getSettings().isVibrateHighlightEnabled(),
            service.getSettings().isSoundHighlightEnabled());
      }
    }

    if (mentioned) {
      // highlight
      message.setColor(Message.COLOR_RED);
      conversation.setStatus(Conversation.STATUS_HIGHLIGHT);
    }
  }
  /** On Private Message */
  @Override
  protected void onPrivateMessage(
      String sender, String login, String hostname, String target, String text) {
    Message message = new Message("<" + sender + "> " + text);
    String queryNick = sender;

    if (queryNick.equals(this.getNick())) {
      queryNick = target;
    }
    Conversation conversation = server.getConversation(queryNick);

    if (conversation == null) {
      // Open a query if there's none yet
      conversation = new Query(queryNick);
      conversation.setHistorySize(service.getSettings().getHistorySize());
      conversation.addMessage(message);
      server.addConversation(conversation);

      Intent intent =
          Broadcast.createConversationIntent(Broadcast.CONVERSATION_NEW, server.getId(), queryNick);
      service.sendBroadcast(intent);
    } else {
      conversation.addMessage(message);

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

    if (sender.equals(this.getNick())) {
      // Don't notify for something sent in our name
      return;
    }

    if (conversation.getStatus() != Conversation.STATUS_SELECTED || !server.getIsForeground()) {
      service.addNewMention(
          server.getId(),
          conversation,
          "<" + sender + "> " + text,
          service.getSettings().isVibrateHighlightEnabled(),
          service.getSettings().isSoundHighlightEnabled());
    }

    if (isMentioned(text)) {
      message.setColor(Message.COLOR_RED);
      conversation.setStatus(Conversation.STATUS_HIGHLIGHT);
    }
  }
  /** On Notice */
  @Override
  protected void onNotice(
      String sourceNick, String sourceLogin, String sourceHostname, String target, String notice) {
    // Post notice to currently selected conversation
    Conversation conversation;

    if (service.getSettings().showNoticeInServerWindow()) {
      conversation = server.getConversation(ServerInfo.DEFAULT_NAME);
    } else {
      conversation = server.getConversation(server.getSelectedConversation());

      if (conversation == null) {
        // Fallback: Use ServerInfo view
        conversation = server.getConversation(ServerInfo.DEFAULT_NAME);
      }
    }

    Message message = new Message("-" + sourceNick + "- " + notice);
    message.setIcon(R.drawable.info);
    conversation.addMessage(message);

    Intent intent =
        Broadcast.createConversationIntent(
            Broadcast.CONVERSATION_MESSAGE, server.getId(), conversation.getName());
    service.sendBroadcast(intent);
  }
  /** On Message */
  @Override
  protected void onMessage(
      String target, String sender, String login, String hostname, String text) {
    Message message = new Message(text, sender);
    Conversation conversation = server.getConversation(target);

    if (isMentioned(text)) {
      // highlight
      message.setColor(Message.COLOR_RED);
      if (conversation.getStatus() != Conversation.STATUS_SELECTED || !server.getIsForeground()) {
        service.addNewMention(
            server.getId(),
            conversation,
            target + ": <" + sender + "> " + text,
            service.getSettings().isVibrateHighlightEnabled(),
            service.getSettings().isSoundHighlightEnabled());
      }

      conversation.setStatus(Conversation.STATUS_HIGHLIGHT);
    }

    conversation.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 unknown */
  @Override
  protected void onUnknown(String line) {
    Message message = new Message(line);
    message.setIcon(R.drawable.action);
    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);
    }
  }
  /** 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);
  }