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

#include "bet.h"

void test_pokerBetGetNextPlayer_should_GetTheNextBetter(void) {
  poker_t poker;
  uint8_t p0, p1, p2, p3, p4;
  pokerInit(&poker);

  p0 = pokerPlayerAdd(&poker);
  p1 = pokerPlayerAdd(&poker);
  p2 = pokerPlayerAdd(&poker);
  p3 = pokerPlayerAdd(&poker);
  p4 = pokerPlayerAdd(&poker);

  pokerPlayerChipsAdd(poker.players + p0, 10000);
  pokerPlayerChipsAdd(poker.players + p1, 10000);
  pokerPlayerChipsAdd(poker.players + p2, 10000);
  pokerPlayerChipsAdd(poker.players + p3, 10000);
  pokerPlayerChipsAdd(poker.players + p4, 10000);
  pokerDealerNew(&poker);

  // Start at blind+1
  poker.better = poker.playerBigBlind;
  poker.better = pokerBetGetNextPlayer(&poker);
  TEST_ASSERT_EQUAL_UINT8(0x04, poker.better);

  // Blind+2
  pokerBetForPlayer(&poker, poker.better, pokerBetGetCurrentCallValue(&poker));
  poker.better = pokerBetGetNextPlayer(&poker);
  TEST_ASSERT_EQUAL_UINT8(0x00, poker.better);

  // BLind+3
  pokerBetForPlayer(&poker, poker.better, pokerBetGetCurrentCallValue(&poker));
  poker.better = pokerBetGetNextPlayer(&poker);
  TEST_ASSERT_EQUAL_UINT8(0x01, poker.better);

  // Dealer
  pokerBetForPlayer(&poker, poker.better, pokerBetGetCurrentCallValue(&poker));
  poker.better = pokerBetGetNextPlayer(&poker);
  TEST_ASSERT_EQUAL_UINT8(0x02, poker.better);

  // Small blind
  pokerBetForPlayer(
    &poker, poker.better,
    pokerBetGetCurrentCallValue(&poker) - poker.players[0x02].currentBet
  );
  poker.better = pokerBetGetNextPlayer(&poker);
  TEST_ASSERT_EQUAL_UINT8(0x03, poker.better);

  // Big Blind
  pokerBetForPlayer(
    &poker, poker.better,
    pokerBetGetCurrentCallValue(&poker) - poker.players[0x03].currentBet
  );
  poker.better = pokerBetGetNextPlayer(&poker);
  TEST_ASSERT_EQUAL_UINT8(0xFF, poker.better);

  // Raise
  poker.better = poker.playerBigBlind;
  pokerBetForPlayer(&poker, poker.better, 100);
  poker.better = pokerBetGetNextPlayer(&poker);
  TEST_ASSERT_EQUAL_UINT8(0x04, poker.better);

  // Raise
  pokerBetForPlayer(&poker, poker.better, 100);
  poker.better = pokerBetGetNextPlayer(&poker);
  TEST_ASSERT_EQUAL_UINT8(0x00, poker.better);
}

void test_pokerBetGetRemainingBetterCount_should_ReturnCountNeedsToBet(void){
  poker_t poker;
  uint8_t p0, p1, p2;

  pokerInit(&poker);
  p0 = pokerPlayerAdd(&poker);
  p1 = pokerPlayerAdd(&poker);
  p2 = pokerPlayerAdd(&poker);

  TEST_ASSERT_EQUAL_UINT8(0x00, pokerBetGetRemainingBetterCount(&poker));

  pokerPlayerChipsAdd(poker.players + p0, 1000);
  TEST_ASSERT_EQUAL_UINT8(0x01, pokerBetGetRemainingBetterCount(&poker));
  pokerPlayerChipsAdd(poker.players + p2, 1000);
  TEST_ASSERT_EQUAL_UINT8(0x02, pokerBetGetRemainingBetterCount(&poker));
  pokerPlayerChipsAdd(poker.players + p1, 1000);
  TEST_ASSERT_EQUAL_UINT8(0x03, pokerBetGetRemainingBetterCount(&poker));

  poker.players[0].state |= POKER_PLAYER_STATE_FOLDED;
  TEST_ASSERT_EQUAL_UINT8(0x02, pokerBetGetRemainingBetterCount(&poker));
  poker.players[1].state |= POKER_PLAYER_STATE_HAS_BET_THIS_ROUND;
  TEST_ASSERT_EQUAL_UINT8(0x01, pokerBetGetRemainingBetterCount(&poker));
  poker.players[2].chips = 0;
  TEST_ASSERT_EQUAL_UINT8(0x00, pokerBetGetRemainingBetterCount(&poker));
}

void test_pokerBet_should_AddChipsToThePot(void) {
  poker_t poker;
  pokerpot_t *pot;
  pokerplayer_t *p0;
  pokerplayer_t *p1;
  uint8_t p0i, p1i;

  pokerInit(&poker);
  pot = poker.pots;

  p0i = pokerPlayerAdd(&poker);
  p1i = pokerPlayerAdd(&poker);

  p0 = poker.players + p0i;
  p1 = poker.players + p1i;

  pokerPlayerChipsAdd(p0, 1000);
  pokerPlayerChipsAdd(p1, 1000);

  TEST_ASSERT_EQUAL_INT32(0, pot->chips);
  TEST_ASSERT_EQUAL_INT32(0, pot->call);
  TEST_ASSERT_EQUAL_INT32(1000, p0->chips);
  TEST_ASSERT_EQUAL_INT32(1000, p1->chips);

  pokerBet(&poker, pot, p0i, 100);
  TEST_ASSERT_EQUAL_INT32(100, pot->chips);
  TEST_ASSERT_EQUAL_INT32(100, pot->call);
  TEST_ASSERT_EQUAL_INT32(900, p0->chips);
  TEST_ASSERT_EQUAL_INT32(1000, p1->chips);

  pokerBet(&poker, pot, p0i, 100);
  TEST_ASSERT_EQUAL_INT32(200, pot->chips);
  TEST_ASSERT_EQUAL_INT32(200, pot->call);
  TEST_ASSERT_EQUAL_INT32(800, p0->chips);
  TEST_ASSERT_EQUAL_INT32(1000, p1->chips);

  pokerBet(&poker, pot, p1i, 300);
  TEST_ASSERT_EQUAL_INT32(500, pot->chips);
  TEST_ASSERT_EQUAL_INT32(300, pot->call);
  TEST_ASSERT_EQUAL_INT32(800, p0->chips);
  TEST_ASSERT_EQUAL_INT32(700, p1->chips);

  // Second pot
  pot = poker.pots + pokerPotAdd(&poker);

  pokerBet(&poker, pot, p0i, 300);
  TEST_ASSERT_EQUAL_INT32(300, pot->chips);
  TEST_ASSERT_EQUAL_INT32(500, pot->call);
  TEST_ASSERT_EQUAL_INT32(500, p0->chips);
  TEST_ASSERT_EQUAL_INT32(700, p1->chips);

  pokerBet(&poker, pot, p1i, 400);
  TEST_ASSERT_EQUAL_INT32(700, pot->chips);
  TEST_ASSERT_EQUAL_INT32(700, pot->call);
  TEST_ASSERT_EQUAL_INT32(500, p0->chips);
  TEST_ASSERT_EQUAL_INT32(300, p1->chips);
}

void test_pokerBet_should_UpdatePlayerState(void) {
  poker_t poker;
  uint8_t i;
  pokerplayer_t *player;

  pokerInit(&poker);
  i = pokerPlayerAdd(&poker);
  player = poker.players + i;
  pokerPlayerChipsAdd(player, 1000);
  
  TEST_ASSERT_BITS_LOW(POKER_PLAYER_STATE_HAS_BET_THIS_ROUND, player->state);
  TEST_ASSERT_EQUAL_INT32(0, player->currentBet);
  TEST_ASSERT_EQUAL_UINT8(0, player->timesRaised);

  pokerBet(&poker, poker.pots, i, 100);
  TEST_ASSERT_BITS_HIGH(POKER_PLAYER_STATE_HAS_BET_THIS_ROUND, player->state);
  TEST_ASSERT_EQUAL_INT32(100, player->currentBet);
  TEST_ASSERT_EQUAL_UINT8(1, player->timesRaised);

  pokerBet(&poker, poker.pots, i, 250);
  TEST_ASSERT_EQUAL_INT32(350, player->currentBet);
  TEST_ASSERT_EQUAL_UINT8(2, player->timesRaised);
}

void test_pokerBetForPlayer_should_BetToTheActivePot(void) {
  poker_t poker;
  pokerpot_t *pot;
  uint8_t i, j;

  pokerInit(&poker);
  i = pokerPlayerAdd(&poker);
  pokerPlayerChipsAdd(poker.players+i, 1000);
  pot = poker.pots;

  TEST_ASSERT_EQUAL_INT32(0, pot->chips);
  pokerBetForPlayer(&poker, i, 100);
  TEST_ASSERT_EQUAL_INT32(100, pot->chips);

  j = pokerPotAdd(&poker);
  pokerBetForPlayer(&poker, i, 50);
  TEST_ASSERT_EQUAL_INT32(100, pot->chips);
  pot = poker.pots + j;
  TEST_ASSERT_EQUAL_INT32(50, pot->chips);
}

void test_pokerBetGetCurrentCallValue_should_CalculateTheCallValue(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_INT32(0, pokerBetGetCurrentCallValue(&poker));
  pokerBetForPlayer(&poker, first, 100);
  TEST_ASSERT_EQUAL_INT32(100, pokerBetGetCurrentCallValue(&poker));
  pokerBetForPlayer(&poker, second, 150);
  TEST_ASSERT_EQUAL_INT32(150, pokerBetGetCurrentCallValue(&poker));

  // Second Pot
  pokerPotAdd(&poker);
  TEST_ASSERT_EQUAL_INT32(150, pokerBetGetCurrentCallValue(&poker));
  TEST_ASSERT_EQUAL_INT32(0, poker.pots[1].chips);
  pokerBetForPlayer(&poker, second, 50);
  TEST_ASSERT_EQUAL_INT32(200, pokerBetGetCurrentCallValue(&poker));
  TEST_ASSERT_EQUAL_INT32(50, poker.pots[1].chips);
}

int test_bet_h() {
  UNITY_BEGIN();

  RUN_TEST(test_pokerBetGetNextPlayer_should_GetTheNextBetter);
  RUN_TEST(test_pokerBetGetRemainingBetterCount_should_ReturnCountNeedsToBet);
  RUN_TEST(test_pokerBet_should_AddChipsToThePot);
  RUN_TEST(test_pokerBet_should_UpdatePlayerState);
  RUN_TEST(test_pokerBetForPlayer_should_BetToTheActivePot);
  RUN_TEST(test_pokerBetGetCurrentCallValue_should_CalculateTheCallValue);

  return UNITY_END();
}