/*
 *     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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <stdio.h>
#include <stdlib.h>

// QT includes
#include <tqlabel.h>
#include <tqtimer.h>
#include <tqpainter.h>
#include <tqtooltip.h>
#include <tqlayout.h>

// KDE includes
#include <tdemessagebox.h>
#include <tdeapplication.h>
#include <tdeglobal.h>
#include <kstandarddirs.h>
#include <kdebug.h>
#include <tdeconfig.h>
#include <tdelocale.h>
#include <kaudioplayer.h>
#include <kcarddialog.h>

// own includes
#include "betbox.h"
#include "kpaint.h"
#include "optionsdlg.h"
#include "newgamedlg.h"
#include "player.h"
#include "playerbox.h"
#include "version.h"
#include "kpoker.h"
#include "defines.h"


// ================================================================
//                         Class PokerGame


PokerGame::PokerGame(KRandomSequence *random)
  : m_deck(random)
{
  // We don't need to save if we just started.
  m_isDirty = false;

  // Some defaults.
  m_type = SinglePlayer;
}


PokerGame::~PokerGame()
{
  m_activePlayers.clear();
  m_removedPlayers.clear();
}


void
PokerGame::dealCards(PokerPlayer *player, bool skip[])
{
  CardValue  card;

  for (int i = 0; i < PokerHandSize; i++) {
    if (skip[i])
      continue;

    card = m_deck.getTopCard();

    player->takeCard(i, card);
  }
}


void
PokerGame::activatePlayer(PokerPlayer *player)
{
  if (!m_activePlayers.contains(player)) {
    m_activePlayers.append(player);
    m_removedPlayers.remove(player);
  }
}


void
PokerGame::inactivatePlayer(PokerPlayer *player)
{
  if (m_activePlayers.contains(player)) {
    m_activePlayers.remove(player);
    m_removedPlayers.append(player);
  }
}


void
PokerGame::newGame(PokerGameType type, 
		   int numPlayers, PokerPlayer *players,
		   int minBet, int maxBet)
{
  m_type = type;

  // Store the players.  These are never changed in the current implementation.
  m_numPlayers = numPlayers;
  m_players    = players;

  setBettingLimits(minBet, maxBet);

  setState(StateStartRound);

  // Initiate player arrays. Make all players active from the start.
  m_activePlayers.clear();
  m_removedPlayers.clear();
  for (unsigned int i = 0; i < m_numPlayers; i++)
    m_activePlayers.append(&m_players[i]);

  // Inform players how much they may bet.
  for (unsigned int i = 0; i < m_activePlayers.count(); i++)
    m_activePlayers.at(i)->setBetDefaults(m_minBet, m_maxBet);
}


void
PokerGame::newRound()
{
  // Reset the pot to zero.
  m_pot = 0;

  // Collect the cards and shuffle them.
  m_deck.reset();
  m_deck.shuffle();

  // Clear the list of active and removed players.
  m_activePlayers.clear();
  m_removedPlayers.clear();
}


// ================================================================
//                       Global variables


CardImages *cardImages;


// ================================================================
//                           Class kpok


kpok::kpok(TQWidget *parent, const char *name)
  : TQWidget(parent, name),
    m_game(&m_random)
{
  TQString version;

  m_random.setSeed(0);
  
  // This class owns the players.  Create them here.
  // Currently we always allocate two - one human and one computer.
  m_numPlayers = 2;		// FIXME: Hard coded!!
  m_players    = new PokerPlayer[m_numPlayers];

  // Initialize the first one to human...
  m_players[0].setHuman();
  m_players[0].setName(i18n("You"));

  // ...and the rest to computer players.
  for (int unsigned i = 1; i < m_numPlayers; i++)
    m_players[i].setName(TQString("Computer %1").arg(i-1));

  lastHandText = "";

  version = kapp->caption() + " " + KPOKER_VERSION;
  setCaption( version );

  mOptions   = 0;

  playerBox  = 0;
  adjust     = false;

  initWindow();		// FIXME: Change this name!!!
  initSound();

  if (!readEntriesAndInitPoker())
    exit(0);
}


kpok::~kpok()
{
  if (mOptions != 0) {
    if (m_numPlayers > 1)
      m_game.setBettingLimits(mOptions->getMinBet(), mOptions->getMaxBet());

    drawDelay = mOptions->getDrawDelay();
    //	cashPerRound = mOptions->getCashPerRound(); // NOT(!) configurable
  }

  TDEConfig* conf = kapp->config();
  conf->setGroup("General");
  conf->writeEntry("MinBet", m_game.getMinBet());
  conf->writeEntry("MaxBet", m_game.getMaxBet());
  // conf->writeEntry("CashPerRound", cashPerRound);
  conf->writeEntry("DrawDelay", drawDelay);


  delete[] m_players;

  for (unsigned int i = 0; i < m_numPlayers; i++)
    delete playerBox[i];
  delete[] playerBox;

  delete mOptions;
}


// Init the main window and load the configuration and card images.
//
// Only called by kpok::kpok() once.

void kpok::initWindow()
{
  m_blinking    = true;
  m_blinkStat   = 0;
  m_blinkingBox = 0;

  // General font stuff.  Define myFixedFont and wonFont.
  TQFont myFixedFont;
  myFixedFont.setPointSize(12);
  TQFont wonFont;
  wonFont.setPointSize(14);
  wonFont.setBold(true);

  topLayout = new TQVBoxLayout(this, BORDER);
  TQVBoxLayout* topInputLayout = new TQVBoxLayout;
  topLayout->addLayout(topInputLayout);

  TQHBoxLayout* betLayout = new TQHBoxLayout;
  inputLayout = new TQHBoxLayout;
  inputLayout->addLayout(betLayout);
  topInputLayout->addLayout(inputLayout);

  // The draw button
  drawButton = new TQPushButton(this);
  drawButton->setText(i18n("&Deal"));
  connect(drawButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(drawClick()));
  inputLayout->addWidget(drawButton);
  inputLayout->addStretch(1);

  // The waving text
  TQFont  waveFont;
  waveFont.setPointSize(16);
  waveFont.setBold(true);
  TQFontMetrics  tmp(waveFont);

  // The widget where the winner is announced.
  mWonWidget = new TQWidget(this);
  inputLayout->addWidget(mWonWidget, 2);
  mWonWidget->setMinimumHeight(50); //FIXME hardcoded value for the wave
  mWonWidget->setMinimumWidth(tmp.width(i18n("You won %1").arg(TDEGlobal::locale()->formatMoney(100))) + 20); // workaround for width problem in wave
  TQHBoxLayout* wonLayout = new TQHBoxLayout(mWonWidget);
  wonLayout->setAutoAdd(true);

  wonLabel = new TQLabel(mWonWidget);
  wonLabel->setFont(wonFont);
  wonLabel->setAlignment(AlignCenter);
  wonLabel->hide();
  inputLayout->addStretch(1);

  // The pot view
  potLabel = new TQLabel(this);
  potLabel->setFont(myFixedFont);
  potLabel->setFrameStyle(TQFrame::WinPanel | TQFrame::Sunken);
  inputLayout->addWidget(potLabel, 0, AlignCenter);

  // Label widget in the lower left.
  clickToHold = new TQLabel(this);
  clickToHold->hide();

  // Timers
  blinkTimer = new TQTimer(this);
  connect( blinkTimer, TQ_SIGNAL(timeout()), TQ_SLOT(bTimerEvent()) );

  waveTimer = new TQTimer(this);
  connect( waveTimer, TQ_SIGNAL(timeout()), TQ_SLOT(waveTimerEvent()) );

  drawTimer = new TQTimer(this);
  connect (drawTimer, TQ_SIGNAL(timeout()), TQ_SLOT(drawCardsEvent()) );

  // and now the betUp/Down Buttons
  betBox = new BetBox(this, 0);
  betLayout->addWidget(betBox);
  connect(betBox, TQ_SIGNAL(betChanged(int)), this, TQ_SLOT(betChange(int)));
  connect(betBox, TQ_SIGNAL(betAdjusted()),   this, TQ_SLOT(adjustBet()));
  connect(betBox, TQ_SIGNAL(fold()),          this, TQ_SLOT(out()));

  // some tips 
  TQToolTip::add(drawButton, i18n("Continue the round"));
  TQToolTip::add(potLabel, i18n("The current pot"));

  // Load all cards into pixmaps first -> in the constructor.
  cardImages = new CardImages(this, 0);

  // The configuration
  TDEConfig* conf = kapp->config();
  conf->setGroup("General");

  // Load the card deck.
  if (conf->readBoolEntry("RandomDeck", true)) {
    cardImages->loadDeck(KCardDialog::getRandomDeck());
  } else {
    cardImages->loadDeck(conf->readPathEntry("DeckPath", KCardDialog::getDefaultDeck()));
  }
  if (conf->readBoolEntry("RandomCardDir", true)) {
    cardImages->loadCards(KCardDialog::getRandomCardDir());
  } else {
    cardImages->loadCards(conf->readPathEntry("CardPath",
				  KCardDialog::getDefaultCardDir()));
  }
}


// Start a new complete game (not a round).

void kpok::newGame()
{
  NewGameDlg* newGameDlg;

  // Get a "New Game" dialog.
  lastHandText = "";
  newGameDlg = new NewGameDlg(this);
  newGameDlg->hideReadingFromConfig();

  if (newGameDlg->exec()) {
    stopBlinking();
    stopDrawing();
    stopWave();

    // Delete the old values.
    for (unsigned int i = 0; i < m_numPlayers; i++)
      delete playerBox[i];
    delete[] playerBox;// necessary?

    int numPlayers = newGameDlg->getPlayers();

    // Most things will be done in initPoker.
    initPoker(numPlayers);

    // Set/show the name and money of all players.
    for (unsigned int i = 0; i < m_numPlayers; i++) {
      m_players[i].setName(newGameDlg->name(i));
      playerBox[i]->showName();
      m_players[i].setCash((newGameDlg->money() >= m_game.getMinBet())
			   ? newGameDlg->money() : START_MONEY);
      m_game.setDirty();
    }

    // Show the money for all players and the pot.
    paintCash();

    // Activate the Draw button.
    drawButton->setText(i18n("&Deal"));
    drawButton->setEnabled(true);
  }

  delete newGameDlg;
}


void kpok::newRound()
{
  bool  onePlayerGame         = false;

  m_game.newRound();

  playerBox[0]->setHeldEnabled(false);

  if (m_numPlayers == 1)
    onePlayerGame = true;

  readOptions(); // maybe some options have changed so check em

  if (m_players[0].getCash() < m_game.getMinBet())
    noMoney();
  else {
    for (unsigned int i = 0; i < m_numPlayers; i++) {
      if (m_players[i].getCash() >= m_game.getMinBet())
	m_game.activatePlayer(&m_players[i]);
      else
	removePlayerFromRound(&m_players[i]);
    }
  }

  if (m_game.getNumActivePlayers() == 1 && m_game.getType() != SinglePlayer)
    switchToOnePlayerRules();

  m_blinkingBox = 0;
  wonLabel->hide();
  stopBlinking();
  stopWave();

  for (int i = 0; i < m_game.getNumActivePlayers(); i++)
    m_game.getActivePlayer(i)->newRound();

  // We are beginning a new round so every card is available.
  drawAllDecks();
  playerBox[0]->showHelds(false);

  // Deal first cards of the round
  bool skip[PokerHandSize];
  for (int i = 0; i < PokerHandSize; i++)
    skip[i] = false;

  for (int i = 0; i < m_game.getNumActivePlayers(); i++)
    drawCards(m_game.getActivePlayer(i), skip);

  if (m_game.getNumActivePlayers() > 1) {
    findHumanPlayer()->changeBet(m_game.getMinBet());
    m_game.bet(m_game.getMinBet());
    betBox->show();
  }
  else {
    m_game.getActivePlayer(0)->changeBet(cashPerRound);
    betBox->hide();
  }

  paintCash();

  drawTimer->start(drawDelay, TRUE);
}


void kpok::bet()
{
  // The players will bet, now.  Player 1 (the human one ;-)) has
  // already bet using the betBox ALL players (except player 1 who has
  // already had) will get a chance to raise the value if nobody
  // raises further that player will not get another chance, else he
  // will.

  bool raised = false;
  int oldMustBet = 0;
  if (m_game.getState() == StateBet2)
    oldMustBet = currentMustBet;

  if (m_game.getState() == StateBet1 || m_game.getState() == StateBet2) {
    // The betting has just begun.  This can be either the first or
    // the second betting phase (state == StateBet1 or StateBet2).
    // FIXME: These state names must be changed!

    // Find out how much the other players must bet.
    currentMustBet = findHumanPlayer()->getCurrentBet(); 

    // First bet as usual.
    for (int i = 0; i < m_game.getNumActivePlayers(); i++) {
      PokerPlayer *player    = m_game.getActivePlayer(i);
      int          playerBet = 0;

      if (player->getHuman()) 
	continue;

      if (m_game.getState() == StateBet1) // first bet phase
	playerBet = player->bet(currentMustBet);
      else if (m_game.getState() == StateBet2) // 2nd bet phase
	playerBet = player->raise(currentMustBet);

      if (playerBet < currentMustBet) {
	removePlayerFromRound(player);
	// FIXME: This should be bet from the beginning!
	if (m_game.getState() == StateBet1)
	  m_game.bet(m_game.getMinBet());
	i--;
      }
      else {
	// The player is seeing or has raised.

	if (playerBet > currentMustBet)
	  raised = true;

	currentMustBet = playerBet;
	m_game.bet(currentMustBet - oldMustBet);
      }
    }
  }
  else if (m_game.getState() == StateRaise1
	   || m_game.getState() == StateRaise2) {

    // The bet has been raised.
    paintCash();
    for (int i = 0; i < m_game.getNumActivePlayers(); i++) {
      PokerPlayer *player  = m_game.getActivePlayer(i);

      // human player
      if (player->getCurrentBet() < currentMustBet && player->getHuman()) {
	removePlayerFromRound(player);
	i--;
      }
    }
  }

  paintCash();


  oldBet_raise = findHumanPlayer()->getCurrentBet(); // used by out() only
  if (m_game.getState() == StateBet1 || m_game.getState() == StateBet2) {
    if (raised) {
      if (m_game.getState() == StateBet1)
	m_game.setState(StateRaise1);
      else
	m_game.setState(StateRaise2);

      if (adjust)
	adjustBet();
      else
	out(); // not necessary
      betBox->setEnabled(true);
      betBox->beginRaise();
      if (adjust)
	emit statusBarMessage(i18n("Clicking on draw means you adjust your bet"));
      else
	emit statusBarMessage(i18n("Clicking on draw means you are out"));
    }
    else {
      if (m_game.getState() == StateBet1)
	m_game.setState(StateExchangeCards);
      else
	m_game.setState(StateSee);
    }
  }
  else if (m_game.getState() == StateRaise1 && !raised) {
    emit clearStatusBar();
    //	weWillAdjustLabel->hide();
    betBox->stopRaise();
    m_game.setState(StateExchangeCards);
  }
  else if (m_game.getState() == StateRaise2 && !raised) {
    emit clearStatusBar();
    //	weWillAdjustLabel->hide();
    betBox->stopRaise();
    m_game.setState(StateSee);
  }


  // Check if player 1 is out -> for players > 2
  // TODO: maybe if (!m_activePlayers.contains(humanPlayer))
  // {exchangeCards(); bet(); displayerWinner_computer();return;}


  // don't continue game if player 1 is alone
  // TODO: port to players > 2
  // this is ONLY working for players <= 2
  if (m_game.getNumInactivePlayers() >= 1 
      && m_game.getNumActivePlayers() == 1)
    displayWinner_Computer(m_game.getActivePlayer(0), true);
}


void kpok::out()
{
  // weWillAdjustLabel->hide();
  emit clearStatusBar();

  m_game.bet(oldBet_raise - findHumanPlayer()->getCurrentBet());
  findHumanPlayer()->changeBet(oldBet_raise 
			       - findHumanPlayer()->getCurrentBet());
  paintCash();

  // Go to next state immediately.  (Previously we told the user to
  // click the action button.)
  drawClick();
  //drawButton->setText(i18n("&End Round"));
}


void kpok::adjustBet()
{
  emit clearStatusBar();

  betChange(currentMustBet - findHumanPlayer()->getCurrentBet());
  paintCash();
}


// Initiate for a completely new game (not a round).
//
// This is called from newGame(), readEntriesAndInitPoker(), loadGame().
//

void kpok::initPoker(unsigned int numPlayers)
{
  m_numPlayers = numPlayers;

  // Tell the game about the players also.
  PokerGameType gametype = (numPlayers == 1) ? SinglePlayer : MultiPlayer;

  // Read some defaults.
  kapp->config()->setGroup("General");
  int  minBet = kapp->config()->readNumEntry("MinBet", MIN_BET);
  int  maxBet = kapp->config()->readNumEntry("MaxBet", MAX_BET);

  // Start a new poker game using the data found out above.
  m_game.newGame(gametype, m_numPlayers, m_players, minBet, maxBet);

  // Not (yet) configurable
  cashPerRound = CASH_PER_ROUND;
  drawDelay = kapp->config()->readNumEntry("DrawDelay", DRAWDELAY);

  m_blinkingBox = 0;
  currentMustBet = m_game.getMinBet();

  // --- Graphics ---

  // Make all labels / boxes / cardwidgets for every player.
  playerBox = new PlayerBox *[numPlayers];

  for (int unsigned i = 0; i < numPlayers; i++) {
    playerBox[i] = new PlayerBox(i == 0, this);
    playerBox[i]->setPlayer(&m_players[i]);
    if (i == 0)
      topLayout->insertWidget(0, playerBox[i]);
    else
      topLayout->addWidget(playerBox[i]);
    playerBox[i]->showName();

    // If it has been deleted and created again it hasn't be shown
    // correctly - hiding and re-showing solves the problem.
    playerBox[i]->hide();
    playerBox[i]->show();
  }

  // Connects for player 1
  //
  // FIXME: Make CardWidget::toggleHeld() work.
  playerBox[0]->activateToggleHeld(); 
  connect(playerBox[0], TQ_SIGNAL(toggleHeld()), this, TQ_SLOT(toggleHeld()));

  // hide some things
  playerBox[0]->showHelds(false);
  wonLabel->hide();
  emit showClickToHold(false);

  // Different meaning of the status for single and multi player games.
  if (m_game.getNumActivePlayers() > 1) {
    setHand(i18n("Nobody"), false);
    betBox->show();
    betBox->setEnabled(false);
    potLabel->show();
  }
  else {
    setHand(i18n("Nothing"));
    betBox->hide();
    potLabel->hide();
    playerBox[0]->singlePlayerGame(cashPerRound);
  }

  // Some final inits.
  drawStat = 0;
  waveActive = 0;
  fCount = 0;

  // Finally clear the pot and show the decks/cash - in one word: begin :-)
  m_game.clearPot();
  drawAllDecks();
  for (unsigned int i = 0; i < m_numPlayers; i++) {
    playerBox[i]->repaintCard();
  }
  paintCash();
  playerBox[0]->setHeldEnabled(false);
}


void kpok::paintCash()
{
  for (unsigned int i = 0; i < m_numPlayers; i++) {
    playerBox[i]->showCash();
  }
  potLabel->setText(i18n("Pot: %1").arg(TDEGlobal::locale()->formatMoney(m_game.getPot())));
}


void kpok::updateLHLabel()
{
  if (!lastHandText.isEmpty())
    setHand(lastHandText);
  else if (m_game.getNumActivePlayers() > 1)
    setHand(i18n("Nobody"), false);
  else
    setHand(i18n("Nothing"));
}


void kpok::setHand(const TQString& newHand, bool lastHand)
{
  emit changeLastHand(newHand, lastHand);

  lastHandText = newHand;
}


void kpok::toggleHeld()
{
  if (m_game.getState() == StateBet1 || m_game.getState() == StateRaise1)
    playSound("hold.wav");
}


void kpok::drawClick()
{
  if (!drawButton->isEnabled())
    return;

  // If this is the start of a new round, then deal new cards.
  if (m_game.getState() == StateStartRound) {
    drawButton->setEnabled(false);
    betBox->setEnabled(false);
    newRound();
  }
  else if (m_game.getState() == StateBet1) { // bet
    emit showClickToHold(false);

    bet();
    if (m_game.getState() == StateExchangeCards) {// should be set in bet()
      drawClick();
    }
  }
  else if (m_game.getState() == StateRaise1) { // continue bet
    bet();
    if (m_game.getState() == StateExchangeCards) {// should be set in bet()
      drawClick();
    }
  }
  else if (m_game.getState() == StateExchangeCards) { // exchange cards
    drawButton->setEnabled(false);
    playerBox[0]->setHeldEnabled(false);
    betBox->setEnabled(false);
    bool skip[PokerHandSize];
    for (int i = 0; i < PokerHandSize; i++)
      skip[i] = false;

    for (int i = 0; i < m_game.getNumActivePlayers(); i++) {
      if (!m_game.getActivePlayer(i)->getHuman())
	m_game.getActivePlayer(i)->exchangeCards(skip);
      else {
	for (int i = 0; i < PokerHandSize; i++) {
	  skip[i] = playerBox[0]->getHeld(i);
	  if (!skip[i])
	    playerBox[0]->paintDeck(i);
	}
      }
      drawCards(m_game.getActivePlayer(i), skip);
    }

    if (playerBox[0]->getHeld(0))
      drawTimer->start(0, TRUE);
    else
      drawTimer->start(drawDelay, TRUE);
  }
  else if (m_game.getState() == StateBet2) { // raise
    setBetButtonEnabled(false);
    bet();

    if (m_game.getState() == StateSee)//should be set in bet()->if no one has raised
      drawClick();
  }
  else if (m_game.getState() == StateRaise2) {
    bet();
    if (m_game.getState() == StateSee)
      drawClick();
  }
  else if (m_game.getState() == StateSee)
    winner();
}


void kpok::drawCards(PokerPlayer* p, bool skip[])
{
  m_game.dealCards(p, skip);
}


void kpok::displayWinner_Computer(PokerPlayer* winner, bool othersPassed)
{
  // Determine the box that contains the winner.
  for (unsigned int i = 0; i < m_numPlayers; i++) {
    if (&m_players[i] == winner)
      m_blinkingBox = i;
  }

  // Give the pot to the winner.
  winner->setCash(winner->getCash() + m_game.getPot());
  m_game.setDirty();

  // Generate a string with winner info and show it.
  TQString label;
  if (winner->getHuman())
    label = i18n("You won %1").arg(TDEGlobal::locale()->formatMoney(m_game.getPot()));
  else
    label = i18n("%1 won %2").arg(winner->getName()).arg(TDEGlobal::locale()->formatMoney(m_game.getPot()));
  wonLabel->setText(label);

  // Start the waving motion of the text.
  TQFont waveFont;
  waveFont.setBold(true);
  waveFont.setPointSize(16);
  TQFontMetrics tmp(waveFont);
  mWonWidget->setMinimumWidth(tmp.width(label) + 20);

  // Play a suitable sound.
  if (winner->getHuman()) {
    playSound("win.wav");
    wonLabel->hide();
    startWave();
  }
  else {
    playSound("lose.wav");
    wonLabel->show();
  }

  m_game.clearPot();
  m_game.setState(StateStartRound);
  drawButton->setEnabled(true);
  setHand(winner->getName(), false);
  paintCash();

  // Only start blinking if player 1 is still in.
  if (m_game.isActivePlayer(&m_players[0]) && !othersPassed)
    startBlinking();

  drawButton->setText(i18n("&Deal New Round"));
}


void kpok::showComputerCards()
{
  // Don't show cards of 'out' players.
  for (int i = 0; i < m_game.getNumActivePlayers(); i++) {
    if (!m_game.getActivePlayer(i)->getHuman()){
      playerBox[i]->paintCard(drawStat); //TODO change: 

      if (i == 1) //TODO : CHANGE!
	playSound("cardflip.wav");//perhaps in playerbox or even in  cardwidget
    }
  }

  if (drawStat == 4) {   // just did last card
    drawButton->setEnabled(true);
    drawStat = 0;
  } else { // only inc drawStat if not done with displaying
    drawStat++;
    showComputerCards();
  }
}


void kpok::setBetButtonEnabled(bool enabled)
{
  betBox->setEnabled(enabled);
}


void kpok::drawCardsEvent()
{
  if (!playerBox[0]->getHeld(drawStat)) {
    playerBox[0]->paintCard(drawStat);

    playSound("cardflip.wav");//maybe in playerbox or even in cardwidget
  }

  if (drawStat == 4) {   // just did last card
    drawButton->setEnabled(true);
    betBox->setEnabled(true);
    drawStat = 0;

    if (m_game.getState() == StateExchangeCards) {
      if (m_game.getType() == SinglePlayer)
	result();
      else {
	// Now give players the chance to raise.
	drawButton->setText(i18n("&See!"));
	m_game.setState(StateBet2);
      }
    } else if (m_game.getState() == StateStartRound) {
      playerBox[0]->setHeldEnabled(true);

      emit showClickToHold(true);
      //clickToHold->show();
      //TODO:
      m_game.setState(StateBet1);
      drawButton->setText(i18n("&Draw New Cards"));
    }

  } else { // only inc drawStat if not done with displaying
    drawStat++;
    // look at next card and if it is held instantly call drawCardEvent again
    if (playerBox[0]->getHeld(drawStat))
      drawTimer->start(0,TRUE);
    else
      drawTimer->start(drawDelay,TRUE);
  }
}


// Called to display the result in a single player game.

void kpok::result()
{
  int testResult = m_game.getActivePlayer(0)->testHand();
  switch (testResult) {
  case Pair:
    if (m_game.getActivePlayer(0)->getHand().get_firstRank() < JACK) {
      displayWin(i18n("Nothing"), 0); 
      break;
    }

    displayWin(i18n("One Pair"), 5);  
    break;

  case TwoPairs:
    displayWin(i18n("Two Pairs"), 10); 
    break;

  case ThreeOfAKind:
    displayWin(i18n("3 of a Kind"), 15); 
    break;

  case Straight:
    displayWin(i18n("Straight"), 20); 
    break;

  case Flush:
    displayWin(i18n("Flush"), 25); 
    break;

  case FullHouse:
    displayWin(i18n("Full House"), 40); 
    break;

  case FourOfAKind:
    displayWin(i18n("4 of a Kind"), 125); 
    break;

  case StraightFlush:
    displayWin(i18n("Straight Flush"), 250); 
    break;

  case RoyalFlush:
    displayWin(i18n("Royal Flush"), 2000); 
    break;

  default:
    displayWin(i18n("Nothing"), 0);
    break;
  }

  startBlinking();
  m_game.setState(StateStartRound);

  if (m_game.getActivePlayer(0)->getCash() < cashPerRound)
    noMoney();
}


// Display winner and give money and so on.

void kpok::winner()
{
  PokerPlayer  *winner;

  showComputerCards();

  winner = m_game.getActivePlayer(0);
  for (int i = 1; i < m_game.getNumActivePlayers(); i++) {
    if (winner->getHand() < m_game.getActivePlayer(i)->getHand()) {
      //kdDebug() << "Hand 2 is better." << endl;
      winner = m_game.getActivePlayer(i);
    }
    else {
      //kdDebug() << "Hand 1 is better." << endl;
    }
  }
 
  displayWinner_Computer(winner, false);
}


void kpok::noMoney()
{
  KMessageBox::sorry(0, i18n("You Lost"), i18n("Oops, you went bankrupt.\n"
					       "Starting a new game.\n"));
  newGame();
}


void kpok::startBlinking()
{
  blinkTimer->start(650);
}


void kpok::stopBlinking()
{
  blinkTimer->stop();
  m_blinkStat   = 1;
  m_blinkingBox = 0;
}


void kpok::startWave()
{
  waveTimer->start(40);
  waveActive = true;
}


void kpok::stopWave()
{
  waveTimer->stop();
  fCount = -1; /* clear image */
  repaint ( FALSE );
  waveActive = false;
}


void kpok::stopDrawing()
{
  drawTimer->stop();
}


void kpok::waveTimerEvent()
{
  fCount = (fCount + 1) & 15;
  repaint( FALSE );
}


void kpok::bTimerEvent()
{
  if (m_blinking) {
    if (m_blinkStat != 0) {
      playerBox[m_blinkingBox]->blinkOn();
      m_blinkStat = 0;
    } else {
      playerBox[m_blinkingBox]->blinkOff();
      m_blinkStat = 1;
    }
  }
}


void kpok::displayWin(const TQString& hand, int cashWon)
{
  TQString buf;

  setHand(hand);
  m_game.getActivePlayer(0)->setCash(m_game.getActivePlayer(0)->getCash()
				     + cashWon);
  m_game.setDirty();
  paintCash();

  if (cashWon) {
    playSound("win.wav");
    buf = i18n("You won %1!").arg(TDEGlobal::locale()->formatMoney(cashWon));
  } else {
    playSound("lose.wav");
    buf = i18n("Game Over"); // locale
  }
  wonLabel->setText(buf);

  if (!cashWon)
    wonLabel->show();
  else {
    wonLabel->hide();
    startWave();
  }

  drawButton->setText(i18n("&Deal New Round"));
}


void kpok::paintEvent( TQPaintEvent *)
{
  /* NOTE: This was shamelessy stolen from the "hello world" example
   * coming with TQt Thanks to the TQt-Guys for doing such a cool
   * example 8-)
   */

  if (!waveActive) {
    return;
  }

  TQString  txt = wonLabel->text();
  wonLabel->hide();

  static int sin_tbl[16] = {
    0, 38, 71, 92, 100, 92, 71, 38,  0, -38, -71, -92, -100, -92, -71, -38};

  if ( txt.isEmpty() ) {
    return;
  }

  TQFont wonFont;
  wonFont.setPointSize(18);
  wonFont.setBold(true);

  TQFontMetrics fm = TQFontMetrics(wonFont);

  int w = fm.width(txt) + 20;
  int h = fm.height() * 2;
  while (w > mWonWidget->width() && wonFont.pointSize() > 6) {// > 6 for emergency abort...
    wonFont.setPointSize(wonFont.pointSize() - 1);
    fm = TQFontMetrics(wonFont);
    w = fm.width(txt) + 20;
    h = fm.height() * 2;
  }

  int pmx = mWonWidget->width() / 2 - w / 2;
  int pmy = 0;
  //   int pmy = (playerBox[0]->x() + playerBox[0]->height() + 10) - h / 4;

  TQPixmap pm( w, h );
  pm.fill( mWonWidget, pmx, pmy );

  if (fCount == -1) { /* clear area */
    bitBlt( mWonWidget, pmx, pmy, &pm );
    return;
  }

  TQPainter p;
  int x = 10;
  int y = h/2 + fm.descent();
  unsigned int i = 0;
  p.begin( &pm );
  p.setFont( wonFont );
  p.setPen( TQColor(0,0,0) );

  while ( i < txt.length() ) {
    int i16 = (fCount+i) & 15;

    p.drawText( x, y-sin_tbl[i16]*h/800, TQString(txt[i]), 1 );
    x += fm.width( txt[i] );
    i++;
  }
  p.end();

  // 4: Copy the pixmap to the Hello widget
  bitBlt( mWonWidget, pmx, pmy, &pm );
}


void kpok::drawAllDecks()
{
  for (int i = 0; i < m_game.getNumActivePlayers(); i++) {
    for (int i2 = 0; i2 < PokerHandSize; i2++) {
      m_game.getActivePlayer(i)->takeCard(i2, 0);
      playerBox[i]->paintCard(i2);
    }
  }
}


void kpok::removePlayerFromRound(PokerPlayer* removePlayer)
{
  removePlayer->setOut(true);
  m_game.inactivatePlayer(removePlayer);

  for (int i = 0; i < m_game.getNumPlayers(); i++)
    playerBox[i]->showCash();
}


void kpok::switchToOnePlayerRules()
{
  KMessageBox::information(0, i18n("You are the only player with money!\n"
				   "Switching to one player rules..."), 
			   i18n("You Won"));

  // Hide all computer players.
  for (unsigned int i = 1; i < m_numPlayers; i++) {
    playerBox[i]->hide();
  }

  m_game.setType(SinglePlayer);

  betBox->hide();
  potLabel->hide();
  playerBox[0]->singlePlayerGame(cashPerRound);
  setHand(i18n("Nothing"));
}


PokerPlayer* kpok::findHumanPlayer()
{
  for (int i = 0; i < m_game.getNumActivePlayers(); i++) {
    if (m_game.getActivePlayer(i)->getHuman())
      return m_game.getActivePlayer(i);
  }

  return m_game.getActivePlayer(0);//error
}


bool kpok::readEntriesAndInitPoker()
{
  NewGameDlg  *newGameDlg = NULL;
  TDEConfig     *conf       = kapp->config();
  int          numPlayers = DEFAULT_PLAYERS;

  conf->setGroup("NewGameDlg");
  bool showNewGameDlg = conf->readBoolEntry("showNewGameDlgOnStartup", false);
  bool aborted        = false;
  bool oldGame        = false;

  if (showNewGameDlg) {
    newGameDlg = new NewGameDlg(this);
    if (!newGameDlg->exec())
      return false; // exit game
  }

  if (!aborted && (!showNewGameDlg || showNewGameDlg &&
		   newGameDlg->readFromConfigFile())) {
    // Try starting an old game.
    //kdDebug() << "Trying to load old game" << endl;
    oldGame = loadGame();
    if (oldGame)
      return true;
  }

  if (!aborted && showNewGameDlg && !oldGame) {
    // Don't use config file - just take the values from the dialog.
    // (This is also config - the dialog defaults are from config.)
    numPlayers = newGameDlg->getPlayers();
    if (numPlayers <= 0)
      aborted = true;

    for (int i = 0; i < numPlayers; i++) {
      m_players[i].setName(newGameDlg->name(i));
      m_players[i].setCash(newGameDlg->money());
      m_game.setDirty();
    }
  }
  initPoker(numPlayers);

  if (newGameDlg != 0) {
    delete newGameDlg;
  }

  return true;
}


void kpok::betChange(int betChange)
{
  PokerPlayer* p = findHumanPlayer();

  switch(m_game.getState()){
  case StateBet1:
    if (p->getCurrentBet() + betChange >= m_game.getMinBet() &&
	p->getCurrentBet() + betChange <= m_game.getMaxBet()) {
      // Bet at least getMinBet() but not more than getMaxBet().
      if (p->changeBet(betChange))
	m_game.bet(betChange);
    }
    break;
  case StateBet2:
  case StateRaise2:
  case StateRaise1:
    if (p->getCurrentBet() + betChange >= currentMustBet
	&& p->getCurrentBet() + betChange <= currentMustBet + m_game.getMaxBet()) {
      if (p->changeBet(betChange))
	m_game.bet(betChange);
    }
    break;
  default:
    break;
  }

  paintCash();
}


void kpok::saveGame()
{
  kapp->config()->setGroup("Save");
  saveGame(kapp->config());
}


void kpok::saveGame(TDEConfig* conf)
{
  // kdWarning() << "save game" << endl;
  int players = m_game.getNumPlayers();

  conf->writeEntry("players", players);
  conf->writeEntry("lastHandText", lastHandText);

  for (int i = 0; i < players; i++) {
    conf->writeEntry(TQString("Name_%1").arg(i),  m_players[i].getName());
    conf->writeEntry(TQString("Human_%1").arg(i), m_players[i].getHuman());
    conf->writeEntry(TQString("Cash_%1").arg(i),  m_players[i].getCash());
  }
  
  m_game.clearDirty();
}


void kpok::toggleSound()    { setSound(!sound); }
void kpok::toggleBlinking() { setBlinking(!m_blinking); }
void kpok::toggleAdjust()   { setAdjust(!adjust); }


void kpok::slotPreferences()
{
  if ( mOptions==0 )
    mOptions = new OptionsDlg(this, 0, m_numPlayers);

  if (m_numPlayers > 1)
    mOptions->init(drawDelay, m_game.getMaxBet(), m_game.getMinBet());
  else
    mOptions->init(drawDelay, cashPerRound);

  if (!mOptions->exec()) {
    delete mOptions;
    mOptions = 0;
  }
}


void kpok::setAdjust(bool ad)
{
  adjust = ad;

  //update guessed money statusbar if we currently are in need of adjust
  if (m_game.getState() == StateRaise1 
      || m_game.getState() == StateRaise2) {
    if (adjust)
      adjustBet();
    else
      out();
  }
}


bool kpok::initSound()
{
  sound = true;
  return true;
}


void kpok::playSound(const TQString &soundname)
{
  if (sound)
    KAudioPlayer::play(locate("data", TQString("kpoker/sounds/")+soundname));
}


void kpok::setSound(bool s)
{
  sound = s;
}


void kpok::readOptions()
{
  if (mOptions != 0) {
    if (m_numPlayers > 1) {
      m_game.setBettingLimits(mOptions->getMinBet(), mOptions->getMaxBet());
      for (int i = 0; i < m_game.getNumActivePlayers(); i++)
	m_game.getActivePlayer(i)->setBetDefaults(m_game.getMinBet(), 
						   m_game.getMaxBet()); // inform players how much they may bet
    }
    drawDelay = mOptions->getDrawDelay();
    //	kdDebug() << cashPerRound << endl;
    //	cashPerRound = mOptions->getCashPerRound(); // NOT(!) configurable
    delete mOptions;
    mOptions = 0;
  }
}


bool kpok::loadGame()
{
  kapp->config()->setGroup("Save");
  return loadGame(kapp->config());
}


bool kpok::loadGame(TDEConfig* conf)
{
  int numPlayers = DEFAULT_PLAYERS;

  // conf->setGroup("Save");
  numPlayers = conf->readNumEntry("players", -1);

  if (numPlayers > 0) {
    for (int i = 0; i < numPlayers; i++) {
      TQString buf = conf->readEntry(TQString("Name_%1").arg(i),
				    "Player");
      m_players[i].setName(buf);
      bool human = conf->readBoolEntry(TQString("Human_%1").arg(i),
				       false);
      if (human)
	m_players[i].setHuman(); // i == 0
      int cash = conf->readNumEntry(TQString("Cash_%1").arg(i),
				    START_MONEY);
      m_players[i].setCash(cash);
      m_game.setDirty();
    }
    initPoker(numPlayers);

    // after initPoker because initPoker does a default initialization
    // of lastHandText
    conf->setGroup("Save");
    lastHandText = conf->readEntry("lastHandText", "");

    return true;
  }

  return false;
}


// These slots are called from keyboard shortcuts ('1'..'5')

void kpok::exchangeCard1()  { playerBox[0]->cardClicked(1); }
void kpok::exchangeCard2()  { playerBox[0]->cardClicked(2); }
void kpok::exchangeCard3()  { playerBox[0]->cardClicked(3); }
void kpok::exchangeCard4()  { playerBox[0]->cardClicked(4); }
void kpok::exchangeCard5()  { playerBox[0]->cardClicked(5); }


// This slot is called when the user wants to change some aspect of
// the card deck (front or back).
//
// FIXME: Maybe the slot should be moved to top.cpp instead and simply
//        call a setDeck() or setBack() method in kpok.

void kpok::slotCardDeck()
{
  kapp->config()->setGroup("General");
  TQString deckPath = kapp->config()->readPathEntry("DeckPath", 0);
  TQString cardPath = kapp->config()->readPathEntry("CardPath", 0);
  bool randomDeck, randomCardDir;

  // Show the "Select Card Deck" dialog and load the images for the
  // selected deck, if any.
  if (KCardDialog::getCardDeck(deckPath, cardPath, this, KCardDialog::Both,
			       &randomDeck, &randomCardDir) 
      == TQDialog::Accepted) {

    // Load backside and front images.
    if (playerBox && m_blinking && (m_blinkStat == 0))
      bTimerEvent();

    cardImages->loadDeck(deckPath);
    cardImages->loadCards(cardPath);

    for (int i = 0; i < m_game.getNumActivePlayers(); i++) {
      playerBox[i]->repaintCard();
    }

    // Save selected stuff in the configuration.
    kapp->config()->writePathEntry("DeckPath", deckPath);
    kapp->config()->writeEntry("RandomDeck", randomDeck);
    kapp->config()->writePathEntry("CardPath", cardPath);
    kapp->config()->writeEntry("RandomCardDir", randomCardDir);
  }
}


#include "kpoker.moc"
