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

#import "Country.h"
#import "mapWraps.h"
#import <stdlib.h>
#import <strings.h>
#import <appkit/Panel.h>
#import <appkit/TextFieldCell.h>
#import <appkit/Font.h>
#import <appkit/Text.h>
#import <appkit/View.h>
#import <objc/List.h>

#define ARMYCELLWIDTH	25.0
#define ARMYCELLHEIGHT	17.0

@implementation Country

// the class needs a single TextFieldCell to draw the army numbers in
// the countries
id	armyCell = NULL;

+ initialize
// set the version of the class like any well-behaved class should and 
// allocate our text cell class variable
{
	if (self == [Country class])  {
		[self setVersion:1];
		if (armyCell == NULL)  {
			armyCell = [[TextFieldCell allocFromZone:[self zone]] init];
			[armyCell setBackgroundGray:NX_WHITE];
			[armyCell setBezeled:NO];
			[armyCell setFont:[Font newFont:"Helvetica" size:10.0]];
			[armyCell setAlignment:NX_CENTERED];
			[armyCell setEditable:NO];
			[armyCell setSelectable:NO];
			[armyCell setBordered:YES];
			[armyCell setTextGray:NX_BLACK];
		}
	}
	return self;
}

- initName:(const char *)nm idNum:(int)id shape:(NXCoord *)s shapePts:(int)sPts 
			neighbors:(int *)ne neighborNum:(int)nNum
// This method is the designated initializer for the class.
// It allows the specification of everything a simple country needs.
// This is called by the RiskUtil app.  Risk reads the country objects
// from a typed stream.
{
	self = [super init];
	name = NULL;
	[self setShape:s shapePts:sPts shape2:NULL shape2Pts:0 
				shape3:NULL shape3Pts:0];
	[self setNeighbors:ne neighborNum:nNum];
	[self setName:nm];
	[self setIdNum:id];
	[self setPlayer:-1 andArmies:0];
	[self setArmyCellPtX:0.0 andY:0.0];
	turn=-1;
	unmovableArmies=0;
	return self;
}

- initName:(const char *)nm idNum:(int)id
// cover for DI
{
	self = [self initName:nm idNum:id shape:NULL shapePts:0 
				neighbors:NULL neighborNum:0];
	return self;
}

- init
// cover for DI
{
	self = [self initName:NULL idNum:-1 shape:NULL shapePts:0 
				neighbors:NULL neighborNum:0];
	return self;
}

- free
// free any allocated memory
{
	if (shape) free(shape);
	if (shape2) free(shape2);
	if (shape3) free(shape3);
	if (neighbors) free(neighbors);
	if (name) free(name);
	return [super free];
}

- windowServerInit
// This method is called by MapView at startup to tell the country to
// define its user path(s) in the window server.
{
	if (shape != NULL)  {
		defineCountry(shape, shapePts, idNum, 1);
	}
	if (shape2 != NULL)  {
		defineCountry(shape2, shape2Pts, idNum, 2);
	}
	if (shape3 != NULL)  {
		defineCountry(shape3, shape3Pts, idNum, 3);
	}
	return self;
}

- drawSelfInView:view withColor:(NXColor)color isSelected:(BOOL)sel
// Called by mapview, this method draws the country.
{
//	id defaultManager = [view theDefaultManager];
	NXRect cellRect = {{0.0, 0.0}, {ARMYCELLWIDTH, ARMYCELLHEIGHT}};
	float linewidth, r, g, b;
	NXColor tempColor;
	
	// get the border linewidth and border color from the default manager
	linewidth = 2.0; //[defaultManager borderWidth];
	if (sel)  {
		tempColor = NX_COLORWHITE; //[defaultManager borderSelectedColor];
	}  else  {
		tempColor = NX_COLORBLACK; //[defaultManager borderRegularColor];
	}
	NXConvertColorToRGB(tempColor, &r, &g, &b);
	
	// draw each shape in the country
	NXSetColor(color);
	if (shape != NULL)  {
		drawFromArray(linewidth, r, g, b, idNum, 1);
	}
	NXSetColor(color);
	if (shape2 != NULL)  {
		drawFromArray(linewidth, r, g, b, idNum, 2);
	}
	NXSetColor(color);
	if (shape3 != NULL)  {
		drawFromArray(linewidth, r, g, b, idNum, 3);
	}
	// draw the armies box
	if (armies > 1)  {
		cellRect.origin.x = armyCellPt.x;
		cellRect.origin.y = armyCellPt.y;
		[armyCell setIntValue:armies-1];
		[armyCell drawSelf:&cellRect inView:view];
	}
	return self;
}

- (BOOL)ptInCountry:(NXPoint *)pt
// returns YES if pt is in the border of this country
{
	int in = 0;
	
	if (shape !=NULL)  {
		hitTestFromArray(pt->x, pt->y, idNum, 1, &in);
	}
	if ((in==0) && (shape2 !=NULL))  {
		hitTestFromArray(pt->x, pt->y, idNum, 2, &in);
	}
	if ((in==0) && (shape3 !=NULL))  {
		hitTestFromArray(pt->x, pt->y, idNum, 3, &in);
	}
	if (in==1)  {
		return YES;
	}  else  {
		return NO;
	}
}

- setPlayer:(int)p andArmies:(int)a
// a common change
{
	[self setPlayer:p];
	[self setArmies:a];
	return self;
}

- (BOOL)isNeighborTo:(int)cNum
// returns YES if we are adjacent to country number cNum (ie
// cNum is in our neighbor list.
{
	BOOL found = NO;
	int i;
	
	for (i=0;(i<neighborNum && found==NO);i++)  {
		if (neighbors[i]==cNum)  {
			found=YES;
		}
	}
	return found;
}

- (BOOL)isConnectedTo:(int)cNum withMapList:ml alreadyTried:(BOOL *)tried
// returns YES if we are connected to country cNum by a contiguous series
// of countries all owned by the same player.  This is a recursive routine.
// pass NULL for alreadyTried.
{
	int i=0, fnc =0;
	BOOL connected = NO, mallocedTried = NO;
	int fn[neighborNum];
	
	// if there isn;t already a tried list, make one.
	if (tried == NULL)  {
		int mlc = [ml count];
		mallocedTried = YES;
		tried = (BOOL *)malloc(sizeof(BOOL) * mlc);
		for (i=0;i<mlc;i++)  {
			tried[i] = NO;
		}
	}
	
	// first get all the neighbors which are friendly, check to see
	// whether the neighbors we look at are the country we are looking
	// for.
	for (i=0;i<neighborNum;i++)  {
		if (cNum == neighbors[i])  {
			return YES;
		}
		if ([[ml objectAt:neighbors[i]] player] == player)  {
			fn[fnc++] = neighbors[i];
		}
	}
	
	// now fn contains all the country indexes of friendly neighbors
	// and if we made it here at all, the country we're looking for is
	// not an immediate neighbor.  So recurse.

	// we've tried our neighbors
	tried[idNum]=YES;
	// try all our friendly neighbors
	for (i=0;((i<fnc) && (!connected));i++)  {
		if (!tried[fn[i]])  {
			// only try if we haven't before
			connected = [[ml objectAt:fn[i]] isConnectedTo:cNum withMapList:ml 
								alreadyTried:tried];
		}
	}
	// now connected should tell us whether it is connected.  
	// clean up and return
	
	// free tried only if we malloced it.
	if (mallocedTried)  {
		free(tried);
	}
	
	return connected;
}

- (const char *)name
{
	return name;
}

- (int)idNum
{
	return idNum;
}

- (int)player
{
	return player;
}

- (int)armies
{
	return armies;
}

- (int)movableArmiesForTurn:(int)tNum
// this method is used by the fortify code to make sure no one army can
// scuttle all the way across the map.
{
	if (tNum==turn)  {
		return armies-((unmovableArmies==0)?1:unmovableArmies);
	}  else  {
		return armies-1;
	}
}

- (int *)getNeighborsCount:(int *)c;
// return our neighbors for more lowlevel processing than we can do.
{
	*c = neighborNum;
	return neighbors;
}

- getBounds:(NXRect *)b
// return the bounding box for this country. Used for intelligent updating
{
	b->origin.x = bounds.origin.x;
	b->origin.y = bounds.origin.y;
	b->size.width = bounds.size.width;
	b->size.height = bounds.size.height;
	return self;
}

- setBounds:(NXRect *)b
// set the bounding box for this country. Used for intelligent updating
{
	bounds.origin.x = b->origin.x;
	bounds.origin.y = b->origin.y;
	bounds.size.width = b->size.width;
	bounds.size.height = b->size.height;
	return self;
}

- calcBounds
// calculate the bounding box for this country
{
	NXPoint min, max;
	int i;
	
	min.x = armyCellPt.x;
	min.y = armyCellPt.y;
	max.x = armyCellPt.x + ARMYCELLWIDTH;
	max.y = armyCellPt.y + ARMYCELLHEIGHT;
	
	// keep track of the biggest and smallest x and y for all shapes
	for (i=0; i<shapePts; i+=2)  {
		if (shape[i]<min.x)  {
			min.x = shape[i];
		}  else if (shape[i]>max.x)  {
			max.x = shape[i];
		}
		if (shape[i+1]<min.y)  {
			min.y = shape[i+1];
		}  else if (shape[i+1]>max.y)  {
			max.y = shape[i+1];
		}
	}
	for (i=0; i<shape2Pts; i+=2)  {
		if (shape2[i]<min.x)  {
			min.x = shape2[i];
		}  else if (shape2[i]>max.x)  {
			max.x = shape2[i];
		}
		if (shape2[i+1]<min.y)  {
			min.y = shape2[i+1];
		}  else if (shape2[i+1]>max.y)  {
			max.y = shape2[i+1];
		}
	}
	for (i=0; i<shape3Pts; i+=2)  {
		if (shape3[i]<min.x)  {
			min.x = shape3[i];
		}  else if (shape3[i]>max.x)  {
			max.x = shape3[i];
		}
		if (shape3[i+1]<min.y)  {
			min.y = shape3[i+1];
		}  else if (shape3[i+1]>max.y)  {
			max.y = shape3[i+1];
		}
	}
	bounds.origin.x = min.x;
	bounds.origin.y = min.y;
	bounds.size.width = max.x-min.x;
	bounds.size.height = max.y-min.y;
	return self;
}

- setShape:(NXCoord *)s1 shapePts:(int)sPts 
	 shape2:(NXCoord *)s2 shape2Pts:(int)s2Pts 
	 shape3:(NXCoord *)s3 shape3Pts:(int)s3Pts 
// set the shape(s) of this country
{
	shape = s1; shapePts=sPts;
	shape2 = s2; shape2Pts=s2Pts;
	shape3 = s3; shape3Pts=s3Pts;
	[self calcBounds];
	return self;
}

- setNeighbors:(int *)ne neighborNum:(int)nNum
// set the neighbors of this country
{
	neighbors = ne; neighborNum=nNum;
	return self;
}

- setName:(const char *)nm
// set the name of this country
{
	if (name != NULL) free(name);
	if (nm == NULL)  {
		name = NULL;
	} else {
		name = (char *)malloc(strlen(nm)+1);
		strcpy(name, nm);
	}
	return self;
}

- setIdNum:(int)id
{
	idNum = id;
	return self;
}

- setPlayer:(int)p
{
	player = p;
	return self;
}

- setArmies:(int)a
{
	armies = a;
	return self;
}

- addArmies:(int)a
{
	armies+=a;
	return self;
}

- subArmies:(int)a
{
	armies-=a;
	return self;
}

- addUnmovableArmies:(int)aNum forTurn:(int)tNum
// used by fortify code in conjunction with -movableArmiesForTurn
{
	if (tNum==turn)  {
		unmovableArmies+=aNum;
	}  else  {
		unmovableArmies=aNum;
		turn=tNum;
	}
	return self;
}

- setArmyCellPtX:(NXCoord)x andY:(NXCoord)y
// set the lower left corner of the armies box for this country
{
	armyCellPt.x=x;
	armyCellPt.y=y;
	[self calcBounds];
	return self;
}

- invalidateSelfInView:view
// mark our bounds as dirty in our map view
{
	[view invalidate:&bounds :1];
	return self;
}

- write:(NXTypedStream *)typedStream
// write this object to a typed stream
{
	[super write:typedStream];
	NXWriteType(typedStream, "i", &shapePts);
	if (shapePts>0)  {
		NXWriteArray(typedStream, "f", shapePts, shape);
	}
	NXWriteType(typedStream, "i", &shape2Pts);
	if (shape2Pts>0)  {
		NXWriteArray(typedStream, "f", shape2Pts, shape2);
	}
	NXWriteType(typedStream, "i", &shape3Pts);
	if (shape3Pts>0)  {
		NXWriteArray(typedStream, "f", shape3Pts, shape3);
	}
	NXWriteType(typedStream, "i", &neighborNum);
	if (neighborNum>0)  {
		NXWriteArray(typedStream, "i", neighborNum,  neighbors);
	}
	NXWriteType(typedStream, "*",  &name);
	NXWriteType(typedStream, "i", &idNum);
	NXWriteType(typedStream, "i", &player);
	NXWriteType(typedStream, "i", &armies);
	NXWritePoint(typedStream, &armyCellPt);
	NXWriteRect(typedStream, &bounds);
	
	return self;
}

- read:(NXTypedStream *)typedStream
// read this object from a typed stream
{
	[super write:typedStream];
	NXReadType(typedStream, "i", &shapePts);
	if (shapePts>0)  {
		shape = (NXCoord *)malloc(shapePts*sizeof(NXCoord));
		NXReadArray(typedStream, "f", shapePts, shape);
	}
	NXReadType(typedStream, "i", &shape2Pts);
	if (shape2Pts>0)  {
		shape2 = (NXCoord *)malloc(shape2Pts*sizeof(NXCoord));
		NXReadArray(typedStream, "f", shape2Pts, shape2);
	}
	NXReadType(typedStream, "i", &shape3Pts);
	if (shape3Pts>0)  {
		shape3 = (NXCoord *)malloc(shape3Pts*sizeof(NXCoord));
		NXReadArray(typedStream, "f", shape3Pts, shape3);
	}
	NXReadType(typedStream, "i", &neighborNum);
	if (neighborNum>0)  {
		neighbors = (int *)malloc(neighborNum*sizeof(int));
		NXReadArray(typedStream, "i", neighborNum,  neighbors);
	}
	NXReadType(typedStream, "*",  &name);
	NXReadType(typedStream, "i", &idNum);
	NXReadType(typedStream, "i", &player);
	NXReadType(typedStream, "i", &armies);
	NXReadPoint(typedStream, &armyCellPt);
	NXReadRect(typedStream, &bounds);
	turn=-1;
	unmovableArmies=0;

	return self;
}

@end
