
#import "RedDog.h"
#import "../NEXTVegas/CardSet.subproj/cardset.h"

#define RD_DRAWPAUSE 200000
#define RD_LONGPAUSE 999999

#define RD_LEFTPILE 1
#define RD_RIGHTPILE 3
#define RD_MIDDLEPILE 2
#define RD_DECKPILE 0

#define RD_WINNER 1
#define RD_LOSER 0
#define RD_BEFOREDEAL 2
#define RD_BEFORETHIRD 3

#define RD_NAMEFORDEFAULTS "NVRedDog"

@implementation RedDog
/***************************************************************************************
 *                                 Module Methods                                      *
 ***************************************************************************************/
+ initialize
{
    static NXDefaultsVector ourDefaults = {
        {"numDecks", "8"},
		{NULL, NULL}
    };
    
    NXRegisterDefaults(RD_NAMEFORDEFAULTS, ourDefaults);

	return self;
}


- awakeFromNib
/*
	Sent to every object instantiated in a nib file.  A good place to perform some
	initialization...
*/
{
	// This may take a while, so lets inform the user
	[self say:"Pre-Drawing Cards..."];
	NXPing();
	[Card drawCardImages];

	pile1 = [pileView1 cardPile];
	pile2 = [pileView2 cardPile];
	pile3 = [pileView3 cardPile];
	deckPile = [deckView cardPile];
	
	//[[[deckView setCoversOthers:YES] setCardSize:CS_SMALL] setDrawOutline:NO];
	[deckView setUseShoe:YES];
	[[[pileView1 setCoversOthers:YES] setCardSize:CS_SMALL] setDrawOutline:NO];
	[[[pileView2 setCoversOthers:YES] setCardSize:CS_SMALL] setDrawOutline:NO];
	[[[pileView3 setCoversOthers:YES] setCardSize:CS_SMALL] setDrawOutline:NO];
	
	[[[[[betView setNumCols:MAX_PLAYERS] setNumRows:1] positionChips] 
	             setEnabled:YES] setSoundEnabled:YES];
				 
	handStatus = RD_BEFOREDEAL;
	
	[self revertPreferences:self];
	
	[self checkDeck];
	
	won = 0;
	lost = 0;
	
 	return self;
}

- (BOOL)anyBetsTooLowOrHigh
{
	int i;
	char buf[512];
	
	for(i=0; i<MAX_PLAYERS; i++)
	{
		if([betView amountAtRow:0 col:i] && 
			[betView amountAtRow:0 col:i] < [self tableMin])
		{
			sprintf(buf, "%s, your bet is below the table minimum!", 
				[[PBoss() playerAt:i] playerName]);
			[PBoss() playSound:NV_WARNSOUND];
			[self say:buf];
			return YES;
		}
		if([betView amountAtRow:0 col:i] && 
			[betView amountAtRow:0 col:i] > [self tableMax])
		{
			sprintf(buf, "%s, your bet is above the table maximum!", 
				[[PBoss() playerAt:i] playerName]);
			[PBoss() playSound:NV_WARNSOUND];
			[self say:buf];
			return YES;
		}
	}	
	
	return NO;
}

- deal:sender
{
	if(handStatus == RD_BEFOREDEAL)
	{
		[self dealFirstTwo];
	}
	else if(handStatus == RD_BEFORETHIRD)
	{
		[self dealThird];
	}
		
	return self;
}

- dealFirstTwo
{
	int i;
	
	// Make sure wager is above table min, and below table max
	if([self anyBetsTooLowOrHigh]) return self;
		
	[dealButton setEnabled:NO];

	for(i=0; i<MAX_PLAYERS; i++)
		doubledAlready[i] = NO;
	
	[self checkDeck];
	
	[self dealCardToPile:RD_LEFTPILE];
	[self dealCardToPile:RD_RIGHTPILE];
	
	[self setPayoff];
	
	if(spread == 0) // two cards are the same
	{
		[self say:"The two cards are the same, dealing third card...."];
		NXPing();
		usleep(RD_LONGPAUSE);
		return [self dealThird];
	}
	else if(spread == 1)  // two cards are consecutive
	{		
		[self say:"The two cards are the consecutive, dealing again...."];
		NXPing();
		usleep(RD_LONGPAUSE);
		
		return [self dealFirstTwo];
	}
	
	[self say:"You can double your bet now, if you wish.  Press 'Continue' when ready..."];
	
	handStatus = RD_BEFORETHIRD;
	
	[dealButton setTitle:"Continue"];
	[self enableDealButtonIfNecessary];
	
	return self;
}

- dealThird
{
	[dealButton setEnabled:NO];
	
	[self dealCardToPile:RD_MIDDLEPILE];
		
	usleep(RD_LONGPAUSE);
	
	[self checkHand];
	

	[self say:"New game... Place your bets!"];
	
	handStatus = RD_BEFOREDEAL;
	
	[dealButton setTitle:"Deal"];
	[self enableDealButtonIfNecessary];
	
	return self;
}

- dealCardToPile:(int)pileNum
{
	id card, pile=deckPile, view=deckView;
	
	switch(pileNum)
	{
		case RD_LEFTPILE:	pile = pile1; view = pileView1; break;
		case RD_RIGHTPILE:	pile = pile3; view = pileView3; break;
		case RD_MIDDLEPILE:	pile = pile2; view = pileView2; break;
		default: return nil;
	}
	
	card = [deckPile cardAt:CS_TOP];
	[card setFaceUp:YES];
	
	[pile addCard:card];
	[deckPile removeCard:card];
	
	[deckView display];
	[view display];
	
	NXPing();
	[PBoss() playSound:NV_CARDSOUND];
	usleep(RD_DRAWPAUSE);
	
	return self;
}

- checkDeck
{
	[pile1 empty];
	[pile2 empty];
	[pile3 empty];
	
	[pileView1 display];
	[pileView2 display];
	[pileView3 display];
	
	if([deckPile cardCount] < 4)
	{
		[self newDeck:self];
	}
	
	return self;
}

- checkHand
{
	if([[pile1 cardAt:CS_TOP] userValue] < [[pile2 cardAt:CS_TOP] userValue] && 
		[[pile2 cardAt:CS_TOP] userValue] < [[pile3 cardAt:CS_TOP] userValue])
	{
		handStatus = RD_WINNER;
	}
	else if([[pile1 cardAt:CS_TOP] userValue] > [[pile2 cardAt:CS_TOP] userValue] &&
		[[pile2 cardAt:CS_TOP] userValue] > [[pile3 cardAt:CS_TOP] userValue])
	{
		handStatus = RD_WINNER;
	}
	else if([[pile1 cardAt:CS_TOP] userValue] == [[pile2 cardAt:CS_TOP] userValue] &&
		[[pile2 cardAt:CS_TOP] userValue] == [[pile3 cardAt:CS_TOP] userValue])
	{
		handStatus = RD_WINNER;
	}
	else
	{
		handStatus = RD_LOSER;
	}
	
	if(handStatus == RD_WINNER)
	{
		[self say:"Hand Wins!  Paying Line Bets!"];
		NXPing();
		[betView payBets];
		[wonText setIntValue:++won];
	}
	else
	{
		[self say:"Hand Loses!  Clearing Line Bets!"];
		NXPing();
		[betView clearBets];
		[lostText setIntValue:++lost];
	}
	
	[PBoss() playSound:NV_CHIPSOUND];
	
	NXPing();
	usleep(RD_LONGPAUSE);

	return self;
}

- enableDealButtonIfNecessary
{
	int i;
	BOOL shouldEnable = NO;
	
	for(i=0; i<MAX_PLAYERS; i++)
	{
		if([betView amountAtRow:0 col:i])
			shouldEnable = YES;
	}
	
	if(shouldEnable)
		[dealButton setEnabled:YES];
	else
		[dealButton setEnabled:NO];
	
	return self;
}

- newDeck:sender
{
	id theMatrix;
	int numDecks;
	
	theMatrix = [[deckPrefButton target] itemList];
	numDecks = [[theMatrix selectedCell] tag]; 
	if(numDecks == 0) numDecks = 1;

	[self newDeck:numDecks forView:deckView];
	
	[self setUserValueForPile:deckPile aceHigh:YES];
	
	return self;
}

- setPayoff
{
	spread = abs([[pile1 cardAt:CS_TOP] userValue] - [[pile3 cardAt:CS_TOP] userValue]);
	
	switch(spread)
	{
		case 0:	[betView setPayoff:15 cost:1]; break;
		case 1: return nil;
		case 2:	[betView setPayoff:7 cost:1]; break;
		case 3:	[betView setPayoff:5 cost:1]; break;
		case 4:	[betView setPayoff:3 cost:1]; break;
		case 5:	[betView setPayoff:2 cost:1]; break;
		default:	[betView setPayoff:1 cost:1]; break;
	}
	
	return self;
}

/***************************************************************************************
 *                            Inspector And Rules Methods                              *
 ***************************************************************************************/
- (BOOL)hasRules
/*
	NO if the module does not have a Rules Window, YES if it does.  The default
	method returns NO.
*/
{
	return YES;
}

- inspectorWillBeRemoved:sender
/*
	Sent when this module's inspector is about to be replace with another one.
*/
{
	return self;
}

- inspectorWasInstalled:sender
/*
	Sent just after this module's inspector has been installed.
*/
{
	return self;
}

/***************************************************************************************
 *                                  Player Methods                                     *
 ***************************************************************************************/
- (int)collectAllBetsForPlayer:(int)playerNum
{
	int total = 0;
	
	if(handStatus != RD_BEFORETHIRD)
	{
		total = [betView collectBetAtRow:0 col:playerNum];
		
		[betView display];
		
		[self enableDealButtonIfNecessary];
	}
	
	return total;
}

- playerDidBet:aPlayer
{
	return self;
}

- playerDidClose:aPlayer
/*
 *  Sent after a player has closed and is no longer part of the game.  No messages
 *  should be sent to aPlayer.  This method should just do some internal cleanup
 *  if necessary.
 */
{
	return self;
}

- playerDidJoin:player
{
	return self;
}

- (BOOL)playerWillClose:aPlayer
/*
 *  Should return YES if it is okay to close this player, NO if it is not.  The
 *  default implementation returns YES.
 */
{
    if(RD_BEFORETHIRD) return NO;
	
	return YES;
}

- (BOOL)playerWillJoin:sender
{
	return YES;
}

/***************************************************************************************
 *                                  Table Methods                                      *
 ***************************************************************************************/
- view:aView wasLoadedOnTable:tableObject
{
	table = tableObject;
	
	[self say:"Welcome to Red Dog!  Place Your Bets!"];
	
	return self;
}

- revertPreferences:sender
{
	int numDecks = atoi(NXGetDefaultValue(RD_NAMEFORDEFAULTS, "numDecks"));
	
	[self selectCellWithTag:numDecks forPopUpButton:deckPrefButton];
	
	return self;
}

- setPreferences:sender
{
    static NXDefaultsVector newDefaults = {
        {"numDecks", "8"},
		{NULL, NULL}
    };
	int numDecks = [[[[deckPrefButton target] itemList] selectedCell] tag];

    newDefaults[0].value = alloca(256);

	sprintf(newDefaults[0].value, "%d", numDecks);

    NXWriteDefaults(RD_NAMEFORDEFAULTS, newDefaults);
    
	return self;
}
/***************************************************************************************
 *                               Other Misc. Methods                                   *
 ***************************************************************************************/
- finishSessionAndClose
/*
	This message is sent when a new game is being loaded.  If there is any clean up
	to do, this is where to do it.
*/
{
	[self setPreferences:self];
	
	return self;
}

@end

@implementation RedDog(BetViewDelegate)

- player:aPlayer willBet:(int)betType onView:sender
{
	int amount, prevAmount, tag = [aPlayer tag];
	char buf[128];
	
	if(aPlayer != nil)
	{
		amount = [aPlayer selectedChip];
		
		prevAmount = [sender amountAtRow:0 col:tag];

		switch(betType)
		{
			case NV_BET:
				if(amount == DEFAULT)
				{
					amount = [self tableMin];
				}
				if(handStatus == RD_BEFORETHIRD)
				{
					if(doubledAlready[tag]) return nil;
					
					amount = [betView amountAtRow:0 col:tag];
				}
				if(amount > [aPlayer amountInBank]) 
				{
					NXBeep();
					[self say:"You can't wager more than you have!"];
					return nil;
				}
				if(prevAmount+amount > [self tableMax])
				{
					NXBeep();
					[self say:"That bet would put you over the table max!"];
					return nil;
				}
				[sender bet:amount atRow:0 col:tag];
				[aPlayer removeChip:amount];
				sprintf(buf, "Dealer accepts %s's $%d wager.", 
					[aPlayer playerName], prevAmount+amount);
				[self say:buf];
				
				if(handStatus == RD_BEFORETHIRD)
					doubledAlready[tag] = YES;
				
				break;
			
			case NV_REMOVEBET:
				if(handStatus == RD_BEFORETHIRD) return nil;
				if(prevAmount == 0) return nil;
				if(amount == DEFAULT)
					amount = prevAmount;
				if(amount > prevAmount) 
				{
					amount = prevAmount;
				}

				[sender removeBet:amount atRow:0 col:tag];
				[aPlayer addChip:amount];
				if(prevAmount-amount > 0)
				{
					sprintf(buf, "Dealer accepts %s's $%d.", 
						[aPlayer playerName], prevAmount-amount);
				}
				else
					sprintf(buf, "Player %s removed his wager", [aPlayer playerName]);
				[self say:buf];
				break;
			
			case NV_REMOVEOTHER:
				if(handStatus == RD_BEFORETHIRD) return nil;
				if(prevAmount == 0) return nil;
				amount = prevAmount;
				[sender removeBet:amount atRow:0 col:tag];
				[aPlayer addChip:amount];
				if(prevAmount-amount > 0)
				{
					sprintf(buf, "Dealer accepts %s's $%d.", 
						[aPlayer playerName], prevAmount-amount);
				}
				else
					sprintf(buf, "Player %s removed his wager", [aPlayer playerName]);
				[self say:buf];
				break;
			
			default:
				NXBeep();
				[self say:"That action is not allowed in this game..."];
				break;
		}
	}
	
	[self enableDealButtonIfNecessary];
	
	return self;
}

- (BOOL)playerWillDragChipAtRow:(int)row andCol:(int)col fromView:sender
{
	// dont allow players to drag chips when game is in progress
	if(handStatus == RD_BEFORETHIRD)
		return NO;
	else
		return YES;
}

- playerDraggedChipAtRow:(int)row andCol:(int)col fromView:sender
{
	[self enableDealButtonIfNecessary];
	
	return self;
}

- (BOOL)playerWillDropChipAtRow:(int *)row andCol:(int *)col InView:sender;
{	
	// dont allow players to drag chips when game is in progress
	if(handStatus == RD_BEFORETHIRD)
		return NO;
	else
		return YES;
}

- playerDroppedChipAtRow:(int)row andCol:(int)col inView:sender
{
	[self enableDealButtonIfNecessary];
	
	return self;
}

@end


