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

#pragma once
#include "../libs.h"
#include "card.h"
#include "player.h"
#include "winner.h"
#include "dealer.h"

/** Size of the FULL hand used to calculate a winning. */
#define POKER_WINNING_FULL_SIZE POKER_PLAYER_HAND+POKER_DEALER_HAND_SIZE

/** How many cards make a winning set */
#define POKER_WINNING_SET_SIZE 5

/** Winning Types */  
#define POKER_WINNING_TYPE_NULL 0x00
#define POKER_WINNING_TYPE_ROYAL_FLUSH 0x01
#define POKER_WINNING_TYPE_STRAIGHT_FLUSH 0x02
#define POKER_WINNING_TYPE_FOUR_OF_A_KIND 0x03
#define POKER_WINNING_TYPE_FULL_HOUSE 0x04
#define POKER_WINNING_TYPE_FLUSH 0x05
#define POKER_WINNING_TYPE_STRAIGHT 0x06
#define POKER_WINNING_TYPE_THREE_OF_A_KIND 0x07
#define POKER_WINNING_TYPE_TWO_PAIR 0x08
#define POKER_WINNING_TYPE_PAIR 0x09
#define POKER_WINNNIG_TYPE_HIGH_CARD 0x0A

#define POKER_WINNNIG_CONFIDENCE_ROYAL_FLUSH 1.0f
#define POKER_WINNNIG_CONFIDENCE_STRAIGHT_FLUSH 0.99f
#define POKER_WINNNIG_CONFIDENCE_FOUR_OF_A_KIND 0.9f
#define POKER_WINNNIG_CONFIDENCE_FULL_HOUSE 0.85f
#define POKER_WINNNIG_CONFIDENCE_FLUSH 0.8f
#define POKER_WINNNIG_CONFIDENCE_STRAIGHT 0.7f
#define POKER_WINNNIG_CONFIDENCE_THREE_OF_A_KIND 0.5f
#define POKER_WINNNIG_CONFIDENCE_TWO_PAIR 0.4f
#define POKER_WINNNIG_CONFIDENCE_PAIR 0.2f
#define POKER_WINNNIG_CONFIDENCE_HIGH_CARD 0.1f

/** Holds information about a player's winning state */
typedef struct {
  /** The full set of both the dealer and player's hand */
  card_t full[POKER_WINNING_FULL_SIZE];
  uint8_t fullSize;
  
  /** Holds the winning set */
  card_t set[POKER_WINNING_SET_SIZE];
  uint8_t setSize;

  /** Winning Type */
  uint8_t type;

  /** If there was a kicker card it will be here, otherwise -1 for no kicker */
  card_t kicker;
} pokerplayerwinning_t;

typedef struct {
  /** Winning States */
  pokerplayerwinning_t winnings[POKER_PLAYER_COUNT];
  uint8_t winners[POKER_PLAYER_COUNT];
  uint8_t winnerCount;
} pokerwinner_t;

/**
 * Returns the full hand for a given player including the best cards on the 
 * bench.
 * 
 * @param dealer Poker game dealer instance.
 * @param player Poker player game instance.
 * @param cards Array of at least 7 length to store the array.
 */
void pokerWinnerHandGetFull(
  pokerdealer_t *dealer, pokerplayer_t *player, card_t *cards
);

/**
 * Calculates and returns the winning state for a given player
 * 
 * @param dealer Poker game dealer instance.
 * @param players Array of poker players.
 * @param winning Pointer to the poker winning to fill out.
 * @return The winning state for this player. 
 */
void pokerWinnerPlayerGet(
  pokerdealer_t *dealer, pokerplayer_t *player, pokerplayerwinning_t *winning
);

/**
 * Fills the remaining cards for a given poker player winning hand. Essentially
 * this will just take the highest cards and slot them into the array.
 * @param winning Pointer to the poker winning to fill out.
 */
void _pokerWinnerFillRemaining(pokerplayerwinning_t *winning);

/**
 * Compares two winning sets. The returned card is the kicker if the LEFT side
 * is the winner. If LEFT is not a winner then 0xFF will be returned.
 * 
 * @param left Left winning set.
 * @param right Right winning set.
 * @return The kicker card from left's hand or 0xFF if left is not the winner.
 */
card_t pokerWinnerCompare(
  pokerplayerwinning_t *left, pokerplayerwinning_t *right
);

/**
 * Determines the winning player for the game. Values will be stored back into
 * the poker winning state.
 * 
 * @param winner Location to store the winner state.
 * @param dealer Poker game dealer instance.
 * @param players Array of poker players.
 */
void pokerWinnerCalculate(
  pokerwinner_t *winner, pokerdealer_t *dealer, pokerplayer_t *players
);

/**
 * Get the best valued card from a given hand.
 * 
 * @param cards Array of cards.
 * @param cardCount Count of cards in the array
 * @return The best card within the array.
 */
card_t pokerWinnerGetBestCard(card_t *cards, uint8_t cardCount);


/**
 * Get the confidence of the bet for a given winning type.
 * 
 * @param type Winning type type.
 * @return The confidence.
 */
float pokerWinnerGetTypeConfidence(uint8_t type);

/**
 * Get the weight of a card.
 * 
 * @param card Card to get the weight of.
 * @return The cards' weight.
 */
float pokerWinnerGetCardWeight(card_t card);