@VisibleForTesting
	String buildStatBonusString(ItemStats s)
	{
		ItemStats other = null;
		// Used if switching into a 2 handed weapon to store off-hand stats
		ItemStats offHand = null;
		final ItemEquipmentStats currentEquipment = s.getEquipment();

		ItemContainer c = client.getItemContainer(InventoryID.EQUIPMENT);
		if (s.isEquipable() && currentEquipment != null && c != null)
		{
			final int slot = currentEquipment.getSlot();

			other = getItemStatsFromContainer(c, slot);
			// Check if this is a shield and there's a two-handed weapon equipped
			if (other == null && slot == EquipmentInventorySlot.SHIELD.getSlotIdx())
			{
				other = getItemStatsFromContainer(c, EquipmentInventorySlot.WEAPON.getSlotIdx());
				if (other != null)
				{
					final ItemEquipmentStats otherEquip = other.getEquipment();
					if (otherEquip != null)
					{
						// Account for speed change when two handed weapon gets removed
						// shield - (2h - unarmed) == shield - 2h + unarmed
						other = otherEquip.isTwoHanded() ? other.subtract(UNARMED) : null;
					}
				}
			}

			if (slot == EquipmentInventorySlot.WEAPON.getSlotIdx())
			{
				if (other == null)
				{
					other = UNARMED;
				}

				// Get offhand's stats to be removed from equipping a 2h weapon
				if (currentEquipment.isTwoHanded())
				{
					offHand = getItemStatsFromContainer(c, EquipmentInventorySlot.SHIELD.getSlotIdx());
				}
			}
		}

		final ItemStats subtracted = s.subtract(other).subtract(offHand);
		final ItemEquipmentStats e = subtracted.getEquipment();

		final StringBuilder b = new StringBuilder();

		if (config.showWeight())
		{
			double sw = config.alwaysShowBaseStats() ? subtracted.getWeight() : s.getWeight();
			b.append(buildStatRow("Weight", s.getWeight(), sw, true, false, s.isEquipable()));
		}

		if (subtracted.isEquipable() && e != null)
		{
			b.append(buildStatRow("Prayer", currentEquipment.getPrayer(), e.getPrayer(), false, false));
			b.append(buildStatRow("Speed", currentEquipment.getAspeed(), e.getAspeed(), true, false));
			b.append(buildStatRow("Melee Str", currentEquipment.getStr(), e.getStr(), false, false));
			b.append(buildStatRow("Range Str", currentEquipment.getRstr(), e.getRstr(), false, false));
			b.append(buildStatRow("Magic Dmg", currentEquipment.getMdmg(), e.getMdmg(), false, true));

			final StringBuilder abb = new StringBuilder();
			abb.append(buildStatRow("Stab", currentEquipment.getAstab(), e.getAstab(), false, false));
			abb.append(buildStatRow("Slash", currentEquipment.getAslash(), e.getAslash(), false, false));
			abb.append(buildStatRow("Crush", currentEquipment.getAcrush(), e.getAcrush(), false, false));
			abb.append(buildStatRow("Magic", currentEquipment.getAmagic(), e.getAmagic(), false, false));
			abb.append(buildStatRow("Range", currentEquipment.getArange(), e.getArange(), false, false));

			if (abb.length() > 0)
			{
				b.append(ColorUtil.wrapWithColorTag("Attack Bonus</br>", JagexColors.MENU_TARGET)).append(abb);
			}

			final StringBuilder dbb = new StringBuilder();
			dbb.append(buildStatRow("Stab", currentEquipment.getDstab(), e.getDstab(), false, false));
			dbb.append(buildStatRow("Slash", currentEquipment.getDslash(), e.getDslash(), false, false));
			dbb.append(buildStatRow("Crush", currentEquipment.getDcrush(), e.getDcrush(), false, false));
			dbb.append(buildStatRow("Magic", currentEquipment.getDmagic(), e.getDmagic(), false, false));
			dbb.append(buildStatRow("Range", currentEquipment.getDrange(), e.getDrange(), false, false));

			if (dbb.length() > 0)
			{
				b.append(ColorUtil.wrapWithColorTag("Defence Bonus</br>", JagexColors.MENU_TARGET)).append(dbb);
			}
		}

		return b.toString();
	}
@Test
	public void testBuildStatBonusString()
	{
		// Empty equipment (fully unarmed)
		final ItemContainer equipment = mock(ItemContainer.class);
		when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(equipment);

		String tooltip;
		String sanitizedTooltip;

		tooltip = overlay.buildStatBonusString(ABYSSAL_DAGGER);
		sanitizedTooltip = Text.sanitizeMultilineText(tooltip);
		assertTrue(sanitizedTooltip.contains("Stab: +75"));
		assertTrue(sanitizedTooltip.contains("Slash: +40"));
		assertTrue(sanitizedTooltip.contains("Crush: -4"));
		assertEquals(2, StringUtils.countMatches(sanitizedTooltip, "Magic: +1")); // Attack and defense
		assertTrue(sanitizedTooltip.contains("Melee Str: +75"));
		assertFalse(sanitizedTooltip.contains("Speed:"));

		tooltip = overlay.buildStatBonusString(KATANA);
		sanitizedTooltip = Text.sanitizeMultilineText(tooltip);
		assertTrue(sanitizedTooltip.contains("Stab: +7"));
		assertTrue(sanitizedTooltip.contains("Slash: +45"));
		assertTrue(sanitizedTooltip.contains("Stab: +3")); // Defense
		assertTrue(sanitizedTooltip.contains("Slash: +7")); // Defense
		assertTrue(sanitizedTooltip.contains("Crush: +7")); // Defense
		assertTrue(sanitizedTooltip.contains("Range: -3")); // Defense
		assertTrue(sanitizedTooltip.contains("Melee Str: +40"));
		assertFalse(sanitizedTooltip.contains("Speed:"));

		tooltip = overlay.buildStatBonusString(BLOWPIPE);
		sanitizedTooltip = Text.sanitizeMultilineText(tooltip);
		assertTrue(sanitizedTooltip.contains("Range: +60"));
		assertTrue(sanitizedTooltip.contains("Range Str: +40"));
		assertTrue(sanitizedTooltip.contains("Speed: -1"));
		assertFalse(sanitizedTooltip.contains("Stab:"));

		tooltip = overlay.buildStatBonusString(HEAVY_BALLISTA);
		sanitizedTooltip = Text.sanitizeMultilineText(tooltip);
		assertTrue(sanitizedTooltip.contains("Range: +110"));
		assertTrue(sanitizedTooltip.contains("Speed: +3"));
		assertFalse(sanitizedTooltip.contains("Stab:"));
	}