#include <windows.h>
#include <mmsystem.h>
#include <string.h>
#include <stdio.h>
#include "card.h"
#include "hand.h"
#include "hands.h"
#include "deck.h"

#include "ok.h"

#define CHKSOUND "chk.wav"

/*
	Function:		Hand::Hand()

	Parameters:		itype -- type of hand being created (two card, five card or seven card)
                  iwho  -- who's hand (player or dealer)

	Description: 	Hand constructor

	Returns: 		nothing
*/

Hand::Hand(int itype, int iwho)
{

   memset(&start_of_hand_data, 0, HAND_DATALEN);

   type = itype;
   who = iwho;

}

/*
	Function:		Hand::SwapCards()

	Parameters:		a  -- pointer to card object
                  b  -- pointer to card object

	Description: 	swaps two card objects

	Returns: 		nothing
*/

void 
Hand::SwapCards(Card *a, Card *b)
{
	Card c;

	c = *a;
	*a = *b;
	*b = c;
}

/*
	Function:		Hand::Sort()

	Parameters:		none

	Description: 	Arranges cards in a hand in descending order

	Returns: 		nothing
*/

void 
Hand::Sort(void)
{
   int i, j, amount_to_sort;

   switch (type) {
      case TWO_CARD:
         amount_to_sort = 2;
      break;
      case FIVE_CARD:
         amount_to_sort = 5;
      break;
      case SEVEN_CARD:
         amount_to_sort = 7;
      break;
   }

	for (i = amount_to_sort; i != 1; i--) 
		for (j = 0; j != amount_to_sort - 1; j++) 
			if (card[j].GetValue() < card[j + 1].GetValue()) 
				SwapCards(&card[j], &card[j + 1]);
}

/*
	Function:		Hand::Parse()

	Parameters:		none

	Description: 	Determines the type of poker hand of the current Hand object and assigns various members of the object

	Returns: 		nothing
*/

void 
Hand::Parse(void)
{
	int i, have_ace = 0, values[5], j = 0;
	int have_straight = 0, have_flush = 0, have_three_ok = 0,
		 number_of_pairs = 0;
	int numbers[15];
	char suit;

   switch (type) {
      case TWO_CARD:
	      if (card[0].GetValue() == card[1].GetValue()) {
		      hand_type = PAIR;
		      first_pair = card[0].GetValue();
	      }
	      else {
		      hand_type = HIGHCARD;
		      high_card = card[0].GetValue();
	      }
      break;
      case FIVE_CARD:
	      for (i = 0; i != 15; i++)
		      numbers[i] = 0;

	      for (i = 0; i != 5; i++)
		      values[i] = card[i].GetValue();

	      Sort();

	      high_card = card[0].GetValue();

	      for (i = 0; i != 5; i++)
		      if (card[i].GetValue() == ACE)
			      have_ace = 1;

	      if ((card[0].GetSuit() == card[1].GetSuit()) && (card[1].GetSuit() == card[2].GetSuit()) &&
		      (card[2].GetSuit() == card[3].GetSuit()) && (card[3].GetSuit() == card[4].GetSuit()))
		      have_flush = 1;

	      for (i = 0; i != 4; i++) {
		      if (card[i].GetValue() - card[i + 1].GetValue() != 1)
			      break;
		      j++;
	      }
	      if (j == 4)
		      have_straight = 1;

	      if (have_ace && card[1].GetValue() == FIVE) {
		      j = 0;
		      for (i = 1; i != 4; i++) {
			      if (card[i].GetValue() - card[i + 1].GetValue() == 1)
				      j++;
		      }
		
		      if (j == 3) {
			      have_straight = 1;
					baby = 1;		// baby straights are A 2 3 4 5 and are second highest straight
		      }
	      }

	      if (have_straight && have_flush) 
		      hand_type = STRAIGHTFLUSH;
	      else if (have_flush) 
		      hand_type = FLUSH;
	      else if (have_straight) 
		      hand_type = STRAIGHT;
	      else {
		      for (i = 0; i != 5; i++) 
			      numbers[card[i].GetValue()]++;

		      for (i = 0; i != 15; i++) {
	
			      if (numbers[i] == 4) {
						four_of_what = i;
				      hand_type = FOUROFAKIND;
			      }
			      else if (numbers[i] == 3) {
				      three_of_what = i;
				      have_three_ok = 1;
			      }
			      else if (numbers[i] == 2) {
				      number_of_pairs++;
				      if (number_of_pairs == 1)
							first_pair = i;
				      else 
							second_pair = i;
			      }
		      }

		      if (hand_type)
			      ;
		      else if (have_three_ok && number_of_pairs) 
			      hand_type = FULLHOUSE;
		      else if (have_three_ok) 
			      hand_type = THREEOFAKIND;
		      else if (number_of_pairs == 2) 
			      hand_type = TWOPAIRS;
		      else if (number_of_pairs == 1) 
			      hand_type = PAIR;
		      else 
			      hand_type = HIGHCARD;
	      }
      break;
   }
}

/*
	Function:		Hand::Describe()

	Parameters:		hdc   -- handle of device conext to output description to
                  x     -- horizontal position to place description
                  y     -- vartical position to place description

	Description: 	Writes out a description of a hand to the screen

	Returns: 		nothing
*/

void 
Hand::Describe(HDC hdc, short x, short y)
{

	char szBuffer[100], szNamebuffer[MAX_NAME + 1], szNamebuffer2[MAX_NAME + 1];

	char *hand_types[] = { "High Card", "Pair", "Two Pairs", "Three of a Kind",
								"Straight", "Flush", "Full House", "Four of a Kind",
								"Straight Flush" };

   int nLength;

   SetBkColor(hdc, RGB(0, 255, 0)); 

   switch (type) {
      case TWO_CARD:

	      if (hand_type == PAIR)
				TextOut(hdc, x, y, szBuffer, sprintf(szBuffer, "Pair of %s's", CardName(first_pair, szNamebuffer, MAX_NAME)));
	      else
				TextOut(hdc, x, y, szBuffer, sprintf(szBuffer, "%s high", CardName(high_card, szNamebuffer, MAX_NAME)));
      break;
      case FIVE_CARD:
	      switch (hand_type) {

		      case HIGHCARD:
			      nLength = sprintf(szBuffer, "%s high", CardName(high_card, szNamebuffer, MAX_NAME));
		      break;
		      case PAIR:
			      nLength = sprintf(szBuffer, "%s of %s's", hand_types[hand_type], CardName(first_pair, szNamebuffer, MAX_NAME));
		      break;
		      case TWOPAIRS:
			      nLength = sprintf(szBuffer, "%s %s's and %s's", hand_types[hand_type], CardName(first_pair, szNamebuffer, MAX_NAME), CardName(second_pair, szNamebuffer2, MAX_NAME));
		      break;
		      case THREEOFAKIND:
			      nLength = sprintf(szBuffer, "Three %s's", CardName(three_of_what, szNamebuffer, MAX_NAME));
		      break;
		      case STRAIGHT:
			      if (baby)
				      nLength = sprintf(szBuffer, "Baby straight");
			      else
				      nLength = sprintf(szBuffer, "%s high straight", CardName(high_card, szNamebuffer, MAX_NAME));
		      break;
		      case FLUSH:
			      nLength = sprintf(szBuffer, "%s high flush", CardName(high_card, szNamebuffer, MAX_NAME));
		      break;
		      case FULLHOUSE:
					nLength = sprintf(szBuffer, "%s %s's over %s's", hand_types[hand_type], CardName(three_of_what, szNamebuffer, MAX_NAME), CardName(first_pair, szNamebuffer2, MAX_NAME));
		      break;
		      case FOUROFAKIND:
			      nLength = sprintf(szBuffer, "Four %s's", CardName(four_of_what, szNamebuffer, MAX_NAME));
		      break;
		      case STRAIGHTFLUSH:
			      if (baby)
				      nLength = sprintf(szBuffer, "Baby straight flush");
			      else
				      nLength = sprintf(szBuffer, "%s high straight flush", CardName(high_card, szNamebuffer, MAX_NAME));
		      break;
		      default:
			      nLength = sprintf(szBuffer, "hand_type = %d", hand_type);
		      break;
	      }
			TextOut(hdc, x, y, szBuffer, nLength);

      break;

   }
}

/*
	Function:		Hand::operator>()

	Parameters:		hand -- reference to a Hand object

	Description: 	Compares the current hand object with a passed hand object.
                  This function assumes that the two hand types are the same.

	Returns: 		1 if current hand object is of higher value than the one
                  passed to this function.
*/

int 
Hand::operator>(Hand &hand)
{
	int retval = 0;
	int i, hc1, hc2, hca1[3], hca2[3], j = 0;

   char buffer[100];

   switch (type) {

      case TWO_CARD:
	      if (hand_type < hand.hand_type) 
		      retval = -1;
	      else if (hand_type > hand.hand_type)
		      retval = 1;
	      else {		// hands are equivalent rank so go by card values
		      if (card[0].GetValue() < hand.card[0].GetValue())
			      retval = -1;
		      else if (card[0].GetValue() > hand.card[0].GetValue())
			      retval = 1;
		      else {	// first cards were equivalent-- second card will determine outcome
			      if (card[1].GetValue() < hand.card[1].GetValue())
				      retval = -1;
			      else if (card[1].GetValue() > hand.card[1].GetValue())
				      retval = 1;
			      else
				      retval = 0;
		      }
	      }
      break;

      case FIVE_CARD:

	      if (hand_type < hand.hand_type) 
				retval = -1;
	      else if (hand_type > hand.hand_type)
		      retval = 1;
	      else {		// hands are equivalent rank so go by card values
		      switch (hand_type) {
			      case HIGHCARD:
			      case STRAIGHT:
			      case FLUSH:
			      case STRAIGHTFLUSH:
				      for (i = 0; i != 5; i++) {
					      if (card[i].GetValue() < hand.card[i].GetValue())
						      return(-1);
					      else if (card[i].GetValue() > hand.card[i].GetValue())
						      return(1);
				      }
			      break;
			      case PAIR:
				      if (first_pair < hand.first_pair)
					      retval = -1;
				      else if (first_pair > hand.first_pair)
					      retval = 1;
				      else {	// pairs are the same -- so compare the 3 other cards
					      for (i = 0; i != 5; i++) {
						      if (first_pair != card[i].GetValue()) { // find the highest non pair card
							      hca1[j] = card[i].GetValue();
							      j++;
						      }
					      }
					      j = 0;
					      for (i = 0; i != 5; i++) {
						      if (hand.first_pair != hand.card[i].GetValue()) { // find the highest non pair card
							      hca2[j] = hand.card[i].GetValue();
							      j++;
						      }
					      }
					      for (i = 0; i != 3; i++) {
						      if (hca1[i] < hca2[i])
							      return(-1);
						      else if (hca1[i] > hca2[i])
							      return(1);
					      }
				      }
			      break;
			      case TWOPAIRS:
				      if (first_pair < hand.first_pair)
					      retval = -1;
				      else if (first_pair > hand.first_pair)
					      retval = 1;
				      else if (second_pair < hand.second_pair)
					      retval = -1;
				      else if (second_pair > hand.second_pair)
					      retval = 1;
				      else {	// compare last card
					      for (i = 0; i != 5; i++) {
						      if (first_pair != card[i].GetValue())  // find the highest non pair card
							      hc1 = card[i].GetValue();
					      }
					      for (i = 0; i != 5; i++) {
						      if (hand.first_pair != hand.card[i].GetValue())  // find the highest non pair card
							      hc2 = hand.card[i].GetValue();
					      }
					      if (hc1 < hc2)
						      retval = -1;
					      else if (hc1 > hc2)
						      retval = 1;
				      }
			      break;
			      case THREEOFAKIND:
				      if (three_of_what < hand.three_of_what)
					      retval = -1;
				      else 
					      retval = 1;
			      break;
			      case FOUROFAKIND:
				      if (four_of_what < hand.four_of_what)
					      retval = -1;
				      else 
					      retval = 1;
			      break;
		      }
	      }
      break;
   }
	return(retval);
}


/*
	Function:		Hand::CheckHand()

	Parameters:		two_card_hand - player's two card hand

	Description: 	Compares the values of the current five card
                  hand (whose object should have called this function)
                  and a two card hand which is passed to this function.

	Returns: 		1 if the five card hand is greater than the two card hand
                  0 if the five card hand is less than the two card hand
*/

int 
Hand::Check(Hand *two_card_hand)
{
   int retval = 1, i;
   char szBuffer[100];

   if (type == TWO_CARD)
      return(-1);             //indicate that there is an error -- must call this with the FIVE_CARD

	if (two_card_hand->hand_type > hand_type)
		retval = 0;
	else if (two_card_hand->hand_type < hand_type)
		retval = 1;
	else {
		if (two_card_hand->hand_type == PAIR) {
			if (two_card_hand->card[0].GetValue() > first_pair)
				retval = 0;
			else 
				retval = 1;
		}
		else {
			for (i = 0; i != 2; i++) {
				if (two_card_hand->card[i].GetValue() > card[i].GetValue()) 
					return(0);
				else if (two_card_hand->card[i].GetValue() < card[i].GetValue())
					return(1);
			}
		}
	}
	return(retval);
}

/*
	Function:		Hand::Show()

	Parameters:		hdc      -- device context handle
                  x        -- horizontal position on screen to display hand
                  y        -- vertical position on screen to display hand
                  spacing  -- horizontal spacing of cards

	Description: 	Displays current hand object as card bitmaps

	Returns: 		Nothing
*/

void 
Hand::Show(HDC hdc, short x, short y, int spacing)
{
	int i, max;
	char szBuffer[MAX_NAME + 1];
	HBITMAP hBitmap;

   switch (type) {
      case TWO_CARD:
         max = 2;
         break;
      case FIVE_CARD:
         max = 5;
         break;
      case SEVEN_CARD:
			max = 7;
         break;
   }

	for (i = 0; i < max; i++) {
		hBitmap = LoadBitmap(hInst, card[i].GetBitMapName(szBuffer, MAX_NAME));
		card[i].DrawBitmap(hdc, hBitmap, x, y);
		x += spacing;	// make this a function of width of bitmap of card
		DeleteObject(hBitmap);
		if (type == SEVEN_CARD)
   		sndPlaySound(CHKSOUND, SND_SYNC);	
	}
}

