
#import "Klondike.h"
#import "DrawPileDelegate.h"
#import "GamePileDelegate.h"
#import "SuitPileDelegate.h"
#import "DiscardPileDelegate.h"

#define KL_BETAMOUNT 52
#define KL_CARDVALUE 5

#define KL_NAMEFORPREFS "NVKlondike"

@implementation Klondike
/***************************************************************************************
 *                                 Module Methods                                      *
 ***************************************************************************************/
+ initialize
{
    static NXDefaultsVector ourDefaults = {
        {"numToDraw", "3"},
		{NULL, NULL}
    };
    
    NXRegisterDefaults(KL_NAMEFORPREFS, ourDefaults);

	return self;
}

- awakeFromNib
/*
	Sent to every object instantiated in a nib file.  A good place to perform some
	initialization...
*/
{
	int i;
	
	// This may take a while, so lets inform the user
	[self say:"Pre-Drawing Cards..."];
	NXPing();
	[Card drawCardImages];
	[self say:"Setting up preferences..."];
	NXPing();
 	[self revertPreferences:self];
	
	[self say:"Initializing Table..."];
	NXPing();
	[self enableDealButton];
	[amountBetText setIntValue:KL_BETAMOUNT];
	[amountWonText setIntValue:0];
	[totalWonText setIntValue:0];
	bank = 0;
	
	gameCardPileViews[0] = gamePileView1;
    gameCardPileViews[1] = gamePileView2;
    gameCardPileViews[2] = gamePileView3;
    gameCardPileViews[3] = gamePileView4;
    gameCardPileViews[4] = gamePileView5;
    gameCardPileViews[5] = gamePileView6;
    gameCardPileViews[6] = gamePileView7;
	
    suitCardPileViews[0] = suitPileView1;
    suitCardPileViews[1] = suitPileView2;
    suitCardPileViews[2] = suitPileView3;
    suitCardPileViews[3] = suitPileView4;

	for(i=0; i<4; i++)
	{
		[[[suitCardPileViews[i] setCoversOthers:YES] setDrawOutline:NO] resetBacking:self];
	}
	for(i=0; i<7; i++)
	{
		[[[gameCardPileViews[i] setCoversOthers:YES] setDrawOutline:NO] resetBacking:self];
	}
	[[[drawPileView setCoversOthers:YES] setDrawOutline:NO] resetBacking:self];
	[[[discardPileView setCoversOthers:YES] setDrawOutline:NO] resetBacking:self];

	gameInProgress = NO;
	
	[self say:""];
	
	return self;
}

/*---------------------------------------------------------------------------
|
|    - startGame:
|
|    returns:    (id)  self
|
|----------------------------------------------------------------------------
|
|    Start a new game.
|			
\----------------------------------------------------------------------------*/

- startGame:sender
{
	return [self setupGame:YES];
}


/*---------------------------------------------------------------------------
|
|    - restartGame:
|
|    returns:  (id) self
|
|----------------------------------------------------------------------------
|
|    Restart the game in progress (i.e. don't shuffle the cards).
|			
\----------------------------------------------------------------------------*/

- restartGame:sender
{
    return [self setupGame:NO];    
}


/*---------------------------------------------------------------------------
|
|    - setupGame:(BOOL)redeal
|
|    returns:  (id) self
|
|----------------------------------------------------------------------------
|
|    Setup a new game.  If "redeal" is true, deal a new deck, otherwise
|    use the same cards as the previous game.
|			
\----------------------------------------------------------------------------*/
- setupGame:(BOOL)redeal
{
    int pileIndex, cardIndex , i;
    id  suitCardPiles[4];
    id  gameCardPiles[7];
    id  drawCardPile;
	int numToDraw = [[drawPref selectedCell] tag];
	int amountToBet;
	char buf[128];

	gameInProgress = YES;
    [suitPileDelegate setEnabled:YES];
    [drawPileDelegate setEnabled:YES];
    [gamePileDelegate setEnabled:YES];
    [discardPileDelegate setEnabled:YES];
	
	sprintf(buf, "New 'Draw %s' game...", (numToDraw == 1) ? "One" : "Three");
	[self say:buf];
	
	bank = [totalWonText intValue];
	amountToBet = bank - KL_BETAMOUNT;
	
	[amountBetText setIntValue:KL_BETAMOUNT];
	[amountWonText setIntValue:0];
	
	if(bank >= KL_BETAMOUNT)
	{
		bank -= KL_BETAMOUNT;
	}
	else if(bank > 0)
	{
		amountToBet = KL_BETAMOUNT - bank;
		bank = 0;
		[[PBoss() currentPlayer] removeChip:amountToBet];
	}
	else
	{
		[[PBoss() currentPlayer] removeChip:KL_BETAMOUNT];
	}
	
	[totalWonText setIntValue:bank];
	     	
	if(numToDraw == KL_DRAWONE)
    	//[[drawPileDelegate setMaxPasses:KL_MINPASS] setDrawCardCount:numToDraw];
    	[[drawPileDelegate setMaxPasses:numToDraw] setDrawCardCount:numToDraw];
	else
    	[[drawPileDelegate setMaxPasses:numToDraw] setDrawCardCount:numToDraw];
    	//[[drawPileDelegate setMaxPasses:KL_INFINITEPASS] setDrawCardCount:numToDraw];
	
    [discardPileDelegate setDrawCardCount:numToDraw];
    
    //----------------------------------------------------------------
    //    Sync pile id's with current game window and set 
    //    preferences and delegates
    //----------------------------------------------------------------
    
    suitCardPiles[0] = suitPileView1;
    suitCardPiles[1] = suitPileView2;
    suitCardPiles[2] = suitPileView3;
    suitCardPiles[3] = suitPileView4;
    for (i = 0; i < 4; i++)
    {
       // [suitCardPiles[i] setBackgroundColor:desktopColor];
        [suitCardPiles[i] setCardSize:CS_SMALL];
    }
    
    gameCardPiles[0] = gamePileView1;
    gameCardPiles[1] = gamePileView2;
    gameCardPiles[2] = gamePileView3;
    gameCardPiles[3] = gamePileView4;
    gameCardPiles[4] = gamePileView5;
    gameCardPiles[5] = gamePileView6;
    gameCardPiles[6] = gamePileView7;
    for (i = 0; i < 7; i++)
    {
        //[gameCardPiles[i] setBackgroundColor:desktopColor];
        [gameCardPiles[i] setCardSize:CS_SMALL];
    }
    
    [gamePileDelegate setSuitCardPileViews:suitCardPiles];
    
    //[drawPileView setBackgroundColor:desktopColor];
    [drawPileView setCardSize:CS_SMALL];
    drawCardPile = [drawPileView cardPile];
    [drawPileDelegate setDiscardCardPileView:discardPileView];
    
    //[discardPileView setBackgroundColor:desktopColor];
    [discardPileView setCardSize:CS_SMALL];
    //[discardPileView setDelegate:discardPileDelegate];
    [discardPileDelegate setSuitCardPileViews:suitCardPiles];


    //-----------------------------------------------------------------------
    //    Empty the recycle pile
    //-----------------------------------------------------------------------
    
    [recycleCardPile freeCards];

    //-----------------------------------------------------------------------
    //    Initialize the drawPileView to have a shuffled
    //    deck
    //-----------------------------------------------------------------------
    [drawCardPile freeCards];
    if (redeal)
    {
		[[drawCardPile addDeck] shuffle];
	
		// make a copy of the CardPile for restart option
		if (!prevDeck)
		{
			prevDeck = [[CardPile allocFromZone:[self zone]]
	                           initForCardSize:CS_SMALL];
		}
		else
		{
			[prevDeck freeCards];
			[prevDeck setCardSize:CS_SMALL];
		}
		[prevDeck addCopyOfPile:drawCardPile];
    }
    else
    {
        if (prevDeck)
		{
			// copy the saved deck back to the game deck
			[prevDeck setCardSize:CS_SMALL];
			[drawCardPile addCopyOfPile:prevDeck];
		}
		else
		{
			// this shouldn't happen, but just in case...
			[[[drawCardPile freeCards] addDeck] shuffle];
		}
	}

    
    //----------------------------------------------------------------------
    //    Initialize the discardPileView as empty
    //----------------------------------------------------------------------

    [[discardPileView cardPile] freeCards];
    [discardPileView display];

    //-----------------------------------------------------------------------
    //    Initialize the four "suit piles" as empty
    //-----------------------------------------------------------------------

    for (pileIndex = 0; pileIndex < 4; pileIndex++)
    {
        [[suitCardPiles[pileIndex] cardPile] freeCards];
        [suitCardPiles[pileIndex] display];
    }

    //-----------------------------------------------------------------------
    //    Initialize and deal cards to the seven "user piles"
    //-----------------------------------------------------------------------

    for (pileIndex = 0; pileIndex < 7; pileIndex++)
    {
		id userPile = [gameCardPiles[pileIndex] cardPile];
        
		[userPile freeCards];
		[gameCardPiles[pileIndex] display];
    }

    for (pileIndex = 0; pileIndex < 7; pileIndex++)
    {
		id userPile = [gameCardPiles[pileIndex] cardPile];
        
		for (cardIndex = 0; cardIndex <= pileIndex; cardIndex++)
		{
			id tempCard = [drawCardPile cardAt:CS_TOP];
            
			[drawCardPile removeCard:tempCard];
			[userPile insertCard:tempCard at:CS_TOP];
			[gameCardPiles[pileIndex] display];
        }
		[[userPile cardAt:CS_TOP] flip];
		[gameCardPiles[pileIndex] display];
		//[PBoss() playSound:NV_CARDSOUND];
		//NXPing();
    }

    //-----------------------------------------------------------------------
    //    Draw the remaining cards on the
    //    drawPileView
    //-----------------------------------------------------------------------

    [drawPileView display];
    
    return self;
}


/*---------------------------------------------------------------------------
|
|    - checkForWin
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Called to check the state of the game.
|
\----------------------------------------------------------------------------*/

- checkForWin
{
	int collected = [self totalCollected];
	int won = (collected * KL_CARDVALUE);
	
	[amountWonText setIntValue:collected];
	
	//if(won > [amountBetText intValue])
		[totalWonText setIntValue:won+bank];
	
    if ([[suitPileView1 cardPile] cardCount] +
            [[suitPileView2 cardPile] cardCount] +
            [[suitPileView3 cardPile] cardCount] +
            [[suitPileView4 cardPile] cardCount] == 52)
    {
    	[self win];
    }
    return self;
}
 
- win
{
	[self say:"Winner!!!"];
	[PBoss() playSound:NV_WINSOUND];
	[suitPileDelegate setEnabled:NO];
	[drawPileDelegate setEnabled:NO];
	[gamePileDelegate setEnabled:NO];
	[discardPileDelegate setEnabled:NO];
	
	return self;
}

- enableDealButton
{
	if([PBoss() currentPlayer])
		[dealButton setEnabled:YES];
	else
		[dealButton setEnabled:NO];
	
	return self;
}

- (int)totalCollected
/*
	Returns the total of all the cards in all the suit piles
*/
{
    int amount = [[suitPileView1 cardPile] cardCount] +
                 [[suitPileView2 cardPile] cardCount] +
                 [[suitPileView3 cardPile] cardCount] +
                 [[suitPileView4 cardPile] cardCount];
			 
	return amount;
}

- endGame
{
	//NXRunAlertPanel("Klondike", "The Game Is Over.", "Okay", NULL, NULL);
	[self say:"End of Game.  Keep playing until you collect, or press 'New Deal' to start over."];
	gameInProgress = NO;
	
	return self;
}

- findHomeFor:cardPile fromPileView:aCardPileView
/*
 *  First searches the suit piles to see if the card can go their, then searches
 *  the game piles.  If it finds a home, it returns the view that the card can be
 *  moved to.
 */
{
	int i;
	
	for (i=0;i<4;i++)
	{
		[[suitCardPileViews[i] delegate] setDoubleClickCheck];
		if ([[suitCardPileViews[i] delegate] canAcceptPile:cardPile
											from:aCardPileView
											in:suitCardPileViews[i]])
		{
			return suitCardPileViews[i];
		}
	}
	
	for(i=0; i<7; i++)
	{
		if([[gameCardPileViews[i] delegate] 
			canAcceptPile:cardPile from:aCardPileView in:gameCardPileViews[i]] && 
			gameCardPileViews[i] != aCardPileView)
		{
			return gameCardPileViews[i];
		}
	}

	return nil;
}


/***************************************************************************************
 *                            Inspector And Rules Methods                              *
 ***************************************************************************************/
- (BOOL)hasRules
{
	return YES;
}

- revertPreferences:sender
{
	int numToDraw = atoi(NXGetDefaultValue(KL_NAMEFORPREFS, "numToDraw"));
	
	[drawPref selectCellWithTag:numToDraw];

	return self;
}

- setPreferences:sender
{
    static NXDefaultsVector newDefaults = {
        {"numToDraw", "3"},
		{NULL, NULL}
    };
	int numToDraw = [[drawPref selectedCell] tag];

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

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

    NXWriteDefaults(KL_NAMEFORPREFS, newDefaults);
       
	return self;
}

/***************************************************************************************
 *                                  Player Methods                                     *
 ***************************************************************************************/
- (int)collectAllBetsForPlayer:(int)playerNum
{
	int amountWon = [totalWonText intValue];
	
	if(gameInProgress)
	{
		[self endGame];
		[suitPileDelegate setEnabled:NO];
		[drawPileDelegate setEnabled:NO];
		[gamePileDelegate setEnabled:NO];
		[discardPileDelegate setEnabled:NO];
	}
	[self say:"Game Over.  Press 'New Deal' to start a new game."];
	if(amountWon > 0)
	{
		[totalWonText setIntValue:0];
		[amountWonText setIntValue:0];
		bank = 0;
		return amountWon;
	}
	return 0;
}

- 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.
 */
{
	[self enableDealButton];
	
	return self;
}

- playerDidJoin:player
{
	[self enableDealButton];
	
	return self;
}

/***************************************************************************************
 *                                  Table Methods                                      *
 ***************************************************************************************/
- view:aView wasLoadedOnTable:tableObject
{
	table = tableObject;
	
	[self say:"Welcome to Klondike!!!   Press 'New Deal' to start a new game."];

	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.
*/
{
	return self;
}

@end

@implementation Klondike(BetViewDelegate)

- player:aPlayer willBet:(int)betType onView:aBetView
{
	return self;
}

- (BOOL)playerWillDragChipAtRow:(int)row andCol:(int)col fromView:sender
/*
	Sent by a BetView when a player wishes to drag a chip pile.  Overwrite this
	method to return YES if your module will allow drag and drop.
*/
{
	return NO;
}

- playerDraggedChipAtRow:(int)row andCol:(int)col fromView:sender
/*
	Sent by a BetView after a player drags a chip pile.  
*/
{
	return self;
}

- (BOOL)playerWillDropChipAtRow:(int *)row andCol:(int *)col InView:sender
/*
	Sent by a BetView when a player wants to drop a chip pile on it.  Over write this
	method to return YES if your module allows drag and drop.
*/
{
	return NO;
}

- playerDroppedChipAtRow:(int)row andCol:(int)col inView:sender
/*
	Sent by a BetView when a player drops a chip on it.  Overwrite this method to add
	the pile to the betview.  Check out the blackjack module to see how this is done.
*/
{
	return self;
}

@end

