class ChatCommandsPlugin extends Plugin {

	@Subscribe
	void onChatMessage(ChatMessage chatMessage)
	{
		if (chatMessage.getType() != ChatMessageType.TRADE
			&& chatMessage.getType() != ChatMessageType.GAMEMESSAGE
			&& chatMessage.getType() != ChatMessageType.SPAM
			&& chatMessage.getType() != ChatMessageType.FRIENDSCHATNOTIFICATION)
		{
			return;
		}

		String message = chatMessage.getMessage();
		Matcher matcher = KILLCOUNT_PATTERN.matcher(message);
		if (matcher.find())
		{
			String boss = matcher.group(1);
			int kc = Integer.parseInt(matcher.group(2));

			setKc(boss, kc);
			// We either already have the pb, or need to remember the boss for the upcoming pb
			if (lastPb > -1)
			{
				log.debug("Got out-of-order personal best for {}: {}", boss, lastPb);
				setPb(boss, lastPb);
				lastPb = -1;
			}
			else
			{
				lastBossKill = boss;
			}
			return;
		}

		matcher = WINTERTODT_PATTERN.matcher(message);
		if (matcher.find())
		{
			int kc = Integer.parseInt(matcher.group(1));

			setKc("Wintertodt", kc);
		}

		matcher = RAIDS_PATTERN.matcher(message);
		if (matcher.find())
		{
			String boss = matcher.group(1);
			int kc = Integer.parseInt(matcher.group(2));

			setKc(boss, kc);
			if (lastPb > -1)
			{
				// lastPb contains the last raid duration and not the personal best, because the raid
				// complete message does not include the pb. We have to check if it is a new pb:
				int currentPb = getPb(boss);
				if (currentPb <= 0 || lastPb < currentPb)
				{
					setPb(boss, lastPb);
				}
				lastPb = -1;
			}
			lastBossKill = boss;
			return;
		}

		matcher = DUEL_ARENA_WINS_PATTERN.matcher(message);
		if (matcher.find())
		{
			final int oldWins = getKc("Duel Arena Wins");
			final int wins = Integer.parseInt(matcher.group(2));
			final String result = matcher.group(1);
			int winningStreak = getKc("Duel Arena Win Streak");
			int losingStreak = getKc("Duel Arena Lose Streak");

			if (result.equals("won") && wins > oldWins)
			{
				losingStreak = 0;
				winningStreak += 1;
			}
			else if (result.equals("were defeated"))
			{
				losingStreak += 1;
				winningStreak = 0;
			}
			else
			{
				log.warn("unrecognized duel streak chat message: {}", message);
			}

			setKc("Duel Arena Wins", wins);
			setKc("Duel Arena Win Streak", winningStreak);
			setKc("Duel Arena Lose Streak", losingStreak);
		}

		matcher = DUEL_ARENA_LOSSES_PATTERN.matcher(message);
		if (matcher.find())
		{
			int losses = Integer.parseInt(matcher.group(1));

			setKc("Duel Arena Losses", losses);
		}

		matcher = BARROWS_PATTERN.matcher(message);
		if (matcher.find())
		{
			int kc = Integer.parseInt(matcher.group(1));

			setKc("Barrows Chests", kc);
		}

		matcher = KILL_DURATION_PATTERN.matcher(message);
		if (matcher.find())
		{
			matchPb(matcher);
		}

		matcher = NEW_PB_PATTERN.matcher(message);
		if (matcher.find())
		{
			matchPb(matcher);
		}

		matcher = RAIDS_PB_PATTERN.matcher(message);
		if (matcher.find())
		{
			matchPb(matcher);
		}

		matcher = RAIDS_DURATION_PATTERN.matcher(message);
		if (matcher.find())
		{
			matchPb(matcher);
		}

		matcher = TOB_WAVE_PB_PATTERN.matcher(message);
		if (matcher.find())
		{
			matchPb(matcher);
		}

		matcher = TOB_WAVE_DURATION_PATTERN.matcher(message);
		if (matcher.find())
		{
			matchPb(matcher);
		}

		matcher = HS_PB_PATTERN.matcher(message);
		if (matcher.find())
		{
			int floor = Integer.parseInt(matcher.group("floor"));
			String floortime = matcher.group("floortime");
			String floorpb = matcher.group("floorpb");
			String otime = matcher.group("otime");
			String opb = matcher.group("opb");

			String pb = Objects.requireNonNullElse(floorpb, floortime);
			setPb("Hallowed Sepulchre Floor " + floor, timeStringToSeconds(pb));

			if (otime != null)
			{
				pb = Objects.requireNonNullElse(opb, otime);
				setPb("Hallowed Sepulchre", timeStringToSeconds(pb));
			}
		}

		matcher = HS_KC_FLOOR_PATTERN.matcher(message);
		if (matcher.find())
		{
			int floor = Integer.parseInt(matcher.group(1));
			int kc = Integer.parseInt(matcher.group(2));
			setKc("Hallowed Sepulchre Floor " + floor, kc);
		}

		matcher = HS_KC_GHC_PATTERN.matcher(message);
		if (matcher.find())
		{
			int kc = Integer.parseInt(matcher.group(1));
			setKc("Hallowed Sepulchre", kc);
		}

		lastBossKill = null;
	}

}