public double calculate() {

		// our strategy:
		// - put all items in another collection
		// - let the rule objects to pick their favorites

		double amount = 0.0;

		// put all items into a workspace.
		cartWorkspace = new ArrayList<Item>(cart);

		Map<Rule, RuleResult> ruleResults = new HashMap<Rule, RuleResult>();

		do {

			ruleResults.clear();

			// now let the rules to process the items
			for (Rule rule : rules) {

				List<Item> ruleItems = new ArrayList<Item>(cartWorkspace);

				// after calling this method, the rule will process a result
				// which
				// contains:
				// - the total amount of price of goods discounted.
				// - the list of items which are elegible for the rule.
				// - the remaining list of items which are out of this rule's
				// scope.
				RuleResult ruleResult = rule.process(ruleItems);

				if (ruleResult.getDiscountedItems().size() > 0) {
					ruleResults.put(rule, ruleResult);
				}

			}

			// now we know all rule's preference. we pick the rule which helps
			// the customer to save most.
			if (!ruleResults.isEmpty()) {

				Map.Entry<Rule, RuleResult> bestRule = null;

				for (Map.Entry<Rule, RuleResult> entry : ruleResults.entrySet()) {

					if (bestRule == null) {
						bestRule = entry;
					} else {
						if (entry.getValue().getAmount() < bestRule.getValue()
								.getAmount()) {
							bestRule = entry;
						}
					}

				}

				List<Item> discountedItems = bestRule.getValue()
						.getDiscountedItems();
				for (Item item : discountedItems) {
					cartWorkspace.remove(item);
				}
				amount += bestRule.getValue().getAmount();

			}

		} while (!ruleResults.isEmpty());

		for (Item item : cartWorkspace) {
			amount += item.getPrice();
		}

		return amount;
	}