//*****************************************************************************
// 
// Blackjack - an X windows program of the casino game.
// Copyright (C) 1997  Tom Daley 
// 
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
// 
// The license is in the file COPYING.
// 
// Tom Daley
// 1309 W. Lorraine Ave.
// Woodland Park, CO  80863-2329
// 
// daley@codenet.net (Tom Daley)
// http://users.codenet.net/daley
// 
//*****************************************************************************

#include <stdlib.h> 
#include <stdio.h> 
#include <fstream.h> 
#include <qmsgbox.h> 
#include <qapp.h>
#include <qbttngrp.h>
#include <qradiobt.h>
#include <qregexp.h>

#include "blackjack.h"
#include "blackjack.bm"
#include "hand.h"
#include "card.h"
#include "bet_option.h"
#include "table_option.h"
#include "insurance_window.h"

//*****************************************************************************
//
// STACK_DECK is used for testing.  If you want a certain set of cards 
// to get a certain test case, uncomment this line and enter cards below.
//
// #define STACK_DECK
//
//*****************************************************************************

BlackJack::
BlackJack(QWidget *parent, const char *name) : QWidget(parent, name),
           _hand_in_progress(0), _shuffle(0) {
   QSize min_status, min_input, min_table, min_blackjack;
   _min_bet = 1.00;	// default to Colorado betting rules
   _max_bet = 5.00;
   _bet_inc = 0.50;

   _decks = 4;		// other default values
   _max_splits = MAX_SPLITS;
   _shuffle_threshold = 24;
   _help = NO_HELP;
   _bank_roll = 200.00;
   
   QBitmap icon(blackjack_width, blackjack_height, blackjack_bits, TRUE);
   setIcon(icon);

// make the menu bar
   QPopupMenu *file = new QPopupMenu;
   file->insertItem("Exit", qApp, SLOT(quit()));

   QPopupMenu *help = new QPopupMenu;
   help->insertItem("None", this, SLOT(noHelp()));
   help->insertItem("Warning", this, SLOT(warnHelp()));
   help->insertItem("Pickey", this, SLOT(pickeyHelp()));
   help->insertItem("Full", this, SLOT(fullHelp()));

   QPopupMenu *options = new QPopupMenu;
   options->insertItem("Help", help);
   options->insertItem("Bet", this, SLOT(betOptions()));
   options->insertItem("Table", this, SLOT(tableOptions()));
   options->insertSeparator();
   options->insertItem("Save", this, SLOT(saveOptions()));

   QPopupMenu *info = new QPopupMenu;
   info->insertItem("About", this, SLOT(about()));
   info->insertItem("About Qt", this, SLOT(aboutQt()));

   _menu_bar_p = new QMenuBar(this);
   _menu_bar_p->insertItem( "File", file );
   _menu_bar_p->insertItem( "Options", options );
   _menu_bar_p->insertSeparator();
   _menu_bar_p->insertItem( "Info", info );

   readHelpFile(); 	// read in user preferences
   if (_max_bet < 0.05)	// if user preferences were way off, set a decent value
      _max_bet = 1.00;
   if (_min_bet > _max_bet)
      _min_bet = _max_bet;
   if (_bet_inc > _max_bet || _bet_inc < 0.05)
      _bet_inc = _min_bet;
   if (_shuffle_threshold <= 0 || _shuffle_threshold > 50)
      _shuffle_threshold = 20;
   if (_max_splits < 0 || _max_splits > MAX_SPLITS)
      _max_splits = MAX_SPLITS;
   if (_decks < 0 || _decks > 6)
      _decks = 4;

   _input_p = new UserInput(_min_bet, _max_bet, _bet_inc, this, "user_input");
   _input_p->makeInvalid(UserInput::Hit|UserInput::Stand|
                         UserInput::Double|UserInput::Split);
   _status_p = new StatusDisplay(_bank_roll, "No Help", this, "status_display");
   _table_p = new CardTable(MAX_SPLITS + 1, this, "card_table");
   _shoe_p = new Shoe(_decks);

   switch (_help) {
      case FULL_HELP:
         fullHelp();
         break;
      case WARNING:
         warnHelp();
         break;
      case PICKEY:
         pickeyHelp();
         break;
      case NO_HELP:
         noHelp();
         break;
   }

#ifdef STACK_DECK
   int j=0;
   _shoe_p->putCard(Card::THREE, Card::CLUB,  j++);
   _shoe_p->putCard(Card::SIX,   Card::SPADE, j++);
   _shoe_p->putCard(Card::THREE, Card::CLUB,  j++);
   _shoe_p->putCard(Card::NINE,  Card::SPADE, j++);
   _shoe_p->putCard(Card::FIVE,  Card::CLUB,  j++);
   _shoe_p->putCard(Card::NINE,  Card::HEART, j++);
   _shoe_p->putCard(Card::EIGHT, Card::CLUB,  j++);
#endif

// set minimum size for main window
   min_status = _status_p->minimumSize(); 
   min_input  = _input_p->minimumSize(); 
   min_table  = _table_p->minimumSize(); 

   if (min_status.width() > min_input.width())
      min_blackjack.setWidth(min_status.width() + min_table.width()); 
   else
      min_blackjack.setWidth(min_input.width() + min_table.width()); 

   if (min_table.height() > min_input.height() + min_status.height())
      min_blackjack.setHeight(min_table.height() + _menu_bar_p->height()); 
   else
      min_blackjack.setHeight(min_input.height() + min_status.height() + 
                              _menu_bar_p->height()); 

   setMinimumSize(min_blackjack);

// See if help is set to FULL, if so tell player to "place a bet"
   checkPreHelp();

   resizeEvent(NULL);
}

//*****************************************************************************

BlackJack::
~BlackJack() {
   delete (_shoe_p);
}

//*****************************************************************************

void BlackJack::
resizeEvent(QResizeEvent *) {
   int l_width = 100;
   int m_bar_height = _menu_bar_p->height();
   int height_remaining = height() - m_bar_height;

   _input_p->setGeometry(0, m_bar_height, l_width, height_remaining * 2/3);

   _status_p->setGeometry(0, m_bar_height + height_remaining * 2/3, 
                         l_width, height_remaining * 1/3 );

   _table_p->setGeometry(l_width, m_bar_height, 
                         width() - l_width, height_remaining);
}

//*****************************************************************************

void BlackJack::
hit() {
   if (_shoe_p->cardsLeft() <= 0) 
      outOfCards();
   else {
      if (checkPostHelp('H')) {
         Hand *hand_p = _table_p->getPlayerHand(_current_player_hand);
         _input_p->makeInvalid(UserInput::Double|UserInput::Split);
         _table_p->addCard(hand_p, _shoe_p->getCard(Card::FACE_UP)); 
         if (hand_p->value() > 21)
            nextHand();
         else
            checkPreHelp();

         _table_p->updatePlayerHands();
         if (hand_p->value() == 21) {
            _dealer_must_play = 1;
            nextHand();
         }
      }
   }
}

//*****************************************************************************

void BlackJack::
split() {
   if (_shoe_p->cardsLeft() <= 1) 
      outOfCards();
   else {
      if (checkPostHelp('P')) {
         Hand *hand_p = new Hand;
         Hand *current_hand_p = _table_p->getPlayerHand(_current_player_hand);
         Card *card_p = current_hand_p->removeCard();   

         _table_p->addPlayerHand(hand_p);
         _table_p->addCard(hand_p, card_p);
         _input_p->makeInvalid(UserInput::Split);
         _table_p->addCard(current_hand_p, _shoe_p->getCard(Card::FACE_UP)); 
         hand_p->setBet(current_hand_p->getBet()); 
         _table_p->betText(hand_p);

         if (card_p->getRank() == Card::ACE) {	// split aces only get one card
            _table_p->addCard(hand_p, _shoe_p->getCard(Card::FACE_UP));
            _dealer_must_play = 1;
            playDealerHand();
         } else if (current_hand_p->value() == 21) { // if 10 split hits an ACE
            _dealer_must_play = 1;
            nextHand();
         } else {				// see if user can split again
            checkForSplit(); 
         }
         checkPreHelp();
         _table_p->updatePlayerHands();
      }
   }
}

//*****************************************************************************

void BlackJack::
doubleDown() {
   if (_shoe_p->cardsLeft() <= 0) 
      outOfCards();
   else {
      if (checkPostHelp('D')) {
         Hand *hand_p = _table_p->getPlayerHand(_current_player_hand);
         hand_p->setBet(hand_p->getBet() * 2.0); 
         _table_p->betText(hand_p);
         _table_p->addCard(hand_p, _shoe_p->getCard(Card::FACE_UP)); 
         _table_p->updatePlayerHands();
   
         if (hand_p->value() <= 21) {
            _dealer_must_play = 1;
         }
         nextHand();
      }
   }
}

//*****************************************************************************

void BlackJack::
stand() {
   if (checkPostHelp('S')) {
      _dealer_must_play = 1;
      nextHand();
   }
}

//*****************************************************************************

void BlackJack::
bet() {
   if (_shoe_p->cardsLeft() < 4) {
      outOfCards();
   } else {
      _input_p->makeInvalid(UserInput::Bet);
      _hand_in_progress = 1;
      _table_p->clear();

      dealCards();
      Hand *hand_p = _table_p->getPlayerHand(_current_player_hand);
      hand_p->setBet(_input_p->getBet());
      _table_p->betText(hand_p);
      _table_p->statusText(hand_p, "Active");
      checkPreHelp();
      _table_p->updatePlayerHands();

      if (_table_p->getDealerHand()->getCard(1)->getRank() == Card::ACE)
         insurance();
   
      if (hand_p->value() == 21)
         playDealerHand();
      else
         postDealChecks();
   }
}

//*****************************************************************************

void BlackJack::
insurance(void) {
   Hand *current_hand_p = _table_p->getPlayerHand(_current_player_hand);
   InsuranceWindow iw(current_hand_p->getBet()/2, _bet_inc, 
                      this, "Insurance", TRUE);
   iw.show();
   current_hand_p->setInsuranceBet(iw.bet()); 

   if (current_hand_p->getInsuranceBet() > (_bet_inc/2)) { // if insurance bet
      float multiplier;
      QString str;
      QMessageBox mb;
      if (_table_p->getDealerHand()->value() == 21) {
         multiplier = 2.0;
         str.sprintf("Dealer has blackjack\n"
                     "Insurance pays $%4.2f", 
                     current_hand_p->getInsuranceBet() * 2);
      } else {
         multiplier = -1.0;
         str.sprintf("Dealer doesn't have blackjack\n"
            "Lose $%4.2f insurance bet", current_hand_p->getInsuranceBet());
      }
      mb.setText(str);
      mb.setButtonText(0, "OK");
      mb.show();
      _status_p->adjustBankroll(multiplier, current_hand_p->getInsuranceBet());
   }
}

//*****************************************************************************

void BlackJack::
readHelpFile(char *name) {
   char *cp;
   int help_file_error = 0;
   QString error_text, file_name = name;

   if (file_name.length() == 0)
      file_name = DEFAULT_HELP_FILE;

   if (cp = getenv("HOME")) {
      QString home(cp);
      file_name = home + (QString)"/" + file_name;
   }

   ifstream in_file(file_name);

   if (! in_file) {
      error_text = createHelpFile(file_name);
      if (error_text.length() == 0) 
         in_file.open(file_name);
   }

   if (error_text.length() == 0) {
      int line_number = 0;

      while (in_file) {
         char buffer[128];
         QString line;
         QRegExp comment("#.*");

         in_file.getline(buffer, 128);
         line = buffer;
         line_number++;
         line.replace(comment, "");
         line = line.simplifyWhiteSpace();
         if (line.length()) {
            QRegExp between("[a-zA-Z0-9._]\\s+[a-zA-Z0-9]");
            int fields = line.contains(between) + 1;
            if (fields == 11) {		// help line
               error_text += readHelpLine(line);
            } else if (fields == 2) {	// argument line
               error_text += readVariableLine(line);
            } else {			// not a valid line
               QString str;
               if (!help_file_error) {
                  str.sprintf("Error while reading help file %s\n",
                             (const char *)file_name);
                  error_text += str;
                  help_file_error++;
               }
               str.sprintf("Bad help file entry, line %d\nLine is: %s\n",
                           line_number, buffer);
               error_text += str;
            }
         }
      }
   }
   if (error_text.length()) {
      QMessageBox mb;
      _help = BAD_HELP;
      mb.setText(error_text);
      mb.setButtonText(0, "OK");
      mb.show();
   }
}

//*****************************************************************************

QString BlackJack::
readHelpLine(QString line) {
   QString error_text, hand_val, index, help_options;
   QRegExp word("[0-9._a-zA-Z]+");
   char up_cards[] = "23456789XA";
   int i, line_index = 0, len;

   word.match(line, line_index, &len);
   hand_val = line.mid(line_index, len);
   line_index += len + 1;
   for (i = 0; i < strlen(up_cards); i++) {
      word.match(line, line_index, &len);
      help_options = line.mid(line_index, len);
      QString key;
      key = hand_val + "-" + up_cards[i];
      if (addHelpEntry(key, help_options)) {
         QString str;
         str.sprintf("Dulpicate help entry for %s\n",
                    (const char *)key);
         error_text += str;
      }
      line_index += len + 1;
   }
   return (error_text);
}

//*****************************************************************************

QString BlackJack::
readVariableLine(QString line) {
   QString error_text, var_name, var_val;
   QRegExp token("[0-9._a-zA-Z]+");
   int line_index = 0, len;

   token.match(line, line_index, &len);
   var_name = line.mid(line_index, len);
   line_index += len + 1;
   token.match(line, line_index, &len);
   var_val = line.mid(line_index, len);

   if (var_name == "HELP") {
      if (var_val == "NONE")
         _help = NO_HELP;
      else if (var_val == "WARNING")
         _help = WARNING;
      else if (var_val == "PICKEY")
         _help = PICKEY;
      else if (var_val == "FULL")
         _help = FULL_HELP;
   } else if (var_name == "DECKS") {
      _decks = atoi(var_val);
   } else if (var_name == "SPLITS") {
      _max_splits = atoi(var_val);
   } else if (var_name == "SHUFFLE") {
      _shuffle_threshold = atoi(var_val);
   } else if (var_name == "MIN_BET") {
      _min_bet = atof(var_val);
   } else if (var_name == "MAX_BET") {
      _max_bet = atof(var_val);
   } else if (var_name == "BET_INC") {
      _bet_inc = atof(var_val);
   } else if (var_name == "BANK_ROLL") {
      _bank_roll = atof(var_val);
   } else {
      error_text.sprintf("Invalid variable line: %s\n", (const char *)line);
   }

   return (error_text);
}

//*****************************************************************************

int BlackJack::
addHelpEntry(QString key, QString value) {
   int result = 0;

   if (_help_dict.find(key))
      result = -1;
   else 
      _help_dict.insert(key, new QString(value)); 

   return (result);
}

//*****************************************************************************

QString BlackJack::
createHelpFile(QString name) {
   QString error_text;

   ofstream out_file(name);

   if (!out_file) {
      error_text.sprintf("Could not create file %s", (const char *)name);
   } else {
      out_file << 
"#\n"
"# This file was automatically generated\n"
"# HELP may be one of NONE, WARNING, PICKEY, or FULL\n"
"# WARNING - will pop up a window if the move is not in the list below\n"
"# PICKEY - WARNING, plus will pop up a window if there is a better move\n"
"# HELP may be one of NONE, WARNING, PICKEY, or FULL\n"
"HELP    NONE\n"
"# DECKS may be 1-6\n"
"DECKS   4\n"
"# SPLITS may be 1-3\n"
"SPLITS  3\n"
"# SHUFFLE may be 1-50, when shoe has less than this many cards shuffle it\n"
"SHUFFLE 20\n"
"MIN_BET 1.00\n"
"MAX_BET 5.00\n"
"BET_INC 0.50\n"
"BANK_ROLL 200.00\n"
"#\n"
"#\n"
"#\n"
"# Upper case letters are moves in which the player is more likely to win\n"
"# Lower case letters are moves in which the dealer is more likely to win\n"
"#\n"
"# This information was taken from the FAQ for rec.gambling.blackjack\n"
"#\n"
"# Dealers \"up card\"\n"
"#\n"
"#   2   3   4   5   6   7   8   9   X   A\n"
"#\n" 
"# Splitable hands\n"
"#\n"
"XX  S   S   S   S   S   S   S   S   S   S\n"
"99  PS  PS  PS  PS  PS  S   PS  ps  s   s\n"
"88  Ps  Ps  Ps  Ps  Ps  Ph  ph  ph  ph  ph\n"
"77  ps  ps  Ps  Ps  Ps  ph  h   h   h   h\n"
"66  ph  ph  ps  Ps  Ps  h   h   h   h   h\n"
"55  DH  DH  DH  DH  DH  DH  DH  DH  H   H\n"
"44  h   H   H   PH  PH  H   h   h   h   h\n"
"33  ph  ph  Ph  Ph  Ph  ph  h   h   h   h\n"
"22  ph  ph  Ph  Ph  Ph  ph  h   h   h   h\n"
"AA  PH  PH  PH  PH  PDH PH  PH  Ph  Ph  Ph\n"
"#\n"
"# Soft hands\n"
"#\n"
"#AX No need for help here this is blackjack\n"
"A9  S   S   S   S   S   S   S   S   S   S\n"
"A8  S   S   S   S   S   S   S   S   S   S\n"
"A7  S   DS  DS  DS  DS  S   S   h   h   h\n"
"A6  H   DH  DH  DH  DH  H   h   h   h   h\n"
"A5  h   H   DH  DH  DH  h   h   h   h   h\n"
"A4  h   H   DH  DH  DH  H   h   h   h   h\n"
"A3  H   H   H   DH  DH  H   H   h   h   h\n"
"A2  H   H   H   DH  DH  H   H   h   h   h\n"
"#\n"
"# Hard hands\n"
"#\n"
"#21  S   S   S   S   S   S   S   S   S   S\n"
"20  S   S   S   S   S   S   S   S   S   S\n"
"19  S   S   S   S   S   S   S   S   S   S\n"
"18  S   S   S   S   S   S   S   s   s   s\n"
"17  s   s   s   s   S   s   s   s   s   s\n"
"16  s   s   s   s   s   h   h   h   h   h\n"
"15  s   s   s   s   s   h   h   h   h   h\n"
"14  s   s   s   s   s   h   h   h   h   h\n"
"13  s   s   s   s   s   h   h   h   h   h\n"
"12  h   h   s   s   s   h   h   h   h   h\n"
"11  DH  DH  DH  DH  DH  DH  DH  DH  DH  H\n"
"10  DH  DH  DH  DH  DH  DH  DH  DH  H   H\n"
"9   H   DH  DH  DH  DH  H   H   h   h   h\n"
"8   h   H   H   H   H   H   h   h   h   h\n"
"7   h   h   h   H   H   h   h   h   h   h\n"
"6   h   h   h   h   h   h   h   h   h   h\n"
"5   h   h   h   h   H   h   h   h   h   h\n"
"#4 This hand can only be A3 or 22 above\n"
"#3 This hand can only be A2 above\n"
"#2 This hand can only be AA above\n";
      out_file.close();
   }
   
   return (error_text);
}

//*****************************************************************************

void BlackJack::
dealCards(void) {
   Hand *hand_p, *dealer_hand_p;

   hand_p = new Hand;
   _table_p->addPlayerHand(hand_p);
   _dealer_must_play = 0;
   _current_player_hand = 1;
   dealer_hand_p = _table_p->getDealerHand();
  
   _table_p->addCard(hand_p, _shoe_p->getCard(Card::FACE_UP)); 
   _table_p->addCard(dealer_hand_p, _shoe_p->getCard(Card::FACE_DOWN)); 
   _table_p->addCard(hand_p, _shoe_p->getCard(Card::FACE_UP)); 
   _table_p->addCard(dealer_hand_p, _shoe_p->getCard(Card::FACE_UP)); 
}

//*****************************************************************************

void BlackJack::
playDealerHand(void) {
   int out_of_cards = 0;
   Hand *dealer_hand_p = _table_p->getDealerHand();
   dealer_hand_p->setAllCardsUp();
   _table_p->drawHand(dealer_hand_p);  

   if (_dealer_must_play) {
      while (dealer_hand_p->value() < 17 && ! out_of_cards) {
         if (_shoe_p->cardsLeft() >= 1) {
            _table_p->addCard(dealer_hand_p, _shoe_p->getCard(Card::FACE_UP)); 
         } else {
            out_of_cards = 1;
         }
      }
   }

   if (out_of_cards)
      outOfCards();
   else
      endOfHands();
}

//*****************************************************************************

void BlackJack::
postDealChecks(void) {
   Hand *hand_p = _table_p->getPlayerHand(_current_player_hand);

   if (_table_p->getDealerHand()->value() == 21) {
      _table_p->statusText(hand_p, "");
      playDealerHand();
   } else if (hand_p->value() == 21 ) {
      nextHand(); 
   } else {
      _input_p->makeValid(UserInput::Hit|UserInput::Stand|UserInput::Double);
      checkForSplit();
   }
}

//*****************************************************************************

void BlackJack::
nextHand(void) {
   int out_of_cards = 0;
   Hand *hand_p = _table_p->getPlayerHand(_current_player_hand);
   _table_p->statusText(hand_p, ""); 	// clear hand status

   if (_current_player_hand == _table_p->numHands())
      playDealerHand();
   else {
      _current_player_hand++;
      hand_p = _table_p->getPlayerHand(_current_player_hand);
      _table_p->statusText(hand_p, "Active");
      
      if (_shoe_p->cardsLeft() <= 0) {
         outOfCards();
      } else {
         _table_p->addCard(hand_p, _shoe_p->getCard(Card::FACE_UP));
         if (hand_p->value() == 21) {
            nextHand();
         } else {
            _input_p->makeValid(UserInput::Hit|UserInput::Stand|
                                UserInput::Double);
            checkForSplit();
            checkPreHelp();
         }
      }
   }

   if (! out_of_cards)
      _table_p->updatePlayerHands();
}
      
//*****************************************************************************

void BlackJack::
endOfHands(void) {
   int hand_num;
   Hand *dealer_hand_p = _table_p->getDealerHand(); 
   QString str;

   for (hand_num = 1; hand_num <= _table_p->numHands(); hand_num++) {
      Hand *hand_p = _table_p->getPlayerHand(hand_num);
      if (hand_p->value() > 21) {
         str.sprintf("You lose $%4.2f", hand_p->getBet());
         _status_p->setResults(-1.0, hand_p->getBet());         
      } else if (dealer_hand_p->value() == hand_p->value()) {
         str.sprintf("Push %d's", hand_p->value());
         _status_p->setResults(0.0, hand_p->getBet());         
      } else if (hand_p->value() == 21 && hand_p->numCards() == 2 &&
                 _table_p->numHands() == 1) {
         str.sprintf("Blackjack, win $%4.2f", hand_p->getBet() * 1.5);
         _status_p->setResults(1.5, hand_p->getBet());
      } else if (dealer_hand_p->value() > 21) {
         str.sprintf("Dealer busted, win $%4.2f", hand_p->getBet());
         _status_p->setResults(1.0, hand_p->getBet());         
      } else if (dealer_hand_p->value() > hand_p->value()) {
         str.sprintf("Dealer has %d, lose $%4.2f", dealer_hand_p->value(),
                                                   hand_p->getBet());
         _status_p->setResults(-1.0, hand_p->getBet());
      } else if (dealer_hand_p->value() < hand_p->value()) {
         str.sprintf("Dealer has %d, win $%4.2f", dealer_hand_p->value(), 
                                                   hand_p->getBet());
         _status_p->setResults(1.0, hand_p->getBet()); 
      } else {
         str = "HELP";
      }
      _table_p->statusText(hand_p, str);
   }

   _input_p->makeInvalid(UserInput::Hit|UserInput::Stand|
                         UserInput::Double|UserInput::Split);
   _input_p->makeValid(UserInput::Bet);

   _table_p->updatePlayerHands();
   _hand_in_progress = 0;
   checkPreHelp();
   checkShuffle();
}
      
//*****************************************************************************

void BlackJack::
checkForSplit(void) {
   Hand *hand_p = _table_p->getPlayerHand(_current_player_hand);

   if (hand_p->isSplitable() && _table_p->numHands() <= _max_splits)
      _input_p->makeValid(UserInput::Split);
}

//*****************************************************************************

void BlackJack::
checkShuffle(void) {
   if (_shuffle) {
      QMessageBox mb;
      mb.setText("Shuffling, number of decks changed.");
      mb.setButtonText(0, "OK");
      mb.show();
      _shuffle = 0; 
      _table_p->clear();
      delete _shoe_p;
      _shoe_p = new Shoe(_decks);
      _shoe_p->shuffle(); 
   }
   if (_shoe_p->cardsLeft() < _shuffle_threshold) {
      QString str;
      QMessageBox mb;
      str.sprintf("Shuffling, only %d cards left.", _shoe_p->cardsLeft());
      mb.setText(str);
      mb.setButtonText(0, "OK");
      mb.show();
      _table_p->clear();
      _shoe_p->shuffle(); 
   }
}

//*****************************************************************************

QString BlackJack::
getHelpIndex(void) {
   QString index, dealer_index;;
   Hand *hand_p = _table_p->getPlayerHand(_current_player_hand);
   index = hand_p->getHelpIndex();      
   index += "-";
   Card::RankType rank = _table_p->getDealerHand()->getCard(1)->getRank();
   if (rank == Card::TEN || rank == Card::JACK || rank == Card::QUEEN ||
       rank == Card::KING)
      dealer_index = "X";
   else if (rank == Card::ACE)
      dealer_index = "A";
   else
      dealer_index.sprintf("%d", rank); 
   index += dealer_index;

   return (index);
}

//*****************************************************************************

QString BlackJack::
getHelpText(QString help_letters) {
   char c;
   QString letter, text;
   Hand *hand_p = _table_p->getPlayerHand(_current_player_hand);

   if (hand_p->numCards() > 2)	// if more than 2 cards, can't split or double
      help_letters.replace(QRegExp("[DPdp]+"), "");

   for (int i = 0; i < help_letters.length(); i++) {
      if (text.length() > 0)
         text += ", ";
      letter = help_letters.mid(i, 1);
      c = *((const char *)letter); 
      switch (c) {
         case 'S':
            text += "STAND";
            break;
         case 's':
            text += "stand";
            break;
         case 'H':
            text += "HIT";
            break;
         case 'h':
            text += "hit";
            break;
         case 'D':
            text += "DOUBLE";
            break;
         case 'd':
            text += "double";
            break;
         case 'P':
            text += "SPLIT";
            break;
         case 'p':
            text += "split";
            break;
      }
   }
   return(text); 
}

//*****************************************************************************

void BlackJack::
checkPreHelp(void) {
   if (_help == FULL_HELP) {
      if (_hand_in_progress) {
         QString index = getHelpIndex(); 
         if (_help_dict.find(index))
            _status_p->setHelp(getHelpText(_help_dict[index]->copy()));
         else
            _status_p->setHelp("No Help");
      } else {
         _status_p->setHelp("Place a bet");
      }
   }
}

//*****************************************************************************
//
//  Return a 1 to continue with play
//

int BlackJack::
checkPostHelp(char action) {
   int button, result = 1; 

   if (_help == WARNING || _help == PICKEY) {
      QString index = getHelpIndex(); 
      if (_help_dict.find(index)) {
         QString help_letters = _help_dict[index]->copy();
         if (help_letters.contains(action, FALSE) == FALSE) {
            QString text, help_text;
            help_text = getHelpText(help_letters); 
            if (help_text.contains(',')) 	// if more than one in list
               text = "WARNING\nThe correct plays are:\n";
            else
               text = "WARNING\nThe correct play is:\n";
            text += help_text;
            text += "\nContinue?";
            button = QMessageBox::warning(this, "Oops", text, "Yes", "No", 
					  0, 1, 1);
            if (button == 1)
               result = 0;
// else, if this choice is not uppercase and there is an uppercase choice
//       and help level is pickey...
         } else if (help_letters.contains(action, TRUE) == FALSE &&
                    help_letters.contains(QRegExp("[A-Z]")) && 
                    _help == PICKEY) {
            QString text, help_text;
            help_letters.replace(QRegExp("[a-z]+"), "");
            help_text = getHelpText(help_letters);
            if (help_text.contains(','))	// if more than one in list
               text = "While this is a resonable play,\n"
                      "plays with better odds are:\n";
            else
               text = "While this is a resonable play,\n"
                      "a play with better odds is:\n";
            text += help_text;
            text += "\nContinue?";
            button = QMessageBox::warning(this, "Oops", text, "Yes", "No", 
					  0, 1, 1);
            if (button == 1)
               result = 0;
         }
      }
   }

   return (result);
}

//*****************************************************************************

void BlackJack::
betOptions() {
   BetOption bo(_min_bet, _max_bet, _bet_inc);
   bo.show();

   if (bo.result() == QDialog::Accepted) {
      _min_bet = bo.min();
      _max_bet = bo.max();
      _bet_inc = bo.inc();
      _input_p->setBetLimits(_min_bet, _max_bet, _bet_inc);
   }
}

//*****************************************************************************

void BlackJack::
tableOptions() {
   TableOption to(_decks, _max_splits, _shuffle_threshold);
   to.show();
   if (to.decks() != _decks) {	// if number of decks changed
      _decks = to.decks();	// set new value
      if (_hand_in_progress) 	// if in the middle of play
         _shuffle = 1; 		// set flag to make new shoe after hand
      else {			// else make new shoe now
         _table_p->clear();
         delete _shoe_p;
         _shoe_p = new Shoe(_decks);
         _shoe_p->shuffle();
      }
   }
   _max_splits = to.splits();
   _shuffle_threshold = to.shufflePoint();
}

//*****************************************************************************

void BlackJack::
saveOptions() {
   char *cp;
   QString rc_file_name;
   QString temp_file_name;

   if (cp = getenv("HOME")) {
      QString home(cp);
      rc_file_name = home + (QString)"/" + DEFAULT_HELP_FILE;
      temp_file_name = home + (QString)"/" + (QString)DEFAULT_HELP_FILE ".tmp";
   }

   ifstream in_file(rc_file_name);
   ofstream out_file(temp_file_name);

   if (in_file && out_file) {
      while (in_file) {
         char buffer[128];
         QString original_line, line;
         QRegExp comment("#.*");

         in_file.getline(buffer, 128);
         line = buffer;
         original_line = line.copy();
         line.replace(comment, "");
         line = line.simplifyWhiteSpace();
         if (line.length()) {
            QRegExp between("[a-zA-Z0-9]\\s+[a-zA-Z0-9]");
            int fields = line.contains(between) + 1;
            if (fields == 2) {   // argument line
               out_file << makeOptionLine(original_line); 
            } else {
               out_file << original_line;
            }
         } else {
            out_file << original_line;
         }
         if (in_file)
            out_file << "\n";
      }
      in_file.close();
      out_file.close();
      rename(temp_file_name, rc_file_name);
   }

   if (in_file)
      in_file.close();

   if (out_file)
      out_file.close();
}

//*****************************************************************************

QString BlackJack::
makeOptionLine(QString line) {
   QString str;
   if (line.contains(QRegExp("^\\s*HELP"))) {
      QString help_str("NONE");
      switch (_help) {
         case FULL_HELP:
            help_str = "FULL";
            break;
         case WARNING:
            help_str = "WARNING";
            break;
         case PICKEY:
            help_str = "PICKEY";
            break;
         case NO_HELP:
            help_str = "NONE";
            break;
      }
      str.sprintf("HELP %s", (const char *)help_str);
      line.replace(QRegExp("^\\s*HELP\\s+[A-Z]+"), str);
   } else if (line.contains(QRegExp("^\\s*DECKS"))) {
      str.sprintf("DECKS %d", _decks);
      line.replace(QRegExp("^\\s*DECKS\\s+[0-9]+"), str);
   } else if (line.contains(QRegExp("^\\s*SPLITS"))) {
      str.sprintf("SPLITS %d", _max_splits);
      line.replace(QRegExp("^\\s*SPLITS\\s+[0-9]+"), str);
   } else if (line.contains(QRegExp("^\\s*SHUFFLE"))) {
      str.sprintf("SHUFFLE %d", _shuffle_threshold);
      line.replace(QRegExp("^\\s*SHUFFLE\\s+[0-9]+"), str);
   } else if (line.contains(QRegExp("^\\s*MIN_BET"))) {
      str.sprintf("MIN_BET %6.2f", _min_bet);
      line.replace(QRegExp("^\\s*MIN_BET\\s+[0-9.]+"), str);
   } else if (line.contains(QRegExp("^\\s*MAX_BET"))) {
      str.sprintf("MAX_BET %6.2f", _max_bet);
      line.replace(QRegExp("^\\s*MAX_BET\\s+[0-9.]+"), str);
   } else if (line.contains(QRegExp("^\\s*BET_INC"))) {
      str.sprintf("BET_INC %6.2f", _bet_inc);
      line.replace(QRegExp("^\\s*BET_INC\\s+[0-9.]+"), str);
   }
 
   return (line);
}

//*****************************************************************************

void BlackJack::
outOfCards(void) {
   QMessageBox mb;
   mb.setText("The shoe is out of cards.\n"
              "The shuffle point may be set too low.\n"
              "The current hand will be aborted.\n"
              "shuffling the shoe.");
   mb.setButtonText(0, "OK");
   mb.show();
   _hand_in_progress = 0;
   _input_p->makeInvalid(UserInput::Hit|UserInput::Stand|
                         UserInput::Double|UserInput::Split);
   _input_p->makeValid(UserInput::Bet);
   _table_p->clear();
   _shoe_p->shuffle();
}

//*****************************************************************************

void BlackJack::
about() {
    QMessageBox::about( this, "About Blackjack",
			"blackjack version 1.2\n"
			"is GPL software written by:\n"
			"\n"
			"Tom Daley\n"
			"1309 W. Lorraine Ave.\n"
			"Woodland Park, CO  80863-2329\n"
			"\n"
			"daley@codenet.net (Tom Daley)\n"
			"http://users.codenet.net/daley");
}

//*****************************************************************************

void BlackJack::
aboutQt() {
    QMessageBox::aboutQt( this, "About Qt");
}

//*****************************************************************************
