/* indent:4  tabsize:8  font:fixed-width */

#import "Pyramid.h"
#import "PyramidPrefs.h"
#import "DiscardCardPileDelegate.h"
#import "GameCardPileDelegate.h"
#import "StockCardPileDelegate.h"
#import "WasteCardPileDelegate.h"
#import "localstrings.h"

static id _pyramidSharedInstance = nil;


@implementation Pyramid

+ (Pyramid*) sharedInstance
/*"
 Instead of the old method of calling SolGameController when
 you wanted to message the Pyramid game module, you now
 call [Pyramid sharedInstance] and direct messages to
 the returned instance. You can use this to check if the game
 has been won, among other things.
"*/
{
    return _pyramidSharedInstance;
}

- initFromBundle:(NSBundle*)aBundle withName:(NSString*)name
/*"
 Extends our superclass method in order to set our shared
 instance variable.
"*/
{
    [super initFromBundle:aBundle withName:name];

    // This is used instead of having a global SolGameController()
    // function. It is assumed that this method will be called
    // only once and that our +sharedInstance method will return
    // the single instance.
    _pyramidSharedInstance = self;

    return self;
}


/*---------------------------------------------------------------------------
|
|    - startGame:
|
|----------------------------------------------------------------------------
|
|    Start a new game.  Get confirmation from the user before aborting a 
|    game in progress.
|			
\----------------------------------------------------------------------------*/

- (void) startGame:sender
{
    if (gameInProgress) [self determineScore];
    [super startGame:sender];
    [self setupGame:YES];
}


/*---------------------------------------------------------------------------
|
|    - restartGame:
|
|----------------------------------------------------------------------------
|
|    Restart the game in progress.
|			
\----------------------------------------------------------------------------*/

- (void) restartGame:sender
{
    if (gameInProgress) [self determineScore];
    [super restartGame:sender];
    [self setupGame:NO];
}


/*---------------------------------------------------------------------------
|
|    - setupGame:(BOOL)redeal
|
|----------------------------------------------------------------------------
|
|    Setup a new game.  If "redeal" is true, deal a new deck, otherwise
|    use the same cards as the previous game.
|			
\----------------------------------------------------------------------------*/

- (void) setupGame:(BOOL)redeal
{
    int 		pileIndex;
    CardPile*	stockCardPile = [stockCardPileView cardPile];

    [gameWindow disableFlushWindow];

    [self setDealCount:1];
    pyramidEmpty = NO;
    
    //----------------------------------------------------------------
    //    Sync pile id's with current game window and set 
    //    preferences and delegates
    //----------------------------------------------------------------
    
    gameCardPiles[0] = gamePileView1;
    gameCardPiles[1] = gamePileView2;
    gameCardPiles[2] = gamePileView3;
    gameCardPiles[3] = gamePileView4;
    gameCardPiles[4] = gamePileView5;
    gameCardPiles[5] = gamePileView6;
    gameCardPiles[6] = gamePileView7;
    gameCardPiles[7] = gamePileView8;
    gameCardPiles[8] = gamePileView9;
    gameCardPiles[9] = gamePileView10;
    gameCardPiles[10] = gamePileView11;
    gameCardPiles[11] = gamePileView12;
    gameCardPiles[12] = gamePileView13;
    gameCardPiles[13] = gamePileView14;
    gameCardPiles[14] = gamePileView15;
    gameCardPiles[15] = gamePileView16;
    gameCardPiles[16] = gamePileView17;
    gameCardPiles[17] = gamePileView18;
    gameCardPiles[18] = gamePileView19;
    gameCardPiles[19] = gamePileView20;
    gameCardPiles[20] = gamePileView21;
    gameCardPiles[21] = gamePileView22;
    gameCardPiles[22] = gamePileView23;
    gameCardPiles[23] = gamePileView24;
    gameCardPiles[24] = gamePileView25;
    gameCardPiles[25] = gamePileView26;
    gameCardPiles[26] = gamePileView27;
    gameCardPiles[27] = gamePileView28;
    
    [stockCardPileView setBackgroundColor:desktopColor];
    [stockCardPileView setCardSize:cardSize];
    [stockCardPileView setDelegate:stockDelegate];
    
    [wasteCardPileView setBackgroundColor:desktopColor];
    [wasteCardPileView setCardSize:cardSize];
    [wasteCardPileView setDelegate:wasteDelegate];
    
    [discardCardPileViewL setBackgroundColor:desktopColor];
    [discardCardPileViewL setCardSize:cardSize];
    [discardCardPileViewL setDelegate:discardDelegate];
    
    [discardCardPileViewR setBackgroundColor:desktopColor];
    [discardCardPileViewR setCardSize:cardSize];
    [discardCardPileViewR setDelegate:discardDelegate];

    [stockDelegate setDiscardLeft:discardCardPileViewL discardRight:discardCardPileViewR waste:wasteCardPileView];
    [gameDelegate setDiscardLeft:discardCardPileViewL discardRight:discardCardPileViewR];
    [wasteDelegate setDiscardLeft:discardCardPileViewL discardRight:discardCardPileViewR];
     
    [gameWindow display];
    [gameWindow enableFlushWindow];
    [gameWindow flushWindow];

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

    /*-----------------------------------------------------------------------
     * Initialize the other piles as empty.
     *---------------------------------------------------------------------*/

    [[wasteCardPileView cardPile] empty];
    [[discardCardPileViewL cardPile] empty];
    [[discardCardPileViewR cardPile] empty];
    [wasteCardPileView display];
    [discardCardPileViewL display];
    [discardCardPileViewR display];    
    
    /*-----------------------------------------------------------------------
     *    Remove the remaining gameCardPileViews from the view
     *    hierarchy so they can be put back in correct order.
     *	  As cards are removed from the Pyramid during game play, the
     *    Pyramid cardPileViews are removed from the view hierarchy to
     *    allow access to the piles underneath.
     *---------------------------------------------------------------------*/

    for (pileIndex = 0; pileIndex < 28; pileIndex++)
    {
	if ([gameCardPiles[pileIndex] superview])
	    [gameCardPiles[pileIndex] removeFromSuperview];
	    // Since throughout the game we keep removing these subviews
	    // from their superview, we have to retain them so they don't 
	    // get released in the middle of the game.
	    [gameCardPiles[pileIndex] retain];
    }

    /*-----------------------------------------------------------------------
     * Set the attributes of the pyramid cardpiles
     *---------------------------------------------------------------------*/

    for (pileIndex = 0; pileIndex < 28; pileIndex++)
    {
        [gameCardPiles[pileIndex] setDelegate:gameDelegate];
        [gameCardPiles[pileIndex] setBackgroundColor:desktopColor];
        [gameCardPiles[pileIndex] setCardSize:cardSize];
        [gameCardPiles[pileIndex] setDelegate:gameDelegate];
        [gameCardPiles[pileIndex] setDrawOutline:NO];
        [gameCardPiles[pileIndex] resetBacking:self];
        if (pileIndex > 0) [gameCardPiles[pileIndex] setCoversOthers:YES];
    }


    /*-----------------------------------------------------------------------
     *    Initialize and deal cards to the 28 "game piles"
     *---------------------------------------------------------------------*/

    for (pileIndex = 0; pileIndex < 28; pileIndex++)
    {
        CardPile* 	userPile;
        Card*		tempCard;
	
        /*-------------------------------------------------------------------
         *    Reinsert the game pile view in the proper hierachy.
         *    The views are unlinked during game play.
         *-----------------------------------------------------------------*/

	[[stockCardPileView superview] addSubview:gameCardPiles[pileIndex]];

	/*-------------------------------------------------------------------
         *    Deal the cards.
	 *-----------------------------------------------------------------*/
	
	userPile = [gameCardPiles[pileIndex] cardPile];
	[userPile empty];
	tempCard = [stockCardPile topCard];
	[tempCard retain];
	[stockCardPile removeCard:tempCard];
	[userPile insertCard:tempCard at:CS_TOP];
	[tempCard release];
	[[userPile topCard] flip];
    }
    
    /*-----------------------------------------------------------------------
     * Update all views, then display the window.
     *---------------------------------------------------------------------*/
    [gameWindow display];
    [gameWindow makeKeyAndOrderFront:self]; 
}


/*---------------------------------------------------------------------------
|
|    - endGame:sender
|
|----------------------------------------------------------------------------
|
|    End the game in progress.  Discard the game window.
|			
\----------------------------------------------------------------------------*/

- (void) endGame:sender
{
    if (gameInProgress) 
    {
        [self determineScore];
        gameInProgress = NO;
    }

    // close the game window
    [super endGame:sender];
}

/*---------------------------------------------------------------------------
|
|    - win
|
|----------------------------------------------------------------------------
|
|    Called when the game has been won.  This is where you can insert fancy
|    winning routines, or just call the default (boring) routine.
|
\----------------------------------------------------------------------------*/

- (void) win
{
    [super win];  // replace this with something wonderful
}


/*---------------------------------------------------------------------------
|
|    - checkForWin
|
|----------------------------------------------------------------------------
|
|    Called to check the state of the game.  Always override (unless your
|    game is impossible to win).  This is ugly because it handles winning
|    and keeping track of the score.
|
\----------------------------------------------------------------------------*/

- (void) checkForWin
{
    int i, j;
    int total;
    NSArray *viewList = [[gameWindow contentView] subviews];
    BOOL didMatch;
    
    total = [viewList count];

    /*-----------------------------------------------------------------------
     *
     *    Points for clearing the pyramid...
     *
     *---------------------------------------------------------------------*/
    for (j = 0, didMatch = NO; j < total && !pyramidEmpty && !didMatch; j++)
    {
        CardPileView* theView = [viewList objectAtIndex:j];
	
        for (i = 0; i < 28 && !didMatch; i++)
	{
	    if (theView == gameCardPiles[i]) 
	        didMatch = YES;
	}
    }
    if (!didMatch && !pyramidEmpty)
    {
	switch ([self dealCount])
	{
	case 1:
        [(PyramidPrefs*)prefs setPyramidScore:[(PyramidPrefs*)prefs pyramidScore] + 50];
	    break;
	case 2:
        [(PyramidPrefs*)prefs setPyramidScore:[(PyramidPrefs*)prefs pyramidScore] + 35];
	    break;
	case 3:
        [(PyramidPrefs*)prefs setPyramidScore:[(PyramidPrefs*)prefs pyramidScore] + 20];
	    break;
	default:
	    break;
	}
	pyramidEmpty = YES;
    }


    /*-----------------------------------------------------------------------
     *
     *    Cleared everything; you win.
     *
     *---------------------------------------------------------------------*/

    if ([[discardCardPileViewL cardPile] cardCount] +
        [[discardCardPileViewR cardPile] cardCount] == 52)
    {
        [self win];
	gameInProgress = NO;
    }
    
    /*-----------------------------------------------------------------------
     *
     *    If the stock pile is empty and we have used both redeals AND the
     *    pyramid is empty, then it's time to count the remaining cards.
     *
     *---------------------------------------------------------------------*/
    if (pyramidEmpty && [[stockCardPileView cardPile] cardCount] == 0 &&
            [self dealCount] == 3)
    {
        [self determineScore];
        gameInProgress = NO;
    }
    
    gameInProgress = YES;
}


/*---------------------------------------------------------------------------
|
|    - (int)dealCount
|
|    returns: (int) the number of deals so far (1, 2, 3, ...)
|
|----------------------------------------------------------------------------
|
|    The deal count is incremented everytime the deck is flipped.
|
\----------------------------------------------------------------------------*/

- (int) dealCount
{
    return dealCount;
}


/*---------------------------------------------------------------------------
|
|    - setDealCount:(int)count
|
|----------------------------------------------------------------------------
|
|    Set the deal count.  This is the number of passes through the stock.
\----------------------------------------------------------------------------*/

- (void) setDealCount:(int)count
{
    dealCount = count; 
}


/*---------------------------------------------------------------------------
|
|    - incDealCount
|
|----------------------------------------------------------------------------
|
|    Add one to the deal count.
|
\----------------------------------------------------------------------------*/

- (void) incDealCount
{
    dealCount++; 
}


/*---------------------------------------------------------------------------
|
|    - determineScore
|
|----------------------------------------------------------------------------
|
|    Subtract the remaining cards from the current score.
|
\----------------------------------------------------------------------------*/

- (void) determineScore
{
    int i;
    int total = 0;
    
    for (i = 0; i < 28; i++)
    {
        total += [[gameCardPiles[i] cardPile] cardCount];
    }
    total += [[stockCardPileView cardPile] cardCount];
    total += [[wasteCardPileView cardPile] cardCount];

    [(PyramidPrefs*)prefs setPyramidScore:[(PyramidPrefs*)prefs pyramidScore] - total]; 
}

@end

