package jrummikub.model;

import jrummikub.util.Pair;

/** Class administering the {@link Stone}s on the game-Table */

public class Table extends StoneTray<StoneSet> implements ITable {

	private static class StoneInfo {
		StoneSet set;
		Position setPosition;
		int stonePosition;

		public StoneInfo(StoneSet set, Position setPosition, int stonePosition) {
			this.set = set;
			this.setPosition = setPosition;
			this.stonePosition = stonePosition;
		}
	}

	/**
	 * Removes {@link Stone} from the Table
	 * 
	 * @param stone
	 *          stone to pick up
	 */
	@Override
	public void pickUpStone(Stone stone) {
		StoneInfo info = findStoneInfo(stone);

		if (info != null) {
			splitSet(info.set, info.setPosition, info.stonePosition);
		}
	}

	private StoneInfo findStoneInfo(Stone stone) {
		// Find the set of the stone
		StoneInfo info;
		StoneSet set = null;
		Position setPosition = null;
		int stonePosition = 0;
		setLoop: for (Pair<StoneSet, Position> i : this) {
			set = i.getFirst();
			setPosition = i.getSecond();
			stonePosition = 0;
			for (Stone j : set) {
				if (j == stone) {
					break setLoop;
				}
				stonePosition++;
			}
			set = null;
		}
		// Stone not found
		if (set == null) {
			info = null;
		} else {
			info = new StoneInfo(set, setPosition, stonePosition);
		}
		return info;
	}

	@Override
	public StoneSet findStoneSet(Stone stone) {
		StoneInfo info = findStoneInfo(stone);
		if (info == null) {
			return null;
		}
		return info.set;
	}

	private void splitSet(StoneSet set, Position setPosition,
			int stonePosition) {
		pickUp(set);

		Pair<StoneSet, StoneSet> firstSplit = set.splitAt(stonePosition);
		Pair<StoneSet, StoneSet> secondSplit = firstSplit.getSecond().splitAt(1);

		StoneSet leftSet = firstSplit.getFirst();
		StoneSet rightSet = secondSplit.getSecond();

		if (set.classify().getFirst() == StoneSet.Type.RUN) {
			Position leftPosition, rightPosition;
			leftPosition = setPosition;
			rightPosition = new Position(setPosition.getX() + stonePosition + 1,
					setPosition.getY());

			drop(leftSet, leftPosition);
			drop(rightSet, rightPosition);
		} else {
			Position newPosition = new Position(setPosition.getX() + 0.5f,
					setPosition.getY());

			if (leftSet == null) {
				drop(rightSet, newPosition);
			} else if (rightSet == null) {
				drop(leftSet, newPosition);
			} else {
				drop(leftSet.join(rightSet), newPosition);
			}
		}
	}

	/** Tests the Table for rule conflicts by checking all the {@link StoneSet} */
	@Override
	public boolean isValid() {
		for (Pair<StoneSet, Position> i : this) {
			if (!i.getFirst().isValid()) {
				return false;
			}
		}
		return true;
	}

}
