// CardManager.m
// Part of Risk by Mike Ferris

#import "CardManager.h"
#import <objc/List.h>
#import <appkit/Application.h>
#import <appkit/Button.h>
#import <appkit/ButtonCell.h>
#import <appkit/Matrix.h>
#import <appkit/publicWraps.h>
#import <appkit/Panel.h>
#import "Random.h"
#import "Card.h"
#import "LanguageApp.h"
#import "GameSetup.h"
#import "MapView.h"
#import "Country.h"
#import "DeckInspector.h"

#define CARDFILE  "Card.data"
#define CARDBACKFILE "Cards/CardBack.tiff"

@implementation CardManager

+ initialize
{
	if (self == [CardManager class])  {
		[self setVersion:1];
	}
	return self;
}

- init
{
	NXTypedStream *ts;

	[super init];
	deckInspector = nil;
	rng = [[Random allocFromZone:[self zone]] init];
	deck = [[List allocFromZone:[self zone]] init];
	discards = [[List allocFromZone:[self zone]] init];
	playerHand[0] = [[List allocFromZone:[self zone]] init];
	playerHand[1] = [[List allocFromZone:[self zone]] init];
	playerHand[2] = [[List allocFromZone:[self zone]] init];
	playerHand[3] = [[List allocFromZone:[self zone]] init];
	playerHand[4] = [[List allocFromZone:[self zone]] init];
	playerHand[5] = [[List allocFromZone:[self zone]] init];
	cardsPlayed = [[List allocFromZone:[self zone]] init];
	cardBack = [[Card allocFromZone:[self zone]] initCountry:-1 type:NIL_TYPE 
								imageFile:CARDBACKFILE];
	
	// fill card list

	ts = NXOpenTypedStreamForFile([NXApp applicationFile:CARDFILE], 
								NX_READONLY);
	cardList = NXReadObject(ts);
	NXCloseTypedStream(ts);
	
	setsTurnedIn=0;
	
	return self;
}

- appDidInit:sender
{
	return self;
}

- free
{
	int i;
	
	[cardList free];
	[cardBack free];
	[discards free];
	for (i=0;i<6;i++)  {
		[playerHand[i] free];
	}
	[rng free];
	[deck freeObjects];
	[deck free];
	return [super free];
}

// low-level list manipulators
- shuffle:list
// shuffle the list given into a random order
{
	id listcopy;
	
	listcopy = [list copy];
	[list empty];
	[self shuffle:listcopy into:list];
	[listcopy free];
	return self;
}

- shuffle:list1 into:list2
// shuffle the list given into the second list.
// in other words: copy list1 into list2 in a random order
// if there are cards in list2 already, they stay at the beginning
// new cards are added to the end.
{
	// loop takes a random item out of list1 and appends it to list2
	while ([list1 count]>0)  {
		[list2 addObject:[list1 removeObjectAt:[rng randMax:[list1 count]-1]]];
	}
	return self;
}

- reset
{
	int i;
	id listcopy;
	
	setsTurnedIn=0;
	listcopy = [cardList copy];
	[self shuffle:listcopy into:deck];
	[listcopy free];
	[discards empty];
	for (i=0;i<6;i++)  {
		[playerHand[i] empty];
	}
	if ((deckInspector != nil) && ([deckInspector panelOnScreen]))  {
		[deckInspector displayAllDecks];
	}
	return self;
}
	
// methods for manipulating player's hands
- playerDrawsCard:(int)p
{
	if ([deck count]<3)  {
		[self shuffle:discards into:deck];
	}
	[playerHand[p] addObject:[deck removeObjectAt:0]];
	
	if ((deckInspector != nil) && ([deckInspector panelOnScreen]))  {
		[deckInspector displayAllDecks];
	}
	
	return self;
}

- playersHand:(int)p  {  return playerHand[p];  }

- (int)numCardsForPlayer:(int)p  {  return [playerHand[p] count];  }

- (BOOL)canMakeSetCard1:(Card *)c1 card2:(Card *)c2 card3:(Card *)c3
{
	if ((c1==nil) || (c2==nil) || (c3==nil))  {
		return NO;
	}  else  {
		// any jokers?  then YES
		if (([c1 type] == NIL_TYPE)  ||
					([c2 type] == NIL_TYPE)  ||
					([c3 type] == NIL_TYPE))  {
			return YES;
		}  else if (([c1 type] == [c2 type]) &&
					([c1 type] == [c3 type]))  {
			return YES;
		}  else if (([c1 type] != [c2 type]) &&
					([c1 type] != [c3 type]) &&
					([c2 type] != [c3 type]))  {
			return YES;
		}
		return NO;
	}
	return NO;
}

- (BOOL)canPlayThree:cl
// given a list with three elements return YES if they can be played
// otherwise NO
{
	if ([cl count] < 3)  {
		return NO;
	}  else if ([cl count] == 3)  {
		return [self canMakeSetCard1:[cl objectAt:0] card2:[cl objectAt:1] 
								card3:[cl objectAt:2]];
	}
	return NO;
}

- (BOOL)canPlayFrom:cl
// returns YES if three of the cards can be played. NO otherwise
{
	if ([cl count] < 3)  {
		return NO;
	}  else if ([cl count] == 3)  {
		return [self canPlayThree:cl];
	}  else if ([cl count] >= 5)  {
		return YES;
	}  else  {
		// there are 4 cards so run permutations
		if ([self canMakeSetCard1:[cl objectAt:0] card2:[cl objectAt:1] 
									card3:[cl objectAt:2]])  {
			return YES;
		}  else  if ([self canMakeSetCard1:[cl objectAt:0] 
						card2:[cl objectAt:2] card3:[cl objectAt:3]])  {
			return YES;
		}  else  if ([self canMakeSetCard1:[cl objectAt:0] 
						card2:[cl objectAt:1] card3:[cl objectAt:3]])  {
			return YES;
		}  else  if ([self canMakeSetCard1:[cl objectAt:1] 
						card2:[cl objectAt:2] card3:[cl objectAt:3]])  {
			return YES;
		}  else  {
			return NO;
		}
	}
	return NO;
}

- (BOOL)canPlayerPlay:(int)p
{
	return [self canPlayFrom:playerHand[p]];
}

- player:(int)p1 takesCardsOf:(int)p2
{
	while ([playerHand[p2] count] > 0)  {
		[playerHand[p1] addObject:[playerHand[p2] removeObjectAt:0]];
		if ((deckInspector != nil) && ([deckInspector panelOnScreen]))  {
			[deckInspector displayAllDecks];
		}
	}
	return self;
}

- (int)player:(int)p playsCards:cl
{
	id theCard, theCountry;
	id countryList = [theMapView countryList];
	int armiesToGive, i;
	
	if ([cl count] != 3)  {
		// has to be exactly three cards
		return -1;
	}
	if ([self canPlayThree:cl])  {
		for (i=0;i<3;i++)  {
			theCard = [cl objectAt:i];
			if ([theCard countryNum]>=0)  {
				theCountry = [countryList objectAt:[theCard countryNum]];
				if ([theCountry player]==p)  {
					[theCountry addArmies:2];
					[theMapView displayCountry:theCountry];
				}
			}
			[discards addObject:[playerHand[p] removeObject:theCard]];
		}
		armiesToGive = [theGameSetup armiesForCardSet:setsTurnedIn];
		setsTurnedIn++;
		if ((deckInspector != nil) && ([deckInspector panelOnScreen]))  {
			[deckInspector displayAllDecks];
		}
		return armiesToGive;
	}  else  {
		return -1;
	}
}

// high-level interface routines

- setupPanel:(BOOL)canPlay forPlayer:(int)p
{
	int c=[playerHand[p] count];
	int i;
	id tempCell, theCard;
	id countryList=[theMapView countryList];
	
	currentPlayer = p;
	currentSetsTurnedIn = 0;
	[cardsPlayed empty];
	if ((deckInspector != nil) && ([deckInspector panelOnScreen]))  {
		[deckInspector displayAllDecks];
	}
	
	// set up the done and cancel all buttons and the force & amassed fields
	[doneButton setEnabled:NO];
	[cancelButton setEnabled:YES];
	[amassedTextField setIntValue:0];
	currentForceNum=0;
	if ((c>=5)  && (canPlay))  {
		[forceTextField setStringValue:"You must turn in cards"];
		[cancelButton setEnabled:NO];
		currentForceNum++;
	}  else  {
		[forceTextField setStringValue:""];
	}
	if (c>=8)  {
		currentForceNum++;
	}
	
	// set the play matrix
	playMatrixCount=0;
	for (i=0;i<3;i++)  {
		tempCell=[playMatrix cellAt:0 :i];
		[tempCell setImage:nil];
		currentSet[i]=nil;
		currentIndices[i]=-1;
		[[playStarMatrix cellAt:0 :i] setImage:nil];
	}
	if (canPlay)  {
		[playMatrix setEnabled:YES];
	}  else  {
		[playMatrix setEnabled:NO];
	}
	[playMatrix display];
	[playStarMatrix setEnabled:NO];
	[playStarMatrix display];
		
	// set the play box, turn in button, and worth text field
	[turnInButton setEnabled:NO];
	[worthTextField setIntValue:[theGameSetup 
			armiesForCardSet:setsTurnedIn+1]];
	
	// set the handMatrix
	for (i=0;i<9;i++)  {
		tempCell=[handMatrix cellAt:0 :i];
		// if we're still in the buttons less than num of player's cards
		// set the image
		if (i<c)  {
			theCard = [playerHand[p] objectAt:i];
			[tempCell setImage:[theCard image]];
			if ([theCard countryNum] >= 0)  {
				if ([[countryList objectAt:[theCard countryNum]]
									 player] == p)  {
					[[handStarMatrix cellAt:0 :i] setIcon:"LittleStar"];
				}  else  {
					[[handStarMatrix cellAt:0 :i] setImage:nil];
				}
			}  else  {
				[[handStarMatrix cellAt:0 :i] setImage:nil];
			}
		}  else  {
			[tempCell setImage:nil];
			[[handStarMatrix cellAt:0 :i] setImage:nil];
		}
	}
	if (canPlay)  {
		[handMatrix setEnabled:YES];
	}  else  {
		[handMatrix setEnabled:NO];
	}
	[handMatrix display];
	[handStarMatrix setEnabled:NO];
	[handStarMatrix display];
	
	
	return self;
}

- (int)runCardPanel:(BOOL)canPlay forPlayer:(int)p
// returns number of armies turned in
{
	int retVal, armiesToGive=0;
	id theCard, theCountry;
	id countryList=[theMapView countryList];
	
	// first set up the panel
	[self setupPanel:canPlay forPlayer:p];
	
	// now run it
	retVal = [NXApp runModalFor:theCardPanel];
	[theCardPanel orderOut:self];
	if (retVal==0)  {
		// done button pressed.  turn in the sets and give the armies
		while ([cardsPlayed count]>0)  {
			theCard = [cardsPlayed removeObjectAt:0];
			if ([theCard countryNum]>=0)  {
				theCountry = [countryList objectAt:[theCard countryNum]];
				if ([theCountry player]==p)  {
					[theCountry addArmies:2];
					[theMapView displayCountry:theCountry];
				}
			}
			[discards addObject:theCard];
			[playerHand[p] removeObject:theCard];
			if ((deckInspector != nil) && ([deckInspector panelOnScreen]))  {
				[deckInspector displayAllDecks];
			}
		}
		armiesToGive = [amassedTextField intValue];
		setsTurnedIn+=currentSetsTurnedIn;
		return armiesToGive;
	}  else if (retVal == 1)  {
		// cancel pressed, clean up and do nothing
		[cardsPlayed empty];
		return 0;
	}  else  {
		NXRunAlertPanel("Debug", "Error: unexpected return value.", 
						"OK", NULL, NULL);
		return 0;
	}
	if ((deckInspector != nil) && ([deckInspector panelOnScreen]))  {
		[deckInspector displayAllDecks];
	}
	
	return 0;
}

- handAction:sender
{
	int index = [[sender selectedCell] tag];
	
	// if there is room in the box and they didn't click on an empty cell
	if ((playMatrixCount<3) && (index < [playerHand[currentPlayer] count]))  {
		if ([[handMatrix cellAt:0 :index] image]==[cardBack image])  {
			return nil;
		}
		if (playMatrixCount==2)  {
			// check to see if this one will make a third
			if (![self canMakeSetCard1:currentSet[0] card2:currentSet[1] 
						card3:[playerHand[currentPlayer] objectAt:index]])  {
				NXBeep();
				return nil;
			}
		}
		// turn the card over
		[[handMatrix cellAt:0 :index] setImage:[cardBack image]];
		// record which card it is
		currentSet[playMatrixCount]=[playerHand[currentPlayer] objectAt:index];
		currentIndices[playMatrixCount]=index;
		// put it down below
		[[playMatrix cellAt:0 :playMatrixCount] setImage:
							[currentSet[playMatrixCount] image]];
		[[playStarMatrix cellAt:0 :playMatrixCount] setImage:
							[[handStarMatrix cellAt:0 :index] image]];
		
		playMatrixCount++;
	}
	[handMatrix display];
	[playMatrix display];
	[handStarMatrix display];
	[playStarMatrix display];
	if (playMatrixCount==3)  {
		[turnInButton setEnabled:YES];
	}
	
	return self;
}

- playAction:sender
{
	int index = [[sender selectedCell] tag];
	int i;
	
	// put it back up above
	if (index < playMatrixCount)  {
		[[handMatrix cellAt:0 :currentIndices[index]] setImage:
								[currentSet[index] image]];
		// move the others back a step
		for (i=index;i<playMatrixCount-1;i++)  {
			[[playMatrix cellAt:0 :i] setImage:
								[[playMatrix cellAt:0 :i+1] image]];
			[[playStarMatrix cellAt:0 :i] setImage:
								[[playStarMatrix cellAt:0 :i+1] image]];
			currentSet[i] = currentSet[i+1];
			currentIndices[i] = currentIndices[i+1];
		}
		[[playMatrix cellAt:0 :playMatrixCount-1] setImage:nil];
		[[playStarMatrix cellAt:0 :playMatrixCount-1] setImage:nil];
		currentSet[playMatrixCount-1] = nil;
		currentIndices[playMatrixCount-1] = -1;
		playMatrixCount--;
	}
	[handMatrix display];
	[playMatrix display];
	[handStarMatrix display];
	[playStarMatrix display];
	if (playMatrixCount<3)  {
		[turnInButton setEnabled:NO];
	}
	
	return self;
}

- stopAction:sender
{
	[NXApp stopModal:[sender tag]];
	return self;
}

- turnInAction:sender
{
	int i;
	
	if (playMatrixCount==3)  {
		for (i=0;i<3;i++)  {
			[cardsPlayed addObject:currentSet[i]];
			currentSet[i]=nil;
			currentIndices[i]=-1;
			[[playMatrix cellAt:0 :i] setImage:nil];
			[[playStarMatrix cellAt:0 :i] setImage:nil];
		}
		playMatrixCount=0;
		[playMatrix display];
		[playStarMatrix display];
		[amassedTextField setIntValue:[amassedTextField intValue] + 
				[theGameSetup 
				armiesForCardSet:setsTurnedIn+currentSetsTurnedIn+1]];
		currentSetsTurnedIn++;
		if (currentSetsTurnedIn>=currentForceNum)  {
			[doneButton setEnabled:YES];
		}
	}
	[worthTextField setIntValue:[theGameSetup 
					armiesForCardSet:setsTurnedIn+currentSetsTurnedIn+1]];
	if ((deckInspector != nil) && ([deckInspector panelOnScreen]))  {
		[deckInspector displayAllDecks];
	}
	return self;
}

- setDeckInspector:anObject
{
	deckInspector = anObject;
	[deckInspector setCardList:cardList deck:deck discards:discards
				player1:playerHand[0] player2:playerHand[1] 
				player3:playerHand[2] player4:playerHand[3] 
				player5:playerHand[4] player6:playerHand[5] 
				cardsPlayed:cardsPlayed];
	[deckInspector displayAllDecks];
	return self;
}

@end
