/*
									  Paigow Poker
									Stuart Blavatnik

Description:

	Paigow poker is a casino game currently found in Las Vegas, Reno,
	Lake Tahoe and in many indian reservations on the west coast.

	Paigow has from one to six players playing against the dealer or
	five players and the dealer playing against one other player.  Each
	player is dealt seven cards and must make two poker hands from them.
	The object of the game is to make two better hands than the dealer
	(or player who acts as dealer) has.

	The first hand (referred to by me as the top hand) is made up of two
	cards.  There are only two kinds of hands possible for this hand.  The
	hand can either be a pair of something or high card.  There are no
	straights or flushes in the top hand.

	The second hand (the bottom hand) is made up from the remaining five
	cards.  This hand	is a regular poker hand with the following order:

		straight flush 				Highest
		four of a kind 	
		full house			
		flush					
		straight				
		three of a kind	
		two pair				
		pair					
		high card						Lowest

	When making your two hands, the only rule you must follow is that the
	top hand must be weaker than the bottom hand.  (i.e. if you have the
	following hand -- King of Clubs, King of Hearts, Queen of Clubs,
	Queen of Diamonds, Five of Spades, Three of Hearts, Two of Spades
	and wanted to put a pair in each hand, you would have to put the
	pair of Queens on Top and the Pair of Kings (plus the other three
	cards on the bottom)  You then would play your top hand against the
	dealer and then your bottom hand against the dealer.  If you win on
	both levels then you would win even money (minus a 5 percent commission)
	If you lose both levels you lose your bet.  If you win on one level and
	lose on the other then the hand would be declared a push or tie.

	The other stipulation in comparing hands (yours to the dealer's) is that
	the dealer wins on ties (i.e. if both you and the dealer had a KING and
	FOUR on top then the dealer would win the top)  Ties are somewhat
	infrequent (especially on the bottom hand) so there is not much of an
	advantage to the house.  This is why a 5 percent commission is taken
	for a win.



Development History

2/18/92		created deck structure, randomizing the deck, dealing for 1
				player and the dealer, player hand setup, partial hand parsing,
				debug mode, sort hands
2/19/92		finished hand parsing, handtype structure, hand rankings, compare
				for top hand, card names, description of hands
2/20/92		fixed memset bug, added stack size, now compile for large model,
				compare for five card hand, made bubble sort more generic,
				check player's hand to make sure 5 card hand is greater than 2
				card hand
2/22/92		pseudo code for determining what the dealer should play
				actual code for four of a kind and flushes
2/23/92		actual code for straights and below -- better debugging / able
				to set up dealer or player's hand and printing out other
				information
2/24/92 -
3/3/92		Windows research
3/4/92		fixed straight with one or two pair inside straight bug
3/10/92		Dealing in Windows
8/93        Conversion to C++
9/93        Enhancements
*/

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <time.h>
#include <mmsystem.h>			

#include "card.h"
#include "hand.h"
#include "paigow.h"
#include "deck.h"
#include "hands.h"
#include "game.h"
#include "ok.h"

int debug, setdealer, setplayer;			// debugging variables

struct boxes mybox[] = {
	PLAYER_RIGHTBOX, 	PLAYER_LEFTBOX,	PLAYER_TOPBOX, PLAYER_BOTTOMBOX,
	PLAYER_RIGHTBOX + BOXXSPACING * 1, 	PLAYER_LEFTBOX + BOXXSPACING * 1, 	PLAYER_TOPBOX, PLAYER_BOTTOMBOX,
	PLAYER_RIGHTBOX + BOXXSPACING * 2, 	PLAYER_LEFTBOX + BOXXSPACING * 2, 	PLAYER_TOPBOX, PLAYER_BOTTOMBOX,
	PLAYER_RIGHTBOX + BOXXSPACING * 3, 	PLAYER_LEFTBOX + BOXXSPACING * 3, 	PLAYER_TOPBOX, PLAYER_BOTTOMBOX,
	PLAYER_RIGHTBOX + BOXXSPACING * 4, 	PLAYER_LEFTBOX + BOXXSPACING * 4, 	PLAYER_TOPBOX, PLAYER_BOTTOMBOX,
	PLAYER_RIGHTBOX + BOXXSPACING * 5, 	PLAYER_LEFTBOX + BOXXSPACING * 5, 	PLAYER_TOPBOX, PLAYER_BOTTOMBOX,
	PLAYER_RIGHTBOX + BOXXSPACING * 6, 	PLAYER_LEFTBOX + BOXXSPACING * 6, 	PLAYER_TOPBOX, PLAYER_BOTTOMBOX
};

HANDLE hInst;
HBITMAP hBitmap;

Game game;
Card card;
Deck *deck;
Hand *player_seven, *player_five, *player_two, *dealer_seven, *dealer_five, *dealer_two;

/*
	Function:		WinMain()

	Parameters:		hInstance         -- id number of the program when running
                  hPrevInstance     -- id number of the most recent instance of this program running
                  lpszCmdParam      -- command line argument buffer
                  nCmdShow          -- how the window should look initially when run (SW_SHOWNORMAL or SW_SHOWMINNOACTIVE)

	Description: 	Standard Windows program entry function.  Registers the program under Windows if the program is not
                  already running,  creates the window and activates the message loop

	Returns: 		16-bit message parameter from the WM_QUIT message
*/

#pragma argsused

int PASCAL
WinMain (HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{

	static char szAppName[] = APP_NAME;
	HWND hwnd;
	MSG msg;
	WNDCLASS wndclass;

	debug = 0;

	if (!hPrevInstance) {                                             //if the program is not already running, register it
		wndclass.style = CS_HREDRAW | CS_VREDRAW;                      //the window should be redrawn if its size is modified
		wndclass.lpfnWndProc = WndProc;                                //definiton of this application's message handling function
		wndclass.cbClsExtra = 0;                                       //extra space (unused in this application)
		wndclass.cbWndExtra = 0;                                       //extra space (unused in this application)
		wndclass.hInstance = hInstance;                                //instance handle of program
		wndclass.hIcon = LoadIcon(hInstance, szAppName);               //minimized icon representation (for when application is shrunk)
		wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);                //cursor to be used in the program
		wndclass.hbrBackground = CreateSolidBrush(RGB_GREEN);          //Green background
		wndclass.lpszMenuName = MENU_NAME;                             //menu to be used in the program
		wndclass.lpszClassName = szAppName;                            //name of program

		RegisterClass(&wndclass);                                      //go for it
	}

	hInst = hInstance;      

	hwnd = CreateWindow(szAppName, APP_WINDOW_TITLE, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, APP_LENGTH, APP_WIDTH,
                        NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
   
	while (GetMessage(&msg, NULL, 0, 0)) {       //only exit on a WM_QUIT message
		TranslateMessage(&msg);                   //keyboard translation
		DispatchMessage(&msg);                    //pass the message back to Windows OS and send to appropriate window procedure (WndProc in this case)
	}
	return msg.wParam;
}

/*
	Function:		WinProc()

	Parameters:		hwnd     -- handle to the window receiving the message
                  message  -- message identifier
                  wParam   -- message parameter
                  lParam   -- message parameter

	Description: 	Program's message handler function.  This function processes all of the messages that occur durring program
                  execution.

	Returns: 		1) PROCESSED_MESSAGE -- 0 -- if message was processed in this function's switch statement
                  2) return value from DefWindowProc -- ??? -- if message was not processed in this function's switch statement
               
*/

#pragma argsused

long FAR PASCAL _export
WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
	static HWND hwndButtonDeal, hwndButtonOK, hwndStatsWindow;
	static FARPROC	lpfnAboutDlgProc, lpfnHouseRulesDlgProc;
	static HANDLE hInstance;
	static int	cxChar, cyChar, which_card_back;

	HDC hdc, statshdc;
  	PAINTSTRUCT ps;
	TEXTMETRIC	tm;
	char messagebuffer[50];
	short x, y;
	int i;

	switch (message) {
		case WM_CREATE:
			hInstance = ((LPCREATESTRUCT) lParam)->hInstance;                          //Get my own handle

         //Link dialog procedures

			lpfnAboutDlgProc = MakeProcInstance((FARPROC)AboutDlgProc, hInstance);     
			lpfnHouseRulesDlgProc = MakeProcInstance((FARPROC)HouseRulesDlgProc, hInstance);

         //Set up font

			hdc = GetDC(hwnd);											                     	//get handle to the device context
			SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));                      //get the font
			GetTextMetrics(hdc, &tm);                                                  //get info about the font
			cxChar = tm.tmAveCharWidth;                                                //set font spacing info
			cyChar = tm.tmHeight + tm.tmExternalLeading;                               //set font spacing info
			ReleaseDC(hwnd, hdc);                                                      //release the device context

         //Create buttons and windows

			hwndButtonDeal = CreateWindow("button", "DEAL",	WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,	cxChar, cyChar,
   												10 * cxChar, 7 * cyChar / 4, hwnd, 0, ((LPCREATESTRUCT) lParam)->hInstance, NULL);     
			hwndButtonOK = CreateWindow("button", "OK", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, cxChar + 100, cyChar,
												10 * cxChar, 7 * cyChar / 4, hwnd, 1, ((LPCREATESTRUCT) lParam)->hInstance, NULL);        
			hwndStatsWindow = CreateWindow("edit", "Stats", WS_CHILD | WS_BORDER | WS_VISIBLE, 300, 320, 300, 100, hwnd, 2,
   												((LPCREATESTRUCT) lParam)->hInstance, NULL);

			EnableWindow(hwndButtonOK, FALSE);	                                       // disable OK button (ghost it)
			which_card_back = -1;		                                                //indicate that this is the first time

         return PROCESSED_MESSAGE;

		case WM_PAINT:
			hdc = BeginPaint(hwnd, &ps);
			if (IsWindowEnabled(hwndButtonOK) || !IsWindowEnabled(hwndButtonDeal)) {
				which_card_back = game.DealDealer(hdc, which_card_back);
				player_seven->Show(hdc, PLAYER_RIGHTBOX + 4, PLAYER_TOPBOX + 10, BOXXSPACING);
				game.ShowHighlites(hdc);
			}
			else if(IsWindowEnabled(hwndButtonDeal) && game.GetNumberHands()) {

            RepaintScreen(hdc);

//            if (IsWindowEnabled(hwndStatsWindow)) {
//   				statshdc = GetDC(hwndStatsWindow);
//					game.PrintStats(statshdc);
//               ReleaseDC(hwnd, statshdc);
//				}

			}
			EndPaint(hwnd, &ps);
			return PROCESSED_MESSAGE;

		case WM_COMMAND:
			switch (wParam) {
				case DEAL_BUTTON_ID:

					EnableWindow(hwndButtonDeal, FALSE);
					which_card_back = DoDealButton(hwnd);

				break;

				case OK_BUTTON_ID:

               DoOkButton(hwnd);

               if (IsWindowEnabled(hwndStatsWindow)) {
   					statshdc = GetDC(hwndStatsWindow);
						game.PrintStats(statshdc);
                  ReleaseDC(hwnd, statshdc);
               }

					EnableWindow(hwndButtonDeal, TRUE);
					EnableWindow(hwndButtonOK, FALSE);

				break;

				case EXIT_MENU_CHOICE:

					SendMessage(hwnd, WM_DESTROY, wParam, lParam);

				break;

				case HOUSE_RULES_MENU_CHOICE:

					DialogBox(hInstance, "HouseRules", hwnd, lpfnHouseRulesDlgProc);

            break;

				case STATS_MENU_CHOICE:

					if (IsWindowEnabled(hwndStatsWindow)) {
         			EnableWindow(hwndStatsWindow, FALSE);
                  ShowWindow(hwndStatsWindow, SW_HIDE);
               }
               else {
						ShowWindow(hwndStatsWindow, SW_SHOW);
                  EnableWindow(hwndStatsWindow, TRUE);
      				statshdc = GetDC(hwndStatsWindow);
	   				game.PrintStats(statshdc);
	               ReleaseDC(hwnd, statshdc);
               }

            break;

				case ABOUT_MENU_CHOICE:

					DialogBox(hInstance, "AboutBox", hwnd, lpfnAboutDlgProc);

            break;

				case DEBUG_MENU_CHOICE:

					debug = ~debug;			//toggle value
					if (debug)
						strcpy(messagebuffer, "DEBUG MODE ON");
					else
						strcpy(messagebuffer, "DEBUG MODE OFF");
					OkMsgBox("Debug Status", "%s", messagebuffer);

				break;
			}
			return PROCESSED_MESSAGE;

		case WM_LBUTTONDOWN:

			x = LOWORD(lParam);
			y = HIWORD(lParam);
			hdc = GetDC(hwnd);
			if(!IsWindowEnabled(hwndButtonDeal))
				game.HighLiteCard(hdc, x, y);

			if (game.GetNumberHighlighted() == 2)
				EnableWindow(hwndButtonOK, TRUE);
			else if (IsWindowEnabled(hwndButtonOK) && game.GetNumberHighlighted() != 2)
					EnableWindow(hwndButtonOK, FALSE);
			ReleaseDC(hwnd, hdc);
			return PROCESSED_MESSAGE;

		case WM_DESTROY:

			PostQuitMessage(0);
			return PROCESSED_MESSAGE;

	}
	return DefWindowProc(hwnd, message, wParam, lParam);  //all messages that are not processed by WndProc get passed to this function
}

/*
	Function:		AboutDlgProc()

	Parameters:		hDlg     -- Handle to the dialog box
                  message  -- message identifier
                  wParam   -- message parameter
                  lParam   -- message parameter

	Description: 	About box dialog procedure 

	Returns: 		TRUE if message was processed in switch statement
                  FALSE otherwise
*/

#pragma argsused

BOOL FAR PASCAL _export
AboutDlgProc(HWND hDlg, UINT message, UINT wParam, LONG lParam)
{
	switch (message) {
		case WM_INITDIALOG:
			return TRUE;
		case WM_COMMAND:
			switch (wParam) {
				case 101:									//resource OK
					EndDialog(hDlg, 0);
					return TRUE;
			}
		break;
	}
	return FALSE;
}

/*
	Function:		HouseRulesDlgProc()

	Parameters:		hDlg     -- Handle to the dialog box
                  message  -- message identifier
                  wParam   -- message parameter
                  lParam   -- message parameter

	Description: 	House rules box dialog procedure 

	Returns: 		TRUE if message was processed in switch statement
                  FALSE otherwise
*/

#pragma argsused

BOOL FAR PASCAL _export
HouseRulesDlgProc(HWND hDlg, UINT message, UINT wParam, LONG lParam)
{
	switch (message) {
		case WM_INITDIALOG:
			CheckRadioButton(hDlg, 103, 106, 103); //clear all buttons except for first
			return FALSE;
		case WM_COMMAND:
			switch (wParam) {
				case 101:									// resource OK
					EndDialog(hDlg, 0);
					return TRUE;
			}
		break;
	}
	return FALSE;
}


/*
	Function:		DoDealButton()

	Parameters:		hwnd  -- handle to window

	Description: 	Functions that are done when the "Deal" button is clicked.

                  1) Create the hand objects

                  2) Create a new deck (and shuffle)

                  3) Deal and sort the hands

                  4) Clear the card area display the cards

	Returns: 		The number of the card back (random) the dealer has
                  
*/

int
DoDealButton(HWND hwnd)
{
	HDC hdc;
   int which_card_back;

   player_seven = new Hand(SEVEN_CARD, PLAYER);
   player_five  = new Hand(FIVE_CARD, PLAYER);
   player_two   = new Hand(TWO_CARD, PLAYER);
   dealer_seven = new Hand(SEVEN_CARD, DEALER);
   dealer_five  = new Hand(FIVE_CARD, DEALER);
   dealer_two   = new Hand(TWO_CARD, DEALER);
   deck         = new Deck();                     //reshuffle

	game.SetNumberHands(game.GetNumberHands() + 1);

	deck->Deal(player_seven, dealer_seven);

	player_seven->Sort();
	hdc = GetDC(hwnd);
	game.ClearPlayingField(hdc);
	player_seven->Show(hdc, PLAYER_RIGHTBOX + 4, PLAYER_TOPBOX + 10, BOXXSPACING);
	which_card_back = game.DealDealer(hdc, -1);						//show dealer's backs
	ReleaseDC(hwnd, hdc);

	return(which_card_back);
}

/*
	Function:		DoOkButton()

	Parameters:		hwnd  -- handle to window

	Description: 	Functions that are done when the "Ok" button is clicked.

                  1) Fix up the play screen 

                  2) build the player's 2 card and 5 card hands

                  3) Sort the player's hands

                  4) build and sort the dealer's seven card hand

                  5) show, parse and describe the two sets of hands

                  6) print the results

                  7) delete the hand objects and the decks

	Returns: 		nothing
                  
*/

void
DoOkButton(HWND hwnd)
{
   HDC hdc;

	hdc = GetDC(hwnd); 
	game.ClearHighlights(hdc);
	game.SetNumberHighlighted(0);
	game.BuildPlayerHands(player_seven, player_five, player_two);
	player_two->Sort();
	player_five->Sort();

	dealer_seven->Sort();
	game.AutoBuildHands(dealer_seven, dealer_five, dealer_two);
	dealer_two->Sort();
	dealer_five->Sort();

	game.ClearPlayingField(hdc);  //new

	player_two->Show(hdc, PLAYER_RIGHTBOX, PLAYER_TWOHANDTOP, BOTHXSPACING);
	player_five->Show(hdc, PLAYER_FIVE_RIGHT, PLAYER_FIVEHANDTOP, BOTHXSPACING);

	dealer_two->Show(hdc, DEALER_RIGHTBOX, DEALER_TWOHANDTOP, BOTHXSPACING);
	dealer_five->Show(hdc, DEALER_FIVE_RIGHT, DEALER_FIVEHANDTOP, BOTHXSPACING);

	player_two->Parse();
	player_five->Parse();
	dealer_two->Parse();
	dealer_five->Parse();

	player_two->Describe(hdc, 10, PLAYER_DESC);
	player_five->Describe(hdc, PLAYER_FIVE_RIGHT, PLAYER_DESC);
	dealer_two->Describe(hdc, 10, DEALER_DESC);
	dealer_five->Describe(hdc, DEALER_FIVE_RIGHT, DEALER_DESC);

	game.Results(hdc, CALCULATE, player_two, player_five, dealer_two, dealer_five);

   delete player_seven;
   delete player_five; 
   delete player_two;
   delete dealer_seven;
   delete dealer_five; 
   delete dealer_two;
   delete deck;

	ReleaseDC(hwnd, hdc);                                                      //release the device context

}

/*
	Function:		RepaintScreen()

	Parameters:		hdc -- device context handle

	Description: 	This function is called when the main window is altered (i.e. made invalid) via resizing or movement

	Returns: 		nothing
*/

void
RepaintScreen(HDC hdc)
{
   player_two->Show(hdc, PLAYER_RIGHTBOX, PLAYER_TWOHANDTOP, BOTHXSPACING);
   player_five->Show(hdc, PLAYER_RIGHTBOX, PLAYER_FIVEHANDTOP, BOTHXSPACING);
   dealer_two->Show(hdc, DEALER_RIGHTBOX, DEALER_TWOHANDTOP, BOTHXSPACING);
	dealer_five->Show(hdc, DEALER_RIGHTBOX, DEALER_FIVEHANDTOP, BOTHXSPACING);

	player_two->Describe(hdc, 10, PLAYER_DESC);
	player_five->Describe(hdc, PLAYER_FIVE_RIGHT, PLAYER_DESC);
	dealer_two->Describe(hdc, 10, DEALER_DESC);
	dealer_five->Describe(hdc, DEALER_FIVE_RIGHT, DEALER_DESC);

	game.Results(hdc, DONOTCALCULATE, player_two, player_five, dealer_two, dealer_five);
}
