/*
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 * 
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include <stdio.h>

#include "player.h"
#include "defines.h"

Player::Player()
{
 card = new int[CARDS];
 foundCards = new FoundCard[CARDS];
 money = START_MONEY;
 for (int i = 0; i < CARDS; i++) {
	card[i] = 0; 	
 }

 human = false;
 name = "Player";
 currentBet = 0;
 isOut = false;
// random.setSeed(0);
}

Player::~Player()
{
 delete[] card;
 delete[] foundCards;
 delete[] cardHelp;
 cardHelp = 0; //this is NECESSARY don't delete this line
}

void Player::giveCardsBack()
{ for (int i = 0; i < CARDS; i++) card[i] = 0; }

void Player::giveCardBack(int cardNr)
{ card[cardNr] = 0; }

void Player::exchangeCards(bool skip[])
{ 
//TODO: improve!
// thy is still a VERY simple method!
 sortedResult(); // make foundCards
 for (int i = 0; i < CARDS; i++) 
 	skip[i] = false; // exchange card
 
 for (int i = 0; i < CARDS && foundCards[i].cardType != 0; i++) 
	skip[foundCards[i].cardNum] = true;
}

void Player::newRound()
{
 giveCardsBack();
 currentBet = 0;
 isOut = false;
}

int Player::bet(int origBet, bool mayRaise)
{
// NOT useable for (status == continueBet) !!!
// needs a rewrite for games with > 2 players
 int bet = origBet;

 // first bet minBet
 currentBet = minBet;
 money -= currentBet;
 bet -= currentBet;
 int newBet = bet; // bet at least the old bet

 if (bet > getCash())// we don't have the money :-(
	return 0;


 // calculate the chances and bet any value
 int chance = sortedResult();

 if (chance < 350) { // 3 of a kind or better!!
	newBet =  maxBet - (int)random.getLong(maxBet /2);//we subtract a 
			// random number to hide our cards (every player would
			// know we have good cards if we would bet maxBet) 
 }
 else if (chance < 400) { // 2 pairs
	newBet = bet + (int)random.getLong(maxBet /2 + origBet);
	if (newBet > maxBet)
		newBet = maxBet;
 }
 else if (chance < 500) { // one pair
	newBet = bet + (int)random.getLong(maxBet /4 + 1);
	if (newBet > getCash() - 2 * minBet)
		newBet = bet;
	if (bet >= getCash() /3)
		newBet = 0;
 }
 else if (chance < 506) { // best card is at least a ten
	newBet = bet;
	if (bet >= getCash() /3)
		newBet = 0;
 }
 else { // bad cards
	if (getCash() - bet >= bet) {// we would still have some money
		newBet = bet;
		if (bet >= getCash() /4)
			newBet = 0;
	}
	else
		newBet = 0;
 }
// and now a final re-check
 if (newBet > bet) {
	if (random.getLong(20) == 0)
		newBet = bet;
	else if (random.getLong(30) <= 1)
		newBet = bet + (newBet - bet) /2;
 }


 if (newBet > getCash())
	newBet = bet; // maybe raise only a little bit but by now just do not raise

 if (!mayRaise && newBet > bet)
	newBet = bet;

 if (!changeBet(newBet))
	return 0; // BIG error

 return currentBet;
}

int Player::raise(int origRaise)
{
// NOT useable for (status == continueRaise) !!!
// needs a rewrite for games with > 2 players
 int raise = origRaise - getCurrentBet();
 int newRaise = raise;

 if (newRaise > getCash())// we don't have the money :-(
	return 0;

 // calculate the chances and bet any value
 int chance = sortedResult();

 if (chance < 350) { // 3 of a kind or better!!
	newRaise =  maxBet - (int)random.getLong(maxBet - maxBet /2);
			// we subtract a random number to hide our cards 
			// (every player would know we have good cards if 
			// we would bet maxBet) 
 }
 else if (chance < 400) { // 2 pairs
	newRaise = raise + (int)random.getLong(maxBet /2 + origRaise + 10);
	if (newRaise > maxBet)
		newRaise = maxBet;
 }
 else if (chance < 500) { // one pair
	newRaise = raise + (int)random.getLong(maxBet /4 + 1);
	if (newRaise > getCash() - 2 * minBet)
		newRaise = raise;
	if (raise >= getCash() /2)
		newRaise = 0;
 }
 else if (chance < 506) { // best card is at least a ten
	newRaise = raise;
	if (raise >= getCash() /2)
		newRaise = 0;
 }
 else { // bad cards
	if (getCash() - raise >= raise && raise <= minBet * 2) { // we would still have some money
		if (raise > getCash() /2)
			newRaise = 0;
		else
			newRaise = raise;
	}
	else
		newRaise = 0;
 }
// and now a final re-check
 if (newRaise > raise) {
	if (random.getLong(20) == 0)
		newRaise = raise;
	else if (random.getLong(30) <= 1)
		newRaise = raise + (newRaise - raise) /2;
 }

 if (newRaise > getCash())
	newRaise = raise; // maybe raise only a little bit but by now just do not raise

 if (!changeBet(newRaise))
	return 0; // BIG error

 return currentBet;
}

bool Player::changeBet(int betChange)
{
 if (currentBet + betChange >= 0 && getCash() - betChange >= 0) {
 	setCash(getCash() - betChange);
	currentBet += betChange;
 	return true;
 }
 return false;
}

void Player::initCardHelp()
{
 cardHelp = new int[highestCard + 1];
 for (int i = 0; i < highestCard /4; i++) 
 	cardHelp[i*4+1]=cardHelp[i*4+2]=cardHelp[i*4+3]=cardHelp[i*4+4]=i;
}

int Player::testHand()
{
 cleanFoundCard();
 int isRoyal = testStraight();
 if (isRoyal != 0) {
 	if (testFlush()) {
		if (isRoyal == 2)
			return 10; // royal flush detected
		else
			return 9; // straight flush detected
	}
	else
		return 7;
 }

 if (testFlush())
 	return 8;

 int matching = 0;
 for (int i = 0; i < CARDS; i++) { // this searches for pairs/three/four of a kind	
	 for (int i2 = i + 1; i2 < CARDS; i2++) 
		if (cardHelp[card[i]] == cardHelp[card[i2]]) {
			matching++;
			addFoundCard(i, card[i]);
			addFoundCard(i2, card[i2]);
		}
 }
 return matching;
}

void Player::addFoundCard(int cardNum, int cardType)
{
 int i;
 bool found = false;
 for (i = 0; foundCards[i].cardType != 0; i++)
 	if (foundCards[i].cardNum == cardNum) found = true;

 if (!found) {
	foundCards[i].cardNum = cardNum;
 	foundCards[i].cardType = cardType;
 }
}

bool Player::testFlush()
{
 int mycolor;
 mycolor = card[0] % 4;
 for (int i = 1; i < CARDS; i++)
 	if (mycolor != card[i] % 4)
		return 0;

 for (int i = 0; i < CARDS; i++)
 	addFoundCard(i, card[i]);
 return 1;	
}

int Player::testStraight()
{
 int cardTypes[CARDS];
 int lowest = 100; // lowest cardtype -> set to something high first :)

 for (int i = 0; i < CARDS; i++) {
 	cardTypes[i] = cardHelp[card[i]];
	if (cardTypes[i] < lowest)
		lowest = cardTypes[i];
 }

 // look for special cases ace-2-3-4-5
// very ugly but fast to write:
 if (! (findCardTypes(cardTypes, 0) && findCardTypes(cardTypes, 12) && 
	findCardTypes(cardTypes, 11) && findCardTypes(cardTypes, 10) && 
	findCardTypes(cardTypes, 9)) ) {
/*
// this is not allowed in the basic rules:
// and: K-A-2-3-4 !!! and: Q-K-A-2-3 ...
	! (findCardTypes(cardTypes, 0) && findCardTypes(cardTypes, 1) &&
	findCardTypes(cardTypes, 12) && findCardTypes(cardTypes, 11) &&
	findCardTypes(cardTypes, 10)) &&
	! (findCardTypes(cardTypes, 0) && findCardTypes(cardTypes, 1) &&
	findCardTypes(cardTypes, 2) && findCardTypes(cardTypes, 12) &&
	findCardTypes(cardTypes, 11)) &&
	! (findCardTypes(cardTypes, 0) && findCardTypes(cardTypes, 1) &&
	findCardTypes(cardTypes, 2) && findCardTypes(cardTypes, 3) &&
	findCardTypes(cardTypes, 12)) ) {*/
 	for (int i = 0; i < CARDS; i++)
		if(!findCardTypes(cardTypes, lowest + i)) 
 			return 0; // did not find a straight
 }

// found a straight: 
 for (int i = 0; i < CARDS; i++)
	addFoundCard(i, card[i]);

 if (lowest == 0 && findCardTypes(cardTypes, 4)) { // Ace and 10
	return 2; // a royal flush :-o
 }
 return 1;
}

bool Player::findCardTypes(int cardT[CARDS], int card)
{
 for (int i = 0; i < CARDS; i++)
 	if(cardT[i] == card)
		return true;
 return false;
}

void Player::cleanFoundCard()
{
 for (int i = 0; i < CARDS; i++)
	foundCards[i].cardType = 0;
}

int Player::addFoundCardsUp()
{
 int allCards = 0;

 for (int i = 0; foundCards[i].cardType != 0 && i < CARDS; i++) {
	allCards += cardHelp[foundCards[i].cardType];
 }
 return allCards;
}


int Player::sortedResult()
{
 int result = testHand();

 //ok the result produced by testHand() is a little bit... uncomfortable
 //so lets sort it for use in displayWinner_Computer()
 //additionally we extend the values e.g. by bestCard and so on


 int newResult = addFoundCardsUp(); // i hope this works
//maybe problems with straights and flushes?

 switch (result) {
	case 1 :
		newResult += 400; // one pair
		break;
  	case 2 :
		newResult += 350; // two pairs
		break;
  	case 3 :
		newResult += 300; // 3 of a kind
		break;
  	case 4 :
		newResult += 250; // full house
		break;
  	case 6 :
		newResult += 200; // 4 of a kind
		break;
  	case 7 :
		newResult = 150; // straight
		break;
  	case 8 :
		newResult += 100; // flush
		break;
  	case 9 :
		newResult += 50; // straight flush
		break;
	case 10 :
		newResult += 0; // ok the royal flush is doubtless the best you can ever get
		break;

  	default: // player has only bestCard (or i have a mistake) :-(
		int bestCard = cardHelp[card[0]];
		int bestCardNr = 0;
		for (int i = 1; i < CARDS; i++) {
			if (cardHelp[card[i]] < bestCard){
				bestCard = cardHelp[card[i]];
				bestCardNr = i;
			}
		}
		addFoundCard(bestCardNr, getCard(bestCardNr));
		newResult = 500 + bestCard;
		break;
 }

 return newResult;
 // the lowest newResult is now the best
}

int Player::findBestButFoundCard()
{
 int bestCard = 100;
 int bestCardNr = 0;
 for (int i = 0; i < CARDS; i++) {
 	if (cardHelp[getCard(i)] < bestCard) {
		bool alreadyFound = false;
 
		for (int j = 0; !alreadyFound && getFoundCard(j).cardType != 0 && j < CARDS; j++) {
			if(getFoundCard(j).cardNum == i)
				alreadyFound = true;
		}
		if (!alreadyFound) {
			bestCard = cardHelp[getCard(i)];
			bestCardNr = i;
		}
	}

 }
 addFoundCard(bestCardNr, getCard(bestCardNr));
 return bestCard;
}

int Player::findBestCard()
{
// this method searches for the best card of the player
// it is used mainly (and up to now only) to get the winner of a REAL draw
// to be exact only when 2 or more players have exactly the same cards but the suits
 int bestCard = cardHelp[getCard(0)];
 int bestCardType = getCard(0);
 for (int i = 1; i < CARDS; i++) {
	if (cardHelp[getCard(i)] < bestCard) {
		bestCard = cardHelp[getCard(i)];
		bestCardType = getCard(i);
	}
 }
 return bestCardType;
}
