/**
 * Copyright (c) 2021 Dominic Masters
 * 
 * This software is released under the MIT License.
 * https://opensource.org/licenses/MIT
 */

#include "player.h"

void test_pokerPlayerAdd_should_AddAPlayer(void) {
  poker_t poker;
  pokerInit(&poker);

  TEST_ASSERT_EQUAL_UINT8(0, poker.playerCount);
  TEST_ASSERT_EQUAL(0, pokerPlayerAdd(&poker));
  TEST_ASSERT_EQUAL_UINT8(1, poker.playerCount);
  TEST_ASSERT_EQUAL(1, pokerPlayerAdd(&poker));
  TEST_ASSERT_EQUAL_UINT8(2, poker.playerCount);
}

void test_pokerPlayerAdd_should_ResetThePlayer(void) {
  poker_t poker;
  pokerplayer_t *player;
  pokerInit(&poker);

  player = poker.players + pokerPlayerAdd(&poker);
  TEST_ASSERT_EQUAL_INT32(0, player->chips);
  TEST_ASSERT_EQUAL_INT32(0, player->currentBet);
  TEST_ASSERT_EQUAL_UINT8(0, player->cardCount);
  TEST_ASSERT_EQUAL_UINT8(0, player->timesRaised);
  TEST_ASSERT_EQUAL_UINT8(POKER_PLAYER_STATE_OUT, player->state);
}

void test_pokerPlayerChipsAdd_should_AddChipsToThePlayer(void) {
  poker_t poker;
  uint8_t playerIndex;
  pokerplayer_t *player;

  pokerInit(&poker);
  playerIndex = pokerPlayerAdd(&poker);
  player = poker.players + playerIndex;

  TEST_ASSERT_EQUAL_INT32(0, player->chips);
  pokerPlayerChipsAdd(player, 32);
  TEST_ASSERT_EQUAL_INT32(32, player->chips);
  pokerPlayerChipsAdd(player, 10);
  TEST_ASSERT_EQUAL_INT32(42, player->chips);
  pokerPlayerChipsAdd(player, -30);
  TEST_ASSERT_EQUAL_INT32(12, player->chips);
}

void test_pokerPlayerChipsAdd_should_TurnOutStateOff(void) {
  poker_t poker;
  uint8_t playerIndex;
  pokerplayer_t *player;

  pokerInit(&poker);
  playerIndex = pokerPlayerAdd(&poker);
  player = poker.players + playerIndex;

  player->state |= POKER_PLAYER_STATE_OUT;
  TEST_ASSERT_BITS_HIGH(POKER_PLAYER_STATE_OUT, player->state);
  pokerPlayerChipsAdd(player, 32);
  TEST_ASSERT_BITS_LOW(POKER_PLAYER_STATE_OUT, player->state);
}

void test_pokerPlayerDoesNeedToBetThisRound_should_CheckCallValue(void) {
  poker_t poker;
  uint8_t first, second;

  pokerInit(&poker);

  first = pokerPlayerAdd(&poker);
  second = pokerPlayerAdd(&poker);
  pokerPlayerChipsAdd(poker.players + first, 10000);
  pokerPlayerChipsAdd(poker.players + second, 10000);

  pokerBetForPlayer(&poker, first, 100);
  TEST_ASSERT_EQUAL(false, pokerPlayerDoesNeedToBetThisRound(&poker, first));
  TEST_ASSERT_EQUAL(true, pokerPlayerDoesNeedToBetThisRound(&poker, second));
  
  pokerBetForPlayer(&poker, second, 200);
  TEST_ASSERT_EQUAL(true, pokerPlayerDoesNeedToBetThisRound(&poker, first));
  TEST_ASSERT_EQUAL(false, pokerPlayerDoesNeedToBetThisRound(&poker, second));

  pokerBetForPlayer(&poker, first, 100);
  TEST_ASSERT_EQUAL(false, pokerPlayerDoesNeedToBetThisRound(&poker, first));
  TEST_ASSERT_EQUAL(false, pokerPlayerDoesNeedToBetThisRound(&poker, second));
}

void test_pokerPlayerDoesNeedToBetThisRound_should_CheckWhetherHasBetYet(void) {
  poker_t poker;
  uint8_t first, second;
  pokerInit(&poker);
  first = pokerPlayerAdd(&poker);
  second = pokerPlayerAdd(&poker);
  pokerPlayerChipsAdd(poker.players + first, 10000);
  pokerPlayerChipsAdd(poker.players + second, 10000);

  TEST_ASSERT_EQUAL(true, pokerPlayerDoesNeedToBetThisRound(&poker, first));
  TEST_ASSERT_EQUAL(true, pokerPlayerDoesNeedToBetThisRound(&poker, second));

  pokerBetForPlayer(&poker, first, 100);
  TEST_ASSERT_EQUAL(false, pokerPlayerDoesNeedToBetThisRound(&poker, first));
  TEST_ASSERT_EQUAL(true, pokerPlayerDoesNeedToBetThisRound(&poker, second));

  pokerBetForPlayer(&poker, second, 100);
  TEST_ASSERT_EQUAL(false, pokerPlayerDoesNeedToBetThisRound(&poker, first));
  TEST_ASSERT_EQUAL(false, pokerPlayerDoesNeedToBetThisRound(&poker, second));
}

void test_pokerPlayerDoesNeedToBetThisRound_should_IgnoreFoldedPlayers(void) {
  poker_t poker;
  uint8_t first, second;
  pokerInit(&poker);
  first = pokerPlayerAdd(&poker);
  second = pokerPlayerAdd(&poker);
  pokerPlayerChipsAdd(poker.players + first, 10000);
  pokerPlayerChipsAdd(poker.players + second, 10000);

  TEST_ASSERT_EQUAL(true, pokerPlayerDoesNeedToBetThisRound(&poker, first));
  TEST_ASSERT_EQUAL(true, pokerPlayerDoesNeedToBetThisRound(&poker, second));

  pokerBetForPlayer(&poker, first, 100);
  TEST_ASSERT_EQUAL(false, pokerPlayerDoesNeedToBetThisRound(&poker, first));
  TEST_ASSERT_EQUAL(true, pokerPlayerDoesNeedToBetThisRound(&poker, second));

  poker.players[second].state |= POKER_PLAYER_STATE_FOLDED;
  TEST_ASSERT_EQUAL(false, pokerPlayerDoesNeedToBetThisRound(&poker, first));
  TEST_ASSERT_EQUAL(false, pokerPlayerDoesNeedToBetThisRound(&poker, second));

  pokerBetForPlayer(&poker, first, 100);
  TEST_ASSERT_EQUAL(false, pokerPlayerDoesNeedToBetThisRound(&poker, first));
  TEST_ASSERT_EQUAL(false, pokerPlayerDoesNeedToBetThisRound(&poker, second));
}

void test_pokerPlayerCanCheck_should_CompareThePotAndPlayerBet(void) {
  poker_t poker;
  uint8_t p0, p1, p2;
  pokerInit(&poker);

  p0 = pokerPlayerAdd(&poker);
  p1 = pokerPlayerAdd(&poker);
  p2 = pokerPlayerAdd(&poker);
  pokerPlayerChipsAdd(poker.players + p0, 1000);
  pokerPlayerChipsAdd(poker.players + p1, 1000);
  pokerPlayerChipsAdd(poker.players + p2, 1000);

  pokerBetForPlayer(&poker, p0, 100);
  TEST_ASSERT_EQUAL(true, pokerPlayerCanCheck(&poker, poker.players+p0));
  TEST_ASSERT_EQUAL(false, pokerPlayerCanCheck(&poker, poker.players+p1));
  TEST_ASSERT_EQUAL(false, pokerPlayerCanCheck(&poker, poker.players+p2));

  pokerBetForPlayer(&poker, p1, 100);
  TEST_ASSERT_EQUAL(true, pokerPlayerCanCheck(&poker, poker.players+p0));
  TEST_ASSERT_EQUAL(true, pokerPlayerCanCheck(&poker, poker.players+p1));
  TEST_ASSERT_EQUAL(false, pokerPlayerCanCheck(&poker, poker.players+p2));

  pokerBetForPlayer(&poker, p2, 100);
  TEST_ASSERT_EQUAL(true, pokerPlayerCanCheck(&poker, poker.players+p0));
  TEST_ASSERT_EQUAL(true, pokerPlayerCanCheck(&poker, poker.players+p1));
  TEST_ASSERT_EQUAL(true, pokerPlayerCanCheck(&poker, poker.players+p2));

  pokerBetForPlayer(&poker, p2, 100);
  TEST_ASSERT_EQUAL(false, pokerPlayerCanCheck(&poker, poker.players+p0));
  TEST_ASSERT_EQUAL(false, pokerPlayerCanCheck(&poker, poker.players+p1));
  TEST_ASSERT_EQUAL(true, pokerPlayerCanCheck(&poker, poker.players+p2));

  pokerBetForPlayer(&poker, p1, 100);
  TEST_ASSERT_EQUAL(false, pokerPlayerCanCheck(&poker, poker.players+p0));
  TEST_ASSERT_EQUAL(true, pokerPlayerCanCheck(&poker, poker.players+p1));
  TEST_ASSERT_EQUAL(true, pokerPlayerCanCheck(&poker, poker.players+p2));
}

void test_pokerPlayerGetFullHand_should_ReturnTheFullHand(void) {
  poker_t poker;
  pokerplayer_t *player;
  uint8_t i;
  card_t cards[POKER_WINNING_FULL_SIZE];

  pokerInit(&poker);
  i = pokerPlayerAdd(&poker);
  player = poker.players + i;
  
  pokerDeal(&poker, player, POKER_PLAYER_HAND_SIZE_MAX);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_ACE, player->cards[0]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_KING, player->cards[1]);

  pokerDealerTurn(&poker, POKER_COMMUNITY_SIZE_MAX);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_QUEEN, poker.community[0]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_JACK, poker.community[1]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_TEN, poker.community[2]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_NINE, poker.community[3]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_EIGHT, poker.community[4]);

  pokerPlayerGetFullHand(&poker, player, cards);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_QUEEN, cards[0]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_JACK, cards[1]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_TEN, cards[2]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_NINE, cards[3]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_EIGHT, cards[4]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_ACE, cards[5]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_KING, cards[6]);

  poker.community[0] = CARD_CLUBS_THREE;
  poker.community[1] = CARD_DIAMONDS_TWO;
  poker.community[2] = CARD_SPADES_EIGHT;
  poker.community[3] = CARD_DIAMONDS_ACE;
  poker.community[4] = CARD_SPADES_FIVE;
  player->cards[0] = CARD_DIAMONDS_KING;
  player->cards[1] = CARD_HEARTS_QUEEN;

  pokerPlayerGetFullHand(&poker, player, cards);
  TEST_ASSERT_EQUAL_UINT8(CARD_CLUBS_THREE, cards[0]);
  TEST_ASSERT_EQUAL_UINT8(CARD_DIAMONDS_TWO, cards[1]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_EIGHT, cards[2]);
  TEST_ASSERT_EQUAL_UINT8(CARD_DIAMONDS_ACE, cards[3]);
  TEST_ASSERT_EQUAL_UINT8(CARD_SPADES_FIVE, cards[4]);
  TEST_ASSERT_EQUAL_UINT8(CARD_DIAMONDS_KING, cards[5]);
  TEST_ASSERT_EQUAL_UINT8(CARD_HEARTS_QUEEN, cards[6]);
}

int test_player_h() {
  UNITY_BEGIN();

  RUN_TEST(test_pokerPlayerAdd_should_ResetThePlayer);
  RUN_TEST(test_pokerPlayerChipsAdd_should_AddChipsToThePlayer);
  RUN_TEST(test_pokerPlayerChipsAdd_should_TurnOutStateOff);
  RUN_TEST(test_pokerPlayerDoesNeedToBetThisRound_should_CheckCallValue);
  RUN_TEST(test_pokerPlayerDoesNeedToBetThisRound_should_CheckWhetherHasBetYet);
  RUN_TEST(test_pokerPlayerDoesNeedToBetThisRound_should_IgnoreFoldedPlayers);
  RUN_TEST(test_pokerPlayerCanCheck_should_CompareThePotAndPlayerBet);
  RUN_TEST(test_pokerPlayerGetFullHand_should_ReturnTheFullHand);

  return UNITY_END();
}