package jrummikub.model;

import static jrummikub.model.StoneTray.Direction.LEFT;
import static jrummikub.model.StoneTray.Direction.RIGHT;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.TreeMap;

import jrummikub.ai.TurnLogic;
import jrummikub.util.Pair;

/** Class managing a {@link Player}'s {@link Stone}s */
public class Hand extends StoneTray<Stone> implements IHand {
	private static final long serialVersionUID = 192210056255744909L;

	/**
	 * The width of the hand
	 */
	public final static int WIDTH = 14;

	@Override
	public int getFreeRowSpace(int row) {
		int count = 0;
		for (Pair<Stone, Position> entry : this) {
			if (entry.getSecond().getY() == row) {
				count++;
			}
		}
		return WIDTH - count;
	}

	@Override
	public int getRowCount() {
		int rows = 0;

		for (Pair<Stone, Position> entry : this) {
			if (entry.getSecond().getY() > rows) {
				rows = (int) entry.getSecond().getY();
			}
		}

		return rows + 1;
	}

	@Override
	protected Pair<Position, Direction> fixInvalidDrop(Stone stone,
			Position pos, Direction dir) {
		double x = pos.getX();
		double y = pos.getY();

		if (x >= 0 && x <= WIDTH - 1) {
			return null;
		}
		if (x < 0) {
			return new Pair<Position, Direction>(new Position(0, y), RIGHT);
		} else {
			if (getFreeRowSpace((int) y) == 0) {
				return new Pair<Position, Direction>(new Position(0, y + 1),
						RIGHT);
			} else {
				return new Pair<Position, Direction>(
						new Position(WIDTH - 1, y), LEFT);
			}
		}
	}

	@Override
	public int getStonePoints(GameSettings settings) {
		int points = 0;

		for (Pair<Stone, Position> entry : this) {
			if (entry.getFirst().isJoker()) {
				points += settings.getJokerPoints();
			} else {
				points += entry.getFirst().getValue();
			}
		}

		return points;
	}

	@Override
	public boolean isInitialMeldPossible(GameSettings settings) {
		List<Stone> handStones = new ArrayList<Stone>();

		for (Pair<Stone, Position> entry : this) {
			handStones.add(entry.getFirst());
		}

		TurnLogic turnLogic = new TurnLogic(settings,
				Collections.<Stone> emptyList(), handStones);
		turnLogic.needIntialMeldThreshold();
		return turnLogic.solve();
	}
	
	
	private static void incrementStoneCount(
			TreeMap<Pair<Integer, StoneColor>, Integer> stones,
			Pair<Integer, StoneColor> stone) {
		if (stones.containsKey(stone)) {
			stones.put(stone, stones.get(stone) + 1);
		} else {
			stones.put(stone, 1);
		}
	}

	private final static Comparator<Pair<Integer, StoneColor>> comparator = new Comparator<Pair<Integer, StoneColor>>() {
		@Override
		public int compare(Pair<Integer, StoneColor> o1,
				Pair<Integer, StoneColor> o2) {
			int firstComparison = o1.getFirst().compareTo(o2.getFirst());
			if (firstComparison != 0) {
				return -firstComparison;
			} else {
				return o1.getSecond().compareTo(o2.getSecond());
			}
		}
	};
	
	/**
	 * Counts the numbers of stones
	 * 
	 * @param stones
	 *          the stones to count
	 * @return the numbers for all stones
	 */
	private static Pair<TreeMap<Pair<Integer, StoneColor>, Integer>, Integer> countStones(
			List<Stone> stones) {
		int jokerCount = 0;
		TreeMap<Pair<Integer, StoneColor>, Integer> stoneCounts = new TreeMap<Pair<Integer, StoneColor>, Integer>(
				comparator);

		for (Stone stone : stones) {
			if (stone.isJoker()) {
				jokerCount++;
			} else {
				Pair<Integer, StoneColor> key = new Pair<Integer, StoneColor>(
						stone.getValue(), stone.getColor());

				incrementStoneCount(stoneCounts, key);
			}
		}
		return new Pair<TreeMap<Pair<Integer, StoneColor>, Integer>, Integer>(
				stoneCounts, jokerCount);
	}

	@Override
	public int getIdenticalStoneCount() {
		List<Stone> stones = new ArrayList<Stone>();

		for (Iterator<Pair<Stone, Position>> iter = this.iterator(); iter
				.hasNext();) {
			stones.add(iter.next().getFirst());
		}

		Pair<TreeMap<Pair<Integer, StoneColor>, Integer>, Integer> stoneCounts = countStones(stones);

		int pairCount = 0;

		for (int count : stoneCounts.getFirst().values()) {
			pairCount += count / 2;
		}

		return pairCount;
	}
}
