/**
 *
 */
package com.github.apixandru.games.rummikub;

import java.util.Collection;
import java.util.HashSet;

/**
 * @author Alexandru-Constantin Bledea
 * @since Sep 16, 2015
 */
final class Cards {

	/**
	 *
	 */
	private Cards() {
	}

	/**
	 * @return
	 */
	static boolean isDifferentColors(final Collection<Card> cards) {
		final Collection<Color> colors = new HashSet<>();
		for (final Card card : cards) {
			final Color cardColor = card.getColor();
			if (null == cardColor) {
				continue;
			}
			if (colors.contains(cardColor)) {
				return false;
			}
			colors.add(cardColor);
		}
		return true;
	}

	/**
	 * @param cards
	 * @return
	 */
	static boolean isSameRanks(final Collection<Card> cards) {
		final Rank rank = getFirstRank(cards);
		for (final Card card : cards) {
			final Rank cardRank = card.getRank();
			if (null != cardRank && rank != cardRank) {
				return false;
			}
		}
		return true;
	}

	/**
	 * @return
	 */
	static boolean isAllSameColor(final Collection<Card> cards) {
		final Color color = getFirstColor(cards);
		for (final Card card : cards) {
			final Color cardColor = card.getColor();
			if (null != cardColor && color != cardColor) {
				return false;
			}
		}
		return true;
	}

	/**
	 * @param cards
	 * @return
	 */
	private static Color getFirstColor(final Collection<Card> cards) {
		for (final Card card : cards) {
			final Color color = card.getColor();
			if (null != color) {
				return color;
			}
		}
		return null;
	}

	/**
	 * @param cards
	 * @return
	 */
	public static boolean isAscendingRanks(final Collection<Card> cards) {
		Rank expected = null;
		boolean first = true;
		for (final Card card : cards) {
			final Rank rank = card.getRank();
			if (first && null != rank) {
				expected = rank;
				first = false;
			}
//			if rank is null then joker, matches
			if (rank != null && expected != rank) {
				return false;
			}
			expected = Rank.next(expected);
		}
		return true;
	}

	/**
	 * Because the run can start with one or two jokers, we need to infer the
	 * rank of the cards that they substitute. This means that the rank in a run
	 * can never be lower than the card number in that run
	 *
	 * @param rank the rank of the card
	 * @param cardNumberInRun the number of the card in the group
	 * @return true if the card rank is valid for the position in the run
	 */
	private static boolean isValidRankInRun(final Rank rank, final int cardNumberInRun) {
		return rank.ordinal() >= cardNumberInRun;
	}

	/**
	 * @param cards
	 * @return
	 */
	private static Rank getFirstRank(final Collection<Card> cards) {
		for (final Card card : cards) {
			final Rank rank = card.getRank();
			if (null != rank) {
				return rank;
			}
		}
		return null;
	}

}

@@ -104,16 +104,39 @@
 HashSet;
+%0Aimport java.util.List;
 %0A%0A/**%0A *
@@ -1574,34 +1574,28 @@
 Ranks(final 
-Collection
+List
 %3CCard%3E cards
@@ -1645,32 +1645,80 @@
  = true;%0A%09%09for (
+int i = 0, to = cards.size(); i %3C to; i++) %7B%0A%09%09%09
 final Card card 
@@ -1713,34 +1713,39 @@
 l Card card 
-:
+=
  cards
-) %7B
+.get(i);
 %0A%09%09%09final Ra
@@ -1798,24 +1798,87 @@
  != rank) %7B%0A
+%09%09%09%09if (!isValidRankInRun(rank, i)) %7B%0A%09%09%09%09%09return false;%0A%09%09%09%09%7D%0A
 %09%09%09%09expected

check the valid rank in run