class ChatNotificationsPlugin extends Plugin {

	@Subscribe
	void onChatMessage(ChatMessage chatMessage)
	{
		MessageNode messageNode = chatMessage.getMessageNode();
		boolean update = false;

		switch (chatMessage.getType())
		{
			case TRADEREQ:
				if (chatMessage.getMessage().contains("wishes to trade with you.") && config.notifyOnTrade())
				{
					notifier.notify(chatMessage.getMessage());
				}
				break;
			case CHALREQ_TRADE:
				if (chatMessage.getMessage().contains("wishes to duel with you.") && config.notifyOnDuel())
				{
					notifier.notify(chatMessage.getMessage());
				}
				break;
			case BROADCAST:
				if (config.notifyOnBroadcast())
				{
					// Some broadcasts have links attached, notated by `|` followed by a number, while others contain color tags.
					// We don't want to see either in the printed notification.
					String broadcast = chatMessage.getMessage();

					int urlTokenIndex = broadcast.lastIndexOf('|');
					if (urlTokenIndex != -1)
					{
						broadcast = broadcast.substring(0, urlTokenIndex);
					}

					notifier.notify(Text.removeFormattingTags(broadcast));
				}
				break;
			case CONSOLE:
				// Don't notify for notification messages
				if (chatMessage.getName().equals(RuneLiteProperties.getTitle()))
				{
					return;
				}
				break;
			case PRIVATECHAT:
			case MODPRIVATECHAT:
				if (config.notifyOnPm())
				{
					int messageHash = this.buildMessageHash(chatMessage);
					if (this.privateMessageHashes.contains(messageHash))
					{
						return;
					}
					this.privateMessageHashes.add(messageHash);
					notifier.notify("Private message received from " + chatMessage.getName());
				}
				break;
		}

		if (usernameMatcher == null && client.getLocalPlayer() != null && client.getLocalPlayer().getName() != null)
		{
			String username = client.getLocalPlayer().getName();
			String pattern = Arrays.stream(username.split(" "))
				.map(s -> s.isEmpty() ? "" : Pattern.quote(s))
				.collect(Collectors.joining("[\u00a0\u0020]")); // space or nbsp
			usernameMatcher = Pattern.compile("\\b" + pattern + "\\b", Pattern.CASE_INSENSITIVE);
			usernameReplacer = "<col" + ChatColorType.HIGHLIGHT.name() + "><u>" + username + "</u><col" + ChatColorType.NORMAL.name() + ">";
		}

		if (config.highlightOwnName() && usernameMatcher != null)
		{
			Matcher matcher = usernameMatcher.matcher(messageNode.getValue());
			if (matcher.find())
			{
				messageNode.setValue(matcher.replaceAll(usernameReplacer));
				update = true;

				if (config.notifyOnOwnName() && (chatMessage.getType() == ChatMessageType.PUBLICCHAT
					|| chatMessage.getType() == ChatMessageType.PRIVATECHAT
					|| chatMessage.getType() == ChatMessageType.FRIENDSCHAT
					|| chatMessage.getType() == ChatMessageType.MODCHAT
					|| chatMessage.getType() == ChatMessageType.MODPRIVATECHAT))
				{
					sendNotification(chatMessage);
				}
			}
		}

		if (highlightMatcher != null)
		{
			String nodeValue = messageNode.getValue();
			Matcher matcher = highlightMatcher.matcher(nodeValue);
			boolean found = false;
			StringBuilder sb = new StringBuilder();

			while (matcher.find())
			{
				String value = matcher.group();

				// Determine the ending color by:
				// 1) use the color from value if it has one
				// 2) use the last color from sb + <content between last match and current match>
				// To do #2 we just search for the last col tag after calling appendReplacement
				String endColor = getLastColor(value);

				// Strip color tags from the highlighted region so that it remains highlighted correctly
				value = stripColor(value);

				matcher.appendReplacement(sb, "<col" + ChatColorType.HIGHLIGHT + '>' + value);

				if (endColor == null)
				{
					endColor = getLastColor(sb.toString());
				}

				// Append end color
				sb.append(endColor == null ? "<col" + ChatColorType.NORMAL + ">" : endColor);

				update = true;
				found = true;
			}

			if (found)
			{
				matcher.appendTail(sb);
				messageNode.setValue(sb.toString());

				if (config.notifyOnHighlight())
				{
					sendNotification(chatMessage);
				}
			}
		}

		if (update)
		{
			messageNode.setRuneLiteFormatMessage(messageNode.getValue());
			chatMessageManager.update(messageNode);
		}
	}

}