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

#import "ComputerPlayer.h"
#import "GameSetup.h"
#import "MapView.h"
#import "Mover.h"
#import "CardManager.h"
#import <objc/List.h>
#import <objc/Storage.h>
#import <appkit/Panel.h>


@implementation ComputerPlayer

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

- init
{
	[self initPlayerNum:-1 mover:nil gameSetup:nil mapView:nil 
					cardManager:nil];
	return self;
}

- initPlayerNum:(int)pnum mover:mover gameSetup:gamesetup mapView:mapview
				cardManager:cardmanager
{
	[super init];
	myPlayerNum = pnum;
	theMover = mover;
	theGameSetup = gamesetup;
	theMapView = mapview;
	theCardManager = cardmanager;
	rng = [[Random allocFromZone:[self zone]] init];
	
	return self;
}

- free
{
	[rng free];
	return [super free];
}

- yourChooseCountry
	// this implementation chooses a random country from the remaining
	// unoccupied countries
{
	return self;
}

- yourInitialPlaceArmies:(int)numArmies
{
	return self;
}

- yourTurnWithArmies:(int)numArmies andCards:(int)numCards
{
	return self;
}

- youWereAttacked:country by:(int)player
{
	return self;
}

- youLostCountry:country to:(int)player
{
	return self;
}

- countryList
{
	return [[theMapView countryList] copy];
}

- myCountries
{
	id list = [[List allocFromZone:[self zone]] init];
	id cl = [theMapView countryList];
	int i, c = [cl count];
	
	for (i=0;i<c;i++)  {
		id temp = [cl objectAt:i];
		if ([temp player] == myPlayerNum)  {
			[list addObject:temp];
		}
	}
	
	return list;
}

- myCountriesWithAvailableArmies
{
	id list = [[List allocFromZone:[self zone]] init];
	id cl = [theMapView countryList];
	int i, c = [cl count];
	
	for (i=0;i<c;i++)  {
		id temp = [cl objectAt:i];
		if (([temp player] == myPlayerNum) && ([temp armies] > 1))  {
			[list addObject:temp];
		}
	}
	
	return list;
}

- neighborsTo:country
{
	id list = [[List allocFromZone:[self zone]] init];
	id cl = [theMapView countryList];
	int i, c, *neighbors;
	
	if (country == nil)  {
		return nil;
	}
	neighbors = [country getNeighborsCount:&c];
	for (i=0;i<c;i++)  {
		[list addObject:[cl objectAt:neighbors[i]]];
	}
	
	return list;
}

- countriesInContinent:(int)continent
{
	switch (continent)  {
		case NORTH_AMERICA:
			return [[theMapView northAmerica] copy];
			break;
		case SOUTH_AMERICA:
			return [[theMapView southAmerica] copy];
			break;
		case EUROPE:
			return [[theMapView europe] copy];
			break;
		case AFRICA:
			return [[theMapView africa] copy];
			break;
		case ASIA:
			return [[theMapView asia] copy];
			break;
		case AUSTRALIA:
			return [[theMapView australia] copy];
			break;
		default:
			return nil;
	}
	return nil;
}

- playersCountries:(int)pnum
{
	id list = [[List allocFromZone:[self zone]] init];
	id cl = [theMapView countryList];
	int i, c = [cl count];
	
	if ((pnum < -1) || (pnum > 5))  {
		return nil;
	}
	for (i=0;i<c;i++)  {
		id temp = [cl objectAt:i];
		if ([temp player] == pnum)  {
			[list addObject:temp];
		}
	}
	
	return list;
}

- unoccupiedCountries
{
	return [self playersCountries:-1];
}

- (BOOL)occupyCountry:country
{
	if (country == nil)  {
		return NO;
	}
	[country setPlayer:myPlayerNum andArmies:1];
	[theMapView displayCountry:country];
	return YES;
}

- myCards
{
	return [[theCardManager playersHand:myPlayerNum] copy];
}

- allMyCardSets
{
	id	store = nil;
	id	hand = [self myCards];
	id	temp[3];
	int i, j, k, c;
	
	if (!hand)  {
		return nil;
	}
	if (![theCardManager canPlayFrom:hand])  {
		[hand free];
		return nil;
	}
	store = [[Storage allocFromZone:[self zone]] initCount:0 
						elementSize:sizeof(temp) description:"[3@]"];
	c = [hand count];
	// triple loop with indexes set from one above.  all ordered permutations.
	// if the three cards pointed to,make a set, add them to the store
	for (i=0;i<c;i++)  {
		temp[0]=[hand objectAt:i];
		for (j=i+1;j<c;j++)  {
			temp[1] = [hand objectAt:j];
			for (k=j+1;k<c;k++)  {
				temp[2] = [hand objectAt:k];
				if ([theCardManager canMakeSetCard1:temp[0] card2:temp[1]
								card3:temp[2]])  {
					[store addElement:(void *)temp];
				}
			}
		}
	}
	[hand free];
	return store;
}

- (id *)compareSets:(id *)set1 :(id *)set2
// set1 and set2 are arrays of 3 ids each
{
	int num1, num2, i;
	int tempc;
	
	// first compare jokers
	num1 = num2 = 0;
	for (i=0;i<3;i++)  {
		if ([set1[i] type] == -1)  {
			num1++;
		}
		if ([set2[i] type] == -1)  {
			num2++;
		}
	}
	if (num1 != num2)  {
		return (num1>num2?set2:set1);
	}
	
	// same number of jokers in each, compare countries
	num1 = num2 = 0;
	for (i=0;i<3;i++)  {
		tempc = [set1[i] countryNum];
		if (tempc == -1)  continue;
		if ([[[theMapView countryList] objectAt:tempc] player] == myPlayerNum) 
		{
			num1++;
		}
		tempc = [set2[i] countryNum];
		if (tempc == -1)  continue;
		if ([[[theMapView countryList] objectAt:tempc] player] == myPlayerNum) 
		{
			num2++;
		}
	}
	return (num1>=num2?set1:set2);
}

- bestSet
{
	id store;
	id cl=nil;
	int i, c;
	id *bestSet = NULL;
	
	
	// first get a list of all possible set.
	store = [self allMyCardSets];

	if (store == nil)  {
		return nil;
	}
	cl = [[List allocFromZone:[self zone]] init];
	c = [store count];
	bestSet = (id *)[store elementAt:0];
	for (i=1;i<c;i++)  {
		bestSet = [self compareSets:bestSet :(id *)[store elementAt:i]];
	}
	for (i=0;i<3;i++)  {
		[cl addObject:bestSet[i]];
	}
	[store free];
	return cl;
}

- (int)playCards:cardList
{
	int numArmies = [theCardManager player:myPlayerNum playsCards:cardList];
	
	if (numArmies == -1)  {
		NXRunAlertPanel("Debug", "playCards: could not turn in card set", 
						"OK", NULL, NULL);
	}
	[theMover displayStatusView]; 
	return numArmies;
}

- (BOOL)placeArmies:(int)numArmies inCountry:country
{
	if ((numArmies <= 0) || (country == nil))  {
		return NO;
	}
	if ([country player]!=myPlayerNum)  {
		return NO;
	}
	[country addArmies:numArmies];
	[theMapView displayCountry:country];

	return YES;
}

- (BOOL)attackOnceFrom:fromCountry to:toCountry 
					victory:(BOOL *)victory fromArmies:(int *)fromArmies 
					toArmies:(int *)toArmies vanquished:(BOOL *)vanquished
					weWin:(BOOL *)wewin
{
	int fa;
	int tp;
	
	if ((fromCountry == nil) || (toCountry == nil))  {
		return NO;
	}
	if ([fromCountry player] != myPlayerNum)  {
		// we can't attack from a country that isn't ours
		return NO;
	}
	if ([toCountry player] == myPlayerNum)  {
		// we can't attack to our own country
		return NO;
	}
	if ([fromCountry armies] < 2)  {
		// not enough armies to attack with
		return NO;
	}
	*victory = [theMover attackOnceFrom:fromCountry to:toCountry];
	*vanquished = NO;
	*wewin = NO;
	if (*victory)  {
		// occupy the country and move the appropriate num of armies in
		fa = [fromCountry armies];
		tp = [toCountry player];
		if (fa > 3)  {
			[toCountry setPlayer:myPlayerNum andArmies:3];
			[fromCountry subArmies:3];
		}  else  {
			[toCountry setPlayer:myPlayerNum andArmies:fa-1];
			[fromCountry subArmies:fa-1];
		}
		if (![theMapView playerOccupiesCountries:tp])  {
			// that player is gone, take his cards and redisplay the status
			[theGameSetup playerConquered:tp];
			if ([theMapView doesPlayerWin:myPlayerNum])  {
				*wewin = YES;
				[theMover endGame:myPlayerNum];
			}  else  {
				[theCardManager player:myPlayerNum takesCardsOf:tp];
			}
			*vanquished = YES;	
		}
		[theMapView displayCountry:fromCountry];
		[theMapView displayCountry:toCountry];
	}
	*fromArmies = [fromCountry armies];
	*toArmies = [toCountry armies];
	
	return YES;
}

- (BOOL)attackTimes:(int)times from:fromCountry to:toCountry 
					victory:(BOOL *)victory fromArmies:(int *)fromArmies 
					toArmies:(int *)toArmies vanquished:(BOOL *)vanquished
					weWin:(BOOL *)wewin
{
	int i=0;
	BOOL cont = YES;
	BOOL retval = NO;
	
	if (times <= 0)  {
		return NO;
	}
	while ((cont) && (i<times))  {
		retval = [self attackOnceFrom:fromCountry to:toCountry
					victory:victory fromArmies:fromArmies toArmies:toArmies
					vanquished:vanquished weWin:wewin];
		if (retval)  {
			if (*victory)  {
				cont = NO;
			}  else  {
				cont = YES;
			}
		}  else  {
			cont = NO;
		}
		i++;
	}
	
	return retval;
}

- (BOOL)attackUntilLeft:(int)untilLeft from:fromCountry to:toCountry 
					victory:(BOOL *)victory fromArmies:(int *)fromArmies 
					toArmies:(int *)toArmies vanquished:(BOOL *)vanquished
					weWin:(BOOL *)wewin
{
	BOOL cont = YES;
	BOOL retval = NO;
	
	if (untilLeft <= 0)  {
		return NO;
	}
	while ((cont) && (untilLeft<[fromCountry armies]))  {
		retval = [self attackOnceFrom:fromCountry to:toCountry
					victory:victory fromArmies:fromArmies toArmies:toArmies
					vanquished:vanquished weWin:wewin];
		if (retval)  {
			if (*victory)  {
				cont = NO;
			}  else  {
				cont = YES;
			}
		}  else  {
			cont = NO;
		}
	}
	
	return retval;
}

- (BOOL)attackUntilCantFrom:fromCountry to:toCountry 
					victory:(BOOL *)victory fromArmies:(int *)fromArmies 
					toArmies:(int *)toArmies vanquished:(BOOL *)vanquished
					weWin:(BOOL *)wewin
{
	BOOL cont = YES;
	BOOL retval = NO;
	
	while ((cont) && ([fromCountry armies]>1))  {
		retval = [self attackOnceFrom:fromCountry to:toCountry
					victory:victory fromArmies:fromArmies toArmies:toArmies
					vanquished:vanquished weWin:wewin];
		if (retval)  {
			if (*victory)  {
				cont = NO;
			}  else  {
				cont = YES;
			}
		}  else  {
			cont = NO;
		}
	}
	
	return retval;
}

- (BOOL)moveArmies:(int)numArmies from:fromCountry to:toCountry
{
	if ((numArmies <= 0) || (fromCountry == nil) || (toCountry == nil))  {
		return NO;
	}
	if (([fromCountry player] != myPlayerNum) || 
				([toCountry player] != myPlayerNum))  {
		// you need to own both countries
		return NO;
	}
	if ([fromCountry armies] <= numArmies)  {
		// there needs to be at least numArmies+1 armies in fromCountry
		return NO;
	}
	[fromCountry subArmies:numArmies];
	[toCountry addArmies:numArmies];
	[theMapView displayCountry:fromCountry];
	[theMapView displayCountry:toCountry];
	return YES;
}

- (int)fortifyRule
{
	return [theGameSetup fortifyRule];
}

@end
