// indent:4  tabsize:4  font:fixed-width
//
// The Cribbage Solitaire module.
//
// Copyright 1994 by David Fedchenko.  All rights reserved.
//

#import "Cribbage.h"
#import "CribbagePrefs.h"
#import "localstrings.h"
#import "CribDrawDelegate.h"
#import "CribLayoutDelegate.h"

@implementation Cribbage

//--------------------------------------------------------------------------
//	- startGame:
//
//	returns: (id) self
//
//	Start a new game.  Get confirmation from the user before aborting a 
//	game in progress.
//--------------------------------------------------------------------------
- startGame:sender
	{
    [super startGame:sender];
    return [self setupGame:YES];
	}

//--------------------------------------------------------------------------
//	- restartGame:
//
//	returns: (id) self
//
//	Restart the game in progress.
//--------------------------------------------------------------------------
- restartGame:sender
	{
    [super restartGame:sender];
    return [self setupGame:NO];
	}

//--------------------------------------------------------------------------
//	- setupGame:
//
//	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
	{
	id	idDrawPile;
	id	idLayouts[16];
	int	i;
	
    [gameWindow disableFlushWindow];
	
	[self loadCardArray:idLayouts];
	
	for (i = 0; i < 16; i++)
		{
        [idLayouts[i] setBackgroundColor:desktopColor];
        [idLayouts[i] setCardSize:cardSize];
        [idLayouts[i] setDelegate:idLayoutDelegate];
		[[idLayouts[i] cardPile] freeCards];
		[idLayouts[i] display];
		}
	
	[idLayoutDelegate setStrict:[prefs strict]];
	[idLayoutDelegate setDrawPileView:idDraw];
	
    [idDraw setBackgroundColor:desktopColor];
    [idDraw setCardSize:cardSize];
    [idDraw setDelegate:idDrawDelegate];
    idDrawPile = [idDraw cardPile];
	
    [idDrawPile freeCards];
    if (redeal)
		{
		[[idDrawPile addDeck] shuffle];
		
	// make a copy of the CardPile for restart option
		if (!idPrevDeck)
			{
			idPrevDeck = [[CardPile allocFromZone:[self zone]]
									initForCardSize:cardSize];
			}
		else
			{
			[idPrevDeck freeCards];
			[idPrevDeck setCardSize:cardSize];
			}
		[idPrevDeck addCopyOfPile:idDrawPile];
		}
	else
		{
		if (idPrevDeck)
			{
			// copy the saved deck back to the game deck
			[idPrevDeck setCardSize:cardSize];
			[idDrawPile addCopyOfPile:idPrevDeck];
			}
		else
			{
			// this shouldn't happen, but just in case...
			[[[idDrawPile freeCards] addDeck] shuffle];
			}
		}
	[[idDrawPile cardAt:CS_TOP] flip];
	
	[idScore1 setStringValue:""];
	[idScore2 setStringValue:""];
	[idScore3 setStringValue:""];
	[idScore4 setStringValue:""];
	[idScore5 setStringValue:""];
	[idScore6 setStringValue:""];
	[idScore7 setStringValue:""];
	[idScore8 setStringValue:""];
	[idFinalScore setStringValue:""];
	[idHeelsScore setStringValue:""];
	
    [gameWindow display];
    [gameWindow reenableFlushWindow];
    [gameWindow flushWindow];

    [gameWindow makeKeyAndOrderFront:self];

    return self;
	}

//--------------------------------------------------------------------------
//	- checkForWin
//
//	returns: (id) self
//
//	First make sure the tableau is filled and if it is, go score each hand.
//	Each hand is loaded into a scoring array along with the card on top of
//	the draw pile and then scored as a cribbage hand.  A winning layout will
//	total 61 or more for all 8 hands.  
//--------------------------------------------------------------------------
- checkForWin
	{
	id	idCards[16];
	id	idHand[5];
	id	idStarter = [[idDraw cardPile] cardAt:CS_TOP];
	int	i;
	int	j = 0;
	int	hand[8];
	int	total = 0;
	
    if ([[idDraw cardPile] cardCount] != 36)
    	{
		return self;
		}
	
	[self loadCardArray:idCards];
	
	for (i = 0; i < 16 ; i++)
		{
		idCards[i] = [[idCards[i] cardPile] cardAt:CS_TOP];
		}
	
	if ([idStarter value] == CS_JACK)
		{
		[idHeelsScore setIntValue:2];
		total += 2;
		}
	else
		{
		[idHeelsScore setIntValue:0];
		}
	
	idHand[0] = idStarter;
	for (i = 0; i < 16; i += 4)
		{
		idHand[1] = idCards[i];
		idHand[2] = idCards[i + 1];
		idHand[3] = idCards[i + 2];
		idHand[4] = idCards[i + 3];
		
		hand[j] = [self scoreHand:idHand];
		total += hand[j];
		j++;
		}
	
	for (i = 0; i < 4; i++)
		{
		idHand[1] = idCards[i];
		idHand[2] = idCards[i + 4];
		idHand[3] = idCards[i + 8];
		idHand[4] = idCards[i + 12];
		
		hand[j] = [self scoreHand:idHand];
		total += hand[j];
		j++;
		}
	
	[idScore1 setIntValue:hand[0]];
	[idScore2 setIntValue:hand[1]];
	[idScore3 setIntValue:hand[2]];
	[idScore4 setIntValue:hand[3]];
	[idScore5 setIntValue:hand[4]];
	[idScore6 setIntValue:hand[5]];
	[idScore7 setIntValue:hand[6]];
	[idScore8 setIntValue:hand[7]];
	[idFinalScore setIntValue:total];
	
	[prefs setHighScore:total];
	
	if (total >= 61)
		{
		[self win];
		}
	
    return self;
	}

//--------------------------------------------------------------------------
//	- gameWindow
//
//	returns: (id) gameWindow
//
//	Returns the window the game is being played in so updating can happen
//	cleanly when cards are being moved around.  
//--------------------------------------------------------------------------
- gameWindow
	{
	return gameWindow;
	}

//--------------------------------------------------------------------------
//	- clickShift:
//
//	returns: (id) self
//
//	The action for the arrow buttons which shift the cards around in the
//	tableau.  This is really only useful when playing in strict mode so 
//	all sides of the played cards can be made available.
//--------------------------------------------------------------------------
- clickShift:sender
	{
	int		i;
	int		j;
	int		c = 0;
	id		idCards[16];
	int		vStart;
	int		hStart;
	int		vEnd;
	int		hEnd;
	int		vStep;
	int		hStep;
	int		c1;
	int		c2;
	BOOL	fVert = YES;
	
	vStart = 0; vStep = 1;
	hStart = 0; hStep = 1;
	switch ([sender tag])
		{
		case 0: // Up
			fVert = YES;
			break;
		
		case 1: // Right
			hStart = 3; hStep = -1;
			fVert = NO;
			break;
		
		case 2: // Down
			vStart = 3; vStep = -1;
			fVert = YES;
			break;
		
		case 3: // Left
			fVert = NO;
			break;
		
		default:
			break;
		}
	
	[self loadCardArray:idCards];
	
	for (i = 0; i < 4; i++)
		{
		c += (fVert) ? [[idCards[vStart * 4 + i] cardPile] cardCount]
					 : [[idCards[i * 4 + hStart] cardPile] cardCount];
		}
	
	if (c)
		{
		return self;
		}
	
	vEnd = 3 - vStart + ((fVert) ? 0 : vStep);
	hEnd = 3 - hStart + ((fVert) ? hStep : 0);
	for (i = vStart; i != vEnd; i += vStep)
		{
		for (j = hStart; j != hEnd; j += hStep)
			{
			c1 = i * 4 + j;
			c2 = c1 + ((fVert) ? vStep * 4 : hStep);
			[[idCards[c1] cardPile] empty];
			[[idCards[c1] cardPile] addPile:[idCards[c2] cardPile]];
			}
		}
	
	for (i = 0; i < 4; i++)
		{
		if (fVert)
			{
			[[idCards[(3 - vStart) * 4 + i] cardPile] empty];
			}
		else
			{
			[[idCards[i * 4 + (3 - hStart)] cardPile] empty];
			}
		}
	
	[gameWindow display];
	
	return self;
	}

//--------------------------------------------------------------------------
//	- loadCardArray:
//
//	returns: (id) self
//
//	Used by several other methods to map the cardViews into an array for
//	easier manipulations.
//--------------------------------------------------------------------------
- loadCardArray:(id [])array
	{
	array[0] = idLayout11;
	array[1] = idLayout12;
	array[2] = idLayout13;
	array[3] = idLayout14;
	array[4] = idLayout21;
	array[5] = idLayout22;
	array[6] = idLayout23;
	array[7] = idLayout24;
	array[8] = idLayout31;
	array[9] = idLayout32;
	array[10] = idLayout33;
	array[11] = idLayout34;
	array[12] = idLayout41;
	array[13] = idLayout42;
	array[14] = idLayout43;
	array[15] = idLayout44;
	
	return self;
	}

//--------------------------------------------------------------------------
//	- scoreHand:
//
//	returns: (int) score
//
//	The main workhorse of this game.  This scores a single hand in which the
//	starter is in the 0 position of the array.  It scores flush, 15s, and 
//	runs/pairs in seperate passes.
//--------------------------------------------------------------------------
-(int) scoreHand:(id [])idHand
	{
	int			score = 0;
	CardSuit	cs = CS_LOWVALUE;
	int			c = 0;
	int			i;
	int			gridA[15] = {0};
	int			gridB[15] = {0};
	int	*		pGridA;
	int *		pGridB;
	int			count[13] = {0};
	int			values[5] = {0, 0, 2, 6, 12};
	int			length = 0;
	int			run = -1;
	int			runscore;
	
	// First check for a flush
	for (i = 1; i < 5; i++)
		{
		if (i == 1)
			{
			cs = [idHand[1] suit];
			}
		c += ([idHand[i] suit] == cs);
		// check for "his knobs"
		if (([idHand[i] value] == CS_JACK) &&
			([idHand[i] suit] == [idHand[0] suit]))
			{
			score++;
			}
		}
	if (c == 4)
		{
		c += ([idHand[0] suit] == cs);
		}
	else
		{
		c = 0;
		}
	score += c;

	// Now figure out the 15s
	for (c = 0; c < 5; c++)
		{
	    i = [idHand[c] value] + 1;
	    if (i > 10)
			{
			i = 10;
			}
	    pGridB = &gridB[i];
	    gridB[i - 1]++;
	    pGridA = gridA;
	    while (i < 15)
			{
			*pGridB++ += *pGridA++;
			i++;
	    	}
	    for (i = 0; i < 15; i++)
			{
			gridA[i] = gridB[i];
			}
		}
	score += gridA[14] * 2;
	
	//  Now look for pairs and runs
	for (i = 0; i < 5; i++)
		{
		count[[idHand[i] value]]++;
		}
	
	for (i = 0; i < 13; i++)
		{
		score += values[count[i]];
		if (run == -1)
			{
			if (count[i]) 
				{
				length++;
				}
			else
				{
				if (length > 2)
					{
					run = i - length;
					}
				else
					{
					length = 0;
					}
				}
			}
		}
	
	if (run == -1 && length > 2)
		{
		run = i - length;
		}
	
	if (run != -1)
		{
		runscore = length;
		for (i = 0; i < length; i++)
			{
			runscore *= count[run + i];
			}
		score += runscore;
		}
	
	return score;
	}

@end
