// Copyright (C) 2002 Neil Stevens <neil@qualityassistant.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// 
// Except as contained in this notice, the name(s) of the author(s) shall not be
// used in advertising or otherwise to promote the sale, use or other dealings
// in this Software without prior written authorization from the author(s).

#ifndef DEALER_H
#define DEALER_H

#include <qmemarray.h>
#include <qintdict.h>
#include <qobject.h>
#include <qpair.h>
#include <qstring.h>
#include "deck.h"

namespace Megami
{

// class Connection - communications from the player to the game
class Connection : public QObject
{
Q_OBJECT

public slots:
	// for use by the player
	void bet(unsigned);
	void hit(void);
	void stand(void);
	void doubleDown(void);

signals:
	// for use by the Dealer
	void connBet(int, unsigned);
	void connHit(int);
	void connStand(int);
	void connDoubleDown(int);

protected:
	friend class Dealer;
	Connection(Dealer *parent, int serial, int order);
	
private:
	int serial;
	int order;
	Dealer *dealer;
};

class Player
{
friend class Megami::Dealer;
public:

	int cash(void) { return money; };

protected:
	QString name;
	int serial;
	unsigned bet;
	Deck::Stack visible;
	Deck::Stack hidden;
	bool done;
	int money;
};

int handCount(const Deck::Stack &);

// class Dealer - the game
class Dealer : public QObject
{
Q_OBJECT

public:
	Dealer(int maxPlayers, QObject * = 0, const char * = 0);
	~Dealer();

public:
	static Player createPlayer(const QString &name, int money);
	Connection *join(Player);
	void quit(Connection *);

	static const int DealerPlayer;
	static const int ErrorPlayer;

	QString name(int player);
	void setName(int player, const QString &name);
	int cash(int player);
	void setCash(int player, unsigned);
	int hiddenCards(int player);
	Deck::Stack visibleCards(int player);

public slots:
	// accept bets
	void start();
	// stop accepting bets and play
	void deal();

signals:
	// player joined
	void join(int player);

	// player quit
	void quit(int player);

	// a hand has started
	void acceptingBets(void);

	// player made a bet
	void bet(int player, unsigned value);

	// card was dealt or revealed
	void dealt(int player, bool hidden, int card);

	// player's turn to do something
	void play(int player);

	// result for a player
	void bust(int player);
	void push(int player);
	void megami(int player);
	void win(int player);
	void lose(int player);

	// ready for another start()
	void done(void);

private slots:
	void connBet(int, unsigned);
	void connHit(int);
	void connStand(int);
	void connDoubleDown(int);

	// play dealer, pay off results;
	void resolve(void);
private:
	enum State
	{
		StateIdle = 0,
		StateBets = 1,
		StateTurn = 2,
		StateResolve = 3
	};
	State state;

	// player management
	QIntDict<Connection> connections;
	unsigned maximum, count;

	Player *players;
	Player dealer;

	int turn;

	// deck management
	static const unsigned deckCount;
	static const unsigned deckMinimum;
	Deck::Stack deck;
	int popCard(void);
	// note: to force a reshuffle just deck.popAll();
	void dealCard(Player &player, bool hidden);

	// support
	int makeSerial(void);
	// warning: can return values < 0 (DealerPlayer, ErrorPlayer)
	int intForSerial(int);
	Player *playerForSerial(int);

	unsigned getNextPlayer(unsigned fromThis);
	unsigned getFirstPlayer(void);
	unsigned getNextActivePlayer(unsigned fromThis);
	unsigned getFirstActivePlayer(void);

	QPair<int, bool> getCountAndSoft(Player &);
	int getCount(Player &p) {return getCountAndSoft(p).first;};
	bool isSoft(Player &p) {return getCountAndSoft(p).second;};
	bool hasMegami(Player &);
	void cont(void);

	void addBet(Player &player, int bet);
};

}

#endif
