@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
		"file:src/main/webapp/WEB-INF/spring/spring-context.xml",
		"classpath:db-context-test.xml"
})
@Transactional
public abstract class AbstractSpringTest {

	@Autowired
	private SessionFactory sessionFactory;
	
	/**
	 * Helper function.  Due to the nature of Hibernate + Spring Transactions, the cache may get
	 * out of sync if a transaction is not committed.  The DB for the transaction may have an
	 * updated entity that hibernate cannot read back because of the cache.  Flush and clear
	 * will re-synchronize this, and also evict all entities from the persistence context.
	 */
	protected void flushAndClear(){
		sessionFactory.getCurrentSession().flush();
		sessionFactory.getCurrentSession().clear();
	}
} public class Deck {
	private List<Card> cards;
	
	/**
	 * Construct a standard shuffled playing card deck.
	 */
	public Deck(){
		this(true);
	}
	
	/**
	 * Create a standard deck of playing cards
	 * @param shuffle true for shuffled deck.  False for not shuffled.
	 */
	public Deck(boolean shuffle){
		initDeck();
		if(shuffle){
			shuffleDeck();
		}
	}
	
	/**
	 * Initialize a deck using a pre existing list of cards
	 * @param cards List of cards, assumed correctly shuffled
	 */
	public Deck(List<Card> cards){
		//this.cards = new LinkedList<Card>();
		//this.cards.addAll(cards);
		this.cards = cards;
	}
	
	public void initDeck(){
		cards = new LinkedList<Card>();
		cards.addAll(Arrays.asList(Card.values()));
	}
	
	public void shuffleDeck(){
		Collections.shuffle(cards);
	}
	
	/**
	 * Returns the top card from the deck.  Removes the card from the deck.
	 * @return {@link Card}
	 */
	public Card dealCard(){
		return cards.remove(0);
	}
	
	/**
	 * Get the cards in the deck in the form of a list
	 * @return
	 */
	public List<Card> exportDeck(){
		return cards;
	}
} public interface GameDao extends BaseDao<Game>{

} public interface HandDao extends BaseDao<HandEntity>{

} public interface PlayerDao extends BaseDao<Player>{

} public enum BlindLevel {

	BLIND_10_20(10,20),
	BLIND_15_30(15,30),
	BLIND_20_40(20,40),
	BLIND_25_50(25,50),
	BLIND_30_60(30,60),
	BLIND_40_80(40,80),
	BLIND_50_100(50,100),
	BLIND_60_120(60,120),
	BLIND_75_150(75,150),
	BLIND_80_160(80,160),
	BLIND_100_200(100,200),
	BLIND_120_240(120,240),
	BLIND_150_300(150,300),
	BLIND_200_400(200,400),
	BLIND_250_500(250,500),
	BLIND_300_600(300,600),
	BLIND_400_800(400,800),
	BLIND_500_1000(500,1000),
	BLIND_600_1200(600,1200),
	BLIND_700_1400(700,1400),
	BLIND_800_1600(800,1600),
	BLIND_900_1800(900,1800),
	BLIND_1000_2000(1000,2000),
	BLIND_1200_2400(1200,2400),
	BLIND_1500_3000(1500,3000),
	BLIND_1600_3200(1600,3200),
	BLIND_1800_3600(1800,3600),
	BLIND_2000_4000(2000,4000),
	BLIND_2500_5000(2500,5000),
	BLIND_3000_6000(3000,6000),
	BLIND_4000_8000(4000,8000),
	BLIND_5000_10K(5000,10000),
	BLIND_6000_12K(6000,12000),
	BLIND_7000_14K(7000,14000),
	BLIND_8000_16K(8000,16000),
	BLIND_9000_18K(9000,18000),
	BLIND_10K_20K(10000,20000),
	BLIND_12K_24K(12000,24000),
	BLIND_14K_28K(14000,28000),
	BLIND_16K_32K(16000,32000),
	BLIND_18K_36K(18000,36000),
	BLIND_20K_40K(20000,40000),
	BLIND_25K_50K(25000,50000);
	
	private int smallBlind;
	private int bigBlind;
	
	private BlindLevel(int smallBlind, int bigBlind){
		this.smallBlind = smallBlind;
		this.bigBlind = bigBlind;
	}
	
	public int getSmallBlind(){
		return smallBlind;
	}
	
	public int getBigBlind(){
		return bigBlind;
	}
} @Entity
@Table(name="board")
public class BoardEntity implements Serializable {
	
	private static final long serialVersionUID = 7043793786047807514L;
	private long id;
	private Card flop1;
	private Card flop2;
	private Card flop3;
	private Card turn;
	private Card river;
	
	/**
	 * Get all of the cards on the board in list form.
	 * The list is ordered with flop first, then turn, then river.
	 * @return
	 */
	@Transient
	public List<Card> getBoardCards(){
		List<Card> cards = new ArrayList<Card>();
		if(flop1 != null){
			cards.add(flop1);
			cards.add(flop2);
			cards.add(flop3);
		}
		if(turn != null){
			cards.add(turn);
		}
		if(river != null){
			cards.add(river);
		}
		return cards;
	}
	
	@Column(name="board_id")
	@Id
	@GeneratedValue(strategy=GenerationType.TABLE)
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	
	@Column(name="flop1")
	@Enumerated(EnumType.STRING)
	public Card getFlop1() {
		return flop1;
	}
	public void setFlop1(Card flop1) {
		this.flop1 = flop1;
	}
	
	@Column(name="flop2")
	@Enumerated(EnumType.STRING)
	public Card getFlop2() {
		return flop2;
	}
	public void setFlop2(Card flop2) {
		this.flop2 = flop2;
	}
	
	@Column(name="flop3")
	@Enumerated(EnumType.STRING)
	public Card getFlop3() {
		return flop3;
	}
	public void setFlop3(Card flop3) {
		this.flop3 = flop3;
	}
	
	@Column(name="turn")
	@Enumerated(EnumType.STRING)
	public Card getTurn() {
		return turn;
	}
	public void setTurn(Card turn) {
		this.turn = turn;
	}
	
	@Column(name="river")
	@Enumerated(EnumType.STRING)
	public Card getRiver() {
		return river;
	}
	public void setRiver(Card river) {
		this.river = river;
	}
} @JsonFormat(shape=Shape.OBJECT)
public enum CommonTournamentFormats {
	
	TWO_HR_SEVENPPL("7 to 8 People, 2 hour length", 15, 1500,
			BlindLevel.BLIND_10_20,
			BlindLevel.BLIND_15_30,
			BlindLevel.BLIND_25_50,
			BlindLevel.BLIND_50_100,
			BlindLevel.BLIND_75_150,
			BlindLevel.BLIND_100_200,
			BlindLevel.BLIND_200_400,
			BlindLevel.BLIND_300_600,
			BlindLevel.BLIND_500_1000,
			BlindLevel.BLIND_1000_2000,
			BlindLevel.BLIND_2000_4000),
	
	TWO_HR_NINEPPL("9 Players, 2 hour length", 10, 1500,
			BlindLevel.BLIND_10_20,
			BlindLevel.BLIND_15_30,
			BlindLevel.BLIND_20_40,
			BlindLevel.BLIND_30_60,
			BlindLevel.BLIND_40_80,
			BlindLevel.BLIND_50_100,
			BlindLevel.BLIND_75_150,
			BlindLevel.BLIND_100_200,
			BlindLevel.BLIND_150_300,
			BlindLevel.BLIND_200_400,
			BlindLevel.BLIND_300_600,
			BlindLevel.BLIND_400_800,
			BlindLevel.BLIND_500_1000,
			BlindLevel.BLIND_1000_2000,
			BlindLevel.BLIND_2000_4000,
			BlindLevel.BLIND_4000_8000),
			
	TWO_HR_SIXPPL("6 Players, 2 hour length", 15, 2000,
			BlindLevel.BLIND_10_20,
			BlindLevel.BLIND_15_30,
			BlindLevel.BLIND_25_50,
			BlindLevel.BLIND_50_100,
			BlindLevel.BLIND_75_150,
			BlindLevel.BLIND_100_200,
			BlindLevel.BLIND_200_400,
			BlindLevel.BLIND_300_600,
			BlindLevel.BLIND_500_1000,
			BlindLevel.BLIND_1000_2000,
			BlindLevel.BLIND_2000_4000);
	
	
	private String description;
	private BlindLevel[] levels;
	private int timeInMins;
	private int startingChips;
	
	private CommonTournamentFormats(String description, int timeInMins, int startingChips, BlindLevel... levels){
		this.description = description;
		this.timeInMins = timeInMins;
		this.levels = levels;
		this.startingChips = startingChips;
	}
	
	public String getDescription(){
		return description;
	}
	
	@JsonProperty("blindLength")
	public int getTimeInMinutes(){
		return timeInMins;
	}
	
	public List<BlindLevel> getBlindLevels(){
		return Arrays.asList(levels);
	}
	
	public int getStartingChips(){
		return this.startingChips;
	}
	
	/**
	 * Override the super Enum method for JSON Serialization purposes.
	 * @return Exactly the same as if you called the Enum name() method.  
	 * Returns the string value of the Enum identifier.
	 */
	public String getName(){
		return super.name();
	}
} @Entity
@Table(name="game")
public class Game implements Serializable {

	private static final long serialVersionUID = -495064662454346171L;
	private long id;
	private int playersRemaining;
	private Player playerInBTN;
	private GameType gameType;
	private String name;
	private boolean isStarted;
	private Set<Player> players;
	private HandEntity currentHand;
	private GameStructure gameStructure;
	
	@Column(name="game_id")
	@Id
	@GeneratedValue(strategy=GenerationType.TABLE)
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	
	@Column(name="players_left")
	public int getPlayersRemaining() {
		return playersRemaining;
	}
	public void setPlayersRemaining(int playersRemaining) {
		this.playersRemaining = playersRemaining;
	}
	
	@OneToOne
	@JoinColumn(name="btn_player_id")
	public Player getPlayerInBTN(){
		return playerInBTN;
	}
	public void setPlayerInBTN(Player playerInBTN){
		this.playerInBTN = playerInBTN;
	}
	
	@Column(name="game_type")
	@Enumerated(EnumType.STRING)
	public GameType getGameType() {
		return gameType;
	}
	public void setGameType(GameType gameType) {
		this.gameType = gameType;
	}
	
	@OneToMany(mappedBy="game", fetch=FetchType.LAZY)
	public Set<Player> getPlayers() {
		return players;
	}
	public void setPlayers(Set<Player> players) {
		this.players = players;
	}
	
	@Column(name="name")
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	@Column(name="is_started")
	public boolean isStarted() {
		return isStarted;
	}
	public void setStarted(boolean isStarted) {
		this.isStarted = isStarted;
	}
	
	@OneToOne(fetch=FetchType.EAGER)
	@JoinColumn(name="current_hand_id")
	public HandEntity getCurrentHand() {
		return currentHand;
	}
	public void setCurrentHand(HandEntity currentHand) {
		this.currentHand = currentHand;
	}
	
	@OneToOne(fetch=FetchType.EAGER, cascade={CascadeType.ALL})
	@JoinColumn(name="game_structure_id")
	public GameStructure getGameStructure() {
		return gameStructure;
	}
	public void setGameStructure(GameStructure gameStructure) {
		this.gameStructure = gameStructure;
	}
} @Entity
@Table(name="game_structure")
public class GameStructure implements Serializable {
	
	private static final long serialVersionUID = 3663515999002547153L;
	private long id;
	private BlindLevel currentBlindLevel;
	private int blindLength;
	private Date currentBlindEndTime;
	private Date puaseStartTime;
	private List<BlindLevel> blindLevels;
	private int startingChips;
	
	@Column(name="game_structure_id")
	@Id
	@GeneratedValue(strategy=GenerationType.TABLE)
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	
	@Column(name="current_blind_level")
	@Enumerated(EnumType.STRING)
	public BlindLevel getCurrentBlindLevel() {
		return currentBlindLevel;
	}
	public void setCurrentBlindLevel(BlindLevel currentBlindLevel) {
		this.currentBlindLevel = currentBlindLevel;
	}
	
	@Column(name="blind_length")
	public int getBlindLength() {
		return blindLength;
	}
	public void setBlindLength(int blindLength) {
		this.blindLength = blindLength;
	}
	
	@Column(name="current_blind_ends")
	@Temporal(TemporalType.TIMESTAMP)
	public Date getCurrentBlindEndTime() {
		return currentBlindEndTime;
	}
	public void setCurrentBlindEndTime(Date currentBlindEndTime) {
		this.currentBlindEndTime = currentBlindEndTime;
	}
	
	@Column(name="starting_chips")
	public int getStartingChips(){
		return startingChips;
	}
	public void setStartingChips(int startingChips){
		this.startingChips = startingChips;
	}
	
	@Column(name="pause_start_time")
	@Temporal(TemporalType.TIMESTAMP)
	public Date getPuaseStartTime() {
		return puaseStartTime;
	}
	public void setPuaseStartTime(Date puaseStartTime) {
		this.puaseStartTime = puaseStartTime;
	}
	
	@ElementCollection(targetClass=BlindLevel.class)
	@JoinTable(name="game_blind", joinColumns=@JoinColumn(name="game_structure_id"))
	@Column(name="blind",nullable=false)
	@Enumerated(EnumType.STRING)
	public List<BlindLevel> getBlindLevels() {
		return blindLevels;
	}
	public void setBlindLevels(List<BlindLevel> blindLevels) {
		this.blindLevels = blindLevels;
	}
} public enum GameType {
	CASH,TOURNAMENT;
} @Entity
@Table(name="hand")
public class HandEntity implements Serializable {
	
	private static final long serialVersionUID = 4557343585146763024L;
	private long id;
	private Game game;
	private BoardEntity board;
	private Set<PlayerHand> players;
	private Player currentToAct;
	private BlindLevel blindLevel;
	private List<Card> cardList;
	private int pot;
	private int totalBetAmount;
	private int lastBetAmount;
	
	@Id
	@GeneratedValue(strategy=GenerationType.TABLE)
	@Column(name="hand_id")
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	
	@ManyToOne(fetch=FetchType.EAGER)
	@JoinColumn(name="game_id")
	public Game getGame() {
		return game;
	}
	public void setGame(Game game) {
		this.game = game;
	}
	
	@OneToOne(fetch=FetchType.EAGER, cascade={CascadeType.ALL})
	@JoinColumn(name="board_id")
	public BoardEntity getBoard() {
		return board;
	}
	public void setBoard(BoardEntity board) {
		this.board = board;
	}
	
	@OneToMany(fetch=FetchType.EAGER, mappedBy="handEntity",cascade={CascadeType.ALL},orphanRemoval=true)
	public Set<PlayerHand> getPlayers() {
		return players;
	}
	public void setPlayers(Set<PlayerHand> players) {
		this.players = players;
	}
	
	@ManyToOne(fetch=FetchType.EAGER)
	@JoinColumn(name="player_to_act_id")
	public Player getCurrentToAct() {
		return currentToAct;
	}
	public void setCurrentToAct(Player currentToAct) {
		this.currentToAct = currentToAct;
	}
	
	@Column(name="blind_level")
	@Enumerated(EnumType.STRING)
	public BlindLevel getBlindLevel() {
		return blindLevel;
	}
	public void setBlindLevel(BlindLevel blindLevel) {
		this.blindLevel = blindLevel;
	}
	
	@ElementCollection(targetClass=Card.class)
	@JoinTable(name="hand_deck", joinColumns=@JoinColumn(name="hand_id"))
	@Column(name="card",nullable=false)
	@Enumerated(EnumType.STRING)
	public List<Card> getCards(){
		return cardList;
	}
	public void setCards(List<Card> cards){
		cardList = cards;
	}
	
	@Column(name="pot")
	public int getPot() {
		return pot;
	}
	public void setPot(int pot) {
		this.pot = pot;
	}
	
	@Column(name="total_bet_amount")
	public int getTotalBetAmount() {
		return totalBetAmount;
	}
	public void setTotalBetAmount(int betAmount) {
		this.totalBetAmount = betAmount;
	}
	
	
	/**
	 * In No Limit poker, the minimum bet size is twice the previous bet.  Use this field to determine
	 * what that amount would be.
	 * @return The last bet/raise amount
	 */
	@Column(name="bet_amount")
	public int getLastBetAmount() {
		return lastBetAmount;
	}
	public void setLastBetAmount(int lastBetAmount) {
		this.lastBetAmount = lastBetAmount;
	}
	
	@Transient
	@Override
	public boolean equals(Object o){
		if (o == null || !(o instanceof HandEntity)){
			return false;
		}
		return ((HandEntity) o).getId() == this.getId();
	}
	
	@Transient
	@Override
	public int hashCode(){
		return (int) this.getId();
	}
} @Entity
@Table(name="player")
public class Player implements Comparable<Player>, Serializable{

	private static final long serialVersionUID = -1384636077333014255L;
	private String id;
	private Game game;
	private String name;
	private int chips;
	private int gamePosition;
	private int finishPosition;
	private boolean sittingOut;
	
	@JsonIgnore
	@Column(name="player_id")
	@Id
	@GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid2")
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	
	@JsonIgnore
	@ManyToOne
	@JoinColumn(name="game_id")
	public Game getGame() {
		return game;
	}
	public void setGame(Game game) {
		this.game = game;
	}
	
	@Column(name="name")
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	@Column(name="chips")
	public int getChips() {
		return chips;
	}
	public void setChips(int chips) {
		this.chips = chips;
	}
	
	@Column(name="game_position")
	public int getGamePosition() {
		return gamePosition;
	}
	public void setGamePosition(int gamePosition) {
		this.gamePosition = gamePosition;
	}
	
	@Column(name="finished_place")
	public int getFinishPosition() {
		return finishPosition;
	}
	public void setFinishPosition(int finishPosition) {
		this.finishPosition = finishPosition;
	}
	
	@Column(name="sitting_out")
	public boolean isSittingOut() {
		return sittingOut;
	}
	public void setSittingOut(boolean sittingOut) {
		this.sittingOut = sittingOut;
	}
	
	@Override
	public boolean equals(Object o){
		if(o == null || !(o instanceof Player)){
			return false;
		}
		Player p = (Player) o;
		if(this.getId() == null){
			return this.getName().equals(p.getName());
		}
		return this.getId().equals(p.getId());
	}
	
	@Override
	public int hashCode(){
		if(id == null){
			return name.hashCode();
		}
		return id.hashCode();
	}
	
	@Override
	@Transient
	public int compareTo(Player p){
		return this.getGamePosition() - p.getGamePosition();
	}
} @Entity
@Table(name="player_hand")
public class PlayerHand implements Comparable<PlayerHand>, Serializable{

	private static final long serialVersionUID = -5499451283824674842L;
	private long id;
	private Player player;
	private HandEntity handEntity;
	private Card card1;
	private Card card2;
	private int betAmount;
	private int roundBetAmount;
	
	@Id
	@GeneratedValue(strategy=GenerationType.TABLE)
	@Column(name="player_hand_id")
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	
	@ManyToOne(fetch=FetchType.EAGER)
	@JoinColumn(name="player_id")
	public Player getPlayer() {
		return player;
	}
	public void setPlayer(Player player) {
		this.player = player;
	}
	
	@ManyToOne
	@JoinColumn(name="hand_id")
	public HandEntity getHandEntity() {
		return handEntity;
	}
	public void setHandEntity(HandEntity hand) {
		this.handEntity = hand;
	}
	
	@Column(name="card1")
	@Enumerated(EnumType.STRING)
	protected Card getCard1() {
		return card1;
	}
	public void setCard1(Card card1) {
		this.card1 = card1;
	}
	
	@Column(name="card2")
	@Enumerated(EnumType.STRING)
	protected Card getCard2() {
		return card2;
	}
	public void setCard2(Card card2) {
		this.card2 = card2;
	}
	
	@Column(name="bet_amount")
	/**
	 * Represents the amount of chips the player has contributed to the pot
	 * for this entire hand
	 * @return
	 */
	public int getBetAmount() {
		return betAmount;
	}
	public void setBetAmount(int betAmount) {
		this.betAmount = betAmount;
	}
	
	@Column(name="round_bet_amount")
	/**
	 * Represents the amount of chips the player has contributed to the pot for only this
	 * current betting round
	 * @return
	 */
	public int getRoundBetAmount() {
		return roundBetAmount;
	}
	public void setRoundBetAmount(int roundBetAmount) {
		this.roundBetAmount = roundBetAmount;
	}
	
	/**
	 * Get the hole cards for the player
	 * @return {@link Hand} container for the two hole cards. Null if both hole cards not specified
	 */
	@Transient
	public Hand getHand(){
		if(card1 == null || card2 == null){
			return null;
		}
		return new Hand(card1, card2);
	}
	
	@Transient
	@Override
	public int compareTo(PlayerHand o) {
		return this.getPlayer().compareTo(o.getPlayer());
	}
} @JsonFormat(shape=Shape.STRING)
public enum PlayerStatus {
	/** The game has not been started */
	NOT_STARTED,
	/** Players are taking there seats */
	SEATING,
	/** Waiting for another player to act */
	WAITING,
	/** Player is still in the hand, but has committed all of his/her chips and will take no further action */
	ALL_IN,
	/** The hand went to showdown, but the player lost */
	LOST_HAND,
	/** Won chips in the previous hand.  The old English for won is winnan. */
	WON_HAND,
	/** Post the Small Blind for this hand */
	POST_SB,
	/** Post the Big Blind for this hand */
	POST_BB,
	/** Player is ready to act.  Player must call or raise to continue. */
	ACTION_TO_CALL,
	/** Player is ready to act. There is no bet, so the player may check. */
	ACTION_TO_CHECK,
	/** The Player is still in the game but not in the hand. */
	SIT_OUT,
	/** The Player has been sat out of the game. The player still has a seat at the table, but is not in the seat. */
	SIT_OUT_GAME,
	/** The Player is no longer in the game.  */
	ELIMINATED;
}
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.hyphenated.card.AbstractSpringTest;
import com.hyphenated.card.Deck;
import com.hyphenated.card.dao.GameDao;
import com.hyphenated.card.dao.HandDao;
import com.hyphenated.card.dao.PlayerDao;
import com.hyphenated.card.domain.BlindLevel;
import com.hyphenated.card.domain.BoardEntity;
import com.hyphenated.card.domain.CommonTournamentFormats;
import com.hyphenated.card.domain.Game;
import com.hyphenated.card.domain.GameStructure;
import com.hyphenated.card.domain.GameType;
import com.hyphenated.card.domain.HandEntity;
import com.hyphenated.card.domain.Player;
import com.hyphenated.card.domain.PlayerHand;
import com.hyphenated.card.domain.PlayerStatus;
	}
	
	@Test
	public void testPlayerHandStarted(){
		HandEntity hand = getBasicHand(this.setupBasicGame(true));
		List<Player> players = new ArrayList<Player>();
		players.addAll(hand.getGame().getPlayers());
		assertEquals(hand.getGame().getPlayers().size(), hand.getPlayers().size());
		Collections.sort(players);
		assertEquals(PlayerStatus.WAITING, playerActionService.getPlayerStatus(players.get(0)));
		assertEquals(PlayerStatus.WAITING, playerActionService.getPlayerStatus(players.get(1)));
		assertEquals(PlayerStatus.WAITING, playerActionService.getPlayerStatus(players.get(2)));
		assertEquals(PlayerStatus.ACTION_TO_CALL, playerActionService.getPlayerStatus(players.get(3)));
		assertEquals(PlayerStatus.WAITING, playerActionService.getPlayerStatus(players.get(4)));
		assertEquals(2000, players.get(0).getChips());
		assertEquals(1000, players.get(1).getChips());
		assertEquals(1500, players.get(2).getChips());
		assertEquals(2200, players.get(3).getChips());
		assertEquals(500, players.get(4).getChips());
	}
	
	@Test
	public void testPreflopPlay(){
		HandEntity hand = getBasicHand(this.setupBasicGame(true));
		List<Player> players = new ArrayList<Player>();
		players.addAll(hand.getGame().getPlayers());
		assertEquals(hand.getGame().getPlayers().size(), hand.getPlayers().size());
		Collections.sort(players);
		assertEquals(30, hand.getPot());
		assertEquals(20, hand.getLastBetAmount());
		assertEquals(20, hand.getTotalBetAmount());
		//Cannot check out of order
		assertFalse(playerActionService.check(players.get(0), hand));
		assertFalse(playerActionService.check(players.get(1), hand));
		assertFalse(playerActionService.check(players.get(2), hand));
		assertFalse(playerActionService.check(players.get(4), hand));
		//Cannot bet out of order
		assertFalse(playerActionService.bet(players.get(0), hand, 100));
		assertFalse(playerActionService.bet(players.get(1), hand, 100));
		assertFalse(playerActionService.bet(players.get(2), hand, 100));
		assertFalse(playerActionService.bet(players.get(4), hand, 100));
		//Cannot call out of order
		assertFalse(playerActionService.call(players.get(0), hand));
		assertFalse(playerActionService.call(players.get(1), hand));
		assertFalse(playerActionService.call(players.get(2), hand));
		assertFalse(playerActionService.call(players.get(4), hand));
		//Cannot fold out of order
		assertFalse(playerActionService.fold(players.get(0), hand));
		assertFalse(playerActionService.fold(players.get(1), hand));
		assertFalse(playerActionService.fold(players.get(2), hand));
		assertFalse(playerActionService.fold(players.get(4), hand));
		
		//Make sure all is still well
		assertEquals(30, hand.getPot());
		assertEquals(20, hand.getLastBetAmount());
		assertEquals(20, hand.getTotalBetAmount());
		assertEquals(players.get(3), hand.getCurrentToAct());
		
		assertTrue(playerActionService.call(players.get(3), hand));
		//2200 - 20 to call
		assertEquals(2180, players.get(3).getChips());
		assertEquals(PlayerStatus.WAITING, playerActionService.getPlayerStatus(players.get(3)));
		assertEquals(PlayerStatus.ACTION_TO_CALL, playerActionService.getPlayerStatus(players.get(4)));
		
		assertFalse(playerActionService.bet(players.get(4), hand,10));
		assertTrue(playerActionService.bet(players.get(4), hand,20));
		assertEquals(460, players.get(4).getChips());
		assertEquals(40, hand.getTotalBetAmount());
		assertEquals(20, hand.getLastBetAmount());
		assertEquals(90, hand.getPot());
		
		assertTrue(playerActionService.call(players.get(0), hand));
		assertEquals(1960, players.get(0).getChips());
		assertEquals(40, hand.getTotalBetAmount());
		assertEquals(20, hand.getLastBetAmount());
		assertEquals(130, hand.getPot());
		
		assertTrue(playerActionService.fold(players.get(1), hand));
		assertEquals(4,hand.getPlayers().size());
		assertEquals(130, hand.getPot());
		assertEquals(PlayerStatus.ACTION_TO_CALL, playerActionService.getPlayerStatus(players.get(2)));
		
		assertTrue(playerActionService.call(players.get(2), hand));
		assertEquals(1460, players.get(2).getChips());
		assertEquals(40, hand.getTotalBetAmount());
		assertEquals(20, hand.getLastBetAmount());
		assertEquals(170, hand.getPot());
		assertEquals(PlayerStatus.ACTION_TO_CALL, playerActionService.getPlayerStatus(players.get(3)));
		
		assertTrue(playerActionService.call(players.get(3), hand));
		assertEquals(2160, players.get(3).getChips());
		assertEquals(40, hand.getTotalBetAmount());
		assertEquals(20, hand.getLastBetAmount());
		assertEquals(190, hand.getPot());	
	}
	
	@Test
	public void testPostFlopPlay(){
		HandEntity hand = getBasicHand(this.setupBasicGame(true));
		List<Player> players = new ArrayList<Player>();
		players.addAll(hand.getGame().getPlayers());
		Collections.sort(players);
		assertTrue(playerActionService.call(players.get(3), hand));
		assertTrue(playerActionService.bet(players.get(4), hand,20));
		assertTrue(playerActionService.call(players.get(0), hand));
		assertTrue(playerActionService.fold(players.get(1), hand));
		assertTrue(playerActionService.call(players.get(2), hand));
		assertTrue(playerActionService.call(players.get(3), hand));
		assertEquals(40, hand.getTotalBetAmount());
		assertEquals(20, hand.getLastBetAmount());
		assertEquals(190, hand.getPot());	
		
		assertEquals(2160, players.get(3).getChips());
		assertEquals(1460, players.get(2).getChips());
		assertEquals(1960, players.get(0).getChips());
		assertEquals(460, players.get(4).getChips());
		
		//caught up on preflop action.  Now deal the flop
		hand.setTotalBetAmount(0);
		hand.setLastBetAmount(0);
		for(PlayerHand ph : hand.getPlayers()){