/*
 * Board
 * description: the object that stores the state of a Splat Board
 * history:
 *	2/14/93 [Erik Kay] - created
 *	< I wasn't keeping track of my changes here >
 *	6/8/93 [Erik Kay] - added bycopy support
 */

#import "Board.h"

@implementation Board

// initialization

- initCols:(int)c Rows:(int)r
{
    [super init];
    maxRow = r;
    maxCol = c;
    numone = 0;
    numtwo = 0;
    board = malloc (sizeof (int *) * (maxCol + 1) * (maxRow + 1));
    filename = NULL;
    return self;
}

- init
{
    [self initCols:8 Rows:8];
    [self clearBoard];
    filename = NULL;
    return self;
}

- free
{
    free(board);
    free(filename);
    [super free];
    return self;
}

// loading a .board file

+ newFromFile:(const char *)fname
{
    int maxr, maxc;
    char line[256];
    id newb;
    FILE *f;
    if ((f = fopen(fname,"r")) == NULL)
    	return nil;
    fgets (line, 255, f);
    maxc = strlen(line)/2;
    maxr = 0;
    do {
	if (strlen(line)/2 != maxc) {
	    return nil;
	}
	maxr++;
    } while (fgets(line, 255, f));
    fclose(f);
    newb = [[Board alloc] initCols:maxc Rows:maxr];
    [newb setFilename:fname];
    [newb clearBoard];
    [newb reloadFromFile];
    return newb;
}

- reloadFromFile
{
    int r, c;
    char line[256];
    FILE *f;
    
    if (!filename || ((f = fopen(filename,"r")) == NULL))
    	return nil;
    r = c = 0;
    fseek(f,0,SEEK_SET);
    while (fgets(line, 255, f)) {
	for (c = 0; c < maxCol; c++) {
	    switch (line[c*2]) {
	    	case '+':
		    [self setPiece:SQUARE_BLOCKED at:c :(maxRow -1 - r)];
		    break;
		case '1':
		    [self setPiece:SQUARE_ONE at:c :(maxRow -1 - r)];
		    break;
		case '2':
		    [self setPiece:SQUARE_TWO at:c :(maxRow -1 - r)];
		    break;
		default:
		    [self setPiece:SQUARE_EMPTY at:c :(maxRow -1 - r)];
		    break;
	    }
	}
	r++;
    }
    fclose(f);
    return self;
}

- setFilename:(const char *)f
{
    filename = malloc(strlen(f) + 1);
    strcpy (filename,f);
    return self;
}

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

//! NOT FINISHED - should be able to archive to a .board file
- writeToFile:(const char *)filename
{
    return self;
}

// board utilities

- clearBoard
{
    int i;
    for (i = 0; i < maxCol * maxRow; i++)
	board[i] = SQUARE_EMPTY;
    numone = numtwo = 0;
    return self;
}

// used by the game over code to fill all empty squares with a given piece
- fillEmptyWith:(square_state)type
{
    int i, c;
    for (i = 0, c = 0; i < maxCol * maxRow; i++)
	if (board[i] == SQUARE_EMPTY) {
	    board[i] = type;
	    c++;
	}
    if (type == SQUARE_ONE)
    	numone += c;
    else if (type == SQUARE_TWO)
	numtwo += c;
    return self;
}

- trueCopy
{
    int i, c;
    square_state *from, *to;
    Board *newb = [[Board alloc] initCols:maxCol Rows:maxRow];
    from = board;
    to = newb->board;
    c = maxCol * maxRow;
    for (i = 0; i < c; i++)
    	*to++ = *from++;
    newb->numone = numone;
    newb->numtwo = numtwo; 
    newb->currentMove = currentMove;
    [newb setFilename:filename];
    return newb;	
}


// individually addressing board squares

// we keep track of how many pieces there are on a board
// This is very useful to most of the computer players that use a greedy-like
// strategy.  It speeds things up quite a bit.
- setPiece:(square_state)value at:(int)col :(int)row
{
    int i;
    if ((col >= maxCol) || (row >= maxRow) || (col < 0) || (row < 0))
	return self;
    i = col + (row * maxCol);
    switch (value) {
    	case SQUARE_ONE:
	    numone++;
	    break;
	case SQUARE_TWO:
	    numtwo++;
	    break;
	default:
	    break;
    }
    switch (board[i]) {
    	case SQUARE_ONE:
	    numone--;
	    break;
	case SQUARE_TWO:
	    numtwo--;
	    break;
	default:
	    break;
    }

    board[i] = value;
    return self;
}

- setPiece:(square_state)value at:(location *)loc
{
    return [self setPiece:value at:loc->col :loc->row];
}

- clearPieceAt:(int)col :(int)row
{
    return self;
}

- (square_state)pieceAt:(int)col :(int)row
{
    if ((col >= maxCol) || (row >= maxRow) || (col < 0) || (row < 0))
	return SQUARE_ERROR;
    return board[col + (row * maxCol)];
}

- (square_state)pieceAt:(location *)loc
{
    if ((loc->col >= maxCol) || (loc->row >= maxRow) || 
    	(loc->col < 0) || (loc->row < 0))
	return SQUARE_ERROR;
    return board[loc->col + (loc->row * maxCol)];
}


// querying other board information

- (int)rows
{
    return maxRow;
}

- (int)cols
{
    return maxCol;
}

- (int)numberOfPiece:(square_state)type
{
    unsigned int count = 0, i;
    switch (type) {
    	case SQUARE_ONE:
	    return numone;
	    break;
	case SQUARE_TWO:
	    return numtwo;
	    break;
	default:
	    for (i = 0; i < maxCol * maxRow; i++) {
		if (board[i] == type)
		    count++;
	    }
	    break;
    }
    return count;
}

// return an array of all of the pieces of a given type (their locations)
- (location *)pieces:(square_state)type
{
    int count, current, i;
    location *pieces;
    count = [self numberOfPiece:type];
    pieces = malloc(sizeof(location) * count);
    current = 0;
    for (i = 0; i < maxCol * maxRow; i++) {
	if (board[i] == type) {
	    pieces[current].col = (i % maxCol);
	    pieces[current].row = (i / maxCol);
	    current++;
	}
    }
    return pieces;
}

// keep track of the last move
- setCurrentMove:(move *)mv
{
    currentMove = *mv;
    return self;
}

- (move *)currentMove
{
    return &currentMove;
}

// keep track of a 'score' or rating for the board
- (int)score
{
    return score;
}

- setScore:(int)s
{
    score = s;
    return self;
}

/*
 * Transport protocol
 */

- encodeRemotelyFor:(NXConnection *)connection
	freeAfterEncoding:(BOOL *)flagp
	isBycopy:(BOOL)isBycopy
{
    if (isBycopy)
	return self;
    else
	return [super encodeRemotelyFor:connection freeAfterEncoding:flagp 
	    isBycopy:NO];
}

- encodeUsing:(id <NXEncoding>)portal 
{
    // char buf[10];
    int count, i, val;
    
    // send the count first, so that the destination can allocate enough
    count = (maxRow + 1) * (maxCol + 1);
    [portal encodeData:&count ofType:"i"];

    // can't send an enum across the wire, so we send it as a series of ints
    for (i = 0; i < count; i++) {
	val = board[i];
	[portal encodeData:&val ofType:"i"];
    }
    [portal encodeData:&maxRow ofType:"i"];
    [portal encodeData:&maxCol ofType:"i"];

    // can't send nested structs, so we send them individually
    [portal encodeData:&currentMove.from.col ofType:"i"];
    [portal encodeData:&currentMove.from.row ofType:"i"];
    [portal encodeData:&currentMove.to.col ofType:"i"];
    [portal encodeData:&currentMove.to.row ofType:"i"];
    
    [portal encodeData:&numone ofType:"i"];
    [portal encodeData:&numtwo ofType:"i"];
    [portal encodeData:&score ofType:"i"];
    [portal encodeData:&filename ofType:"*"];
    return self;
}

- decodeUsing:(id <NXDecoding>)portal
{
    // char buf[10];
    int count, i, val;

    // find out how many squares there are, and allocate accordingly
    [portal decodeData:&count ofType:"i"];
    board = malloc(sizeof(int *) * count);

    // cast these ints into square_states
    for (i = 0; i < count; i++) {
	[portal decodeData:&val ofType:"i"];
	board[i] = val;
    }
    
    [portal decodeData:&maxRow ofType:"i"];
    [portal decodeData:&maxCol ofType:"i"];
    [portal decodeData:&currentMove.from.col ofType:"i"];
    [portal decodeData:&currentMove.from.row ofType:"i"];
    [portal decodeData:&currentMove.to.col ofType:"i"];
    [portal decodeData:&currentMove.to.row ofType:"i"];
    [portal decodeData:&numone ofType:"i"];
    [portal decodeData:&numtwo ofType:"i"];
    [portal decodeData:&score ofType:"i"];
    [portal decodeData:&filename ofType:"*"];
    return self;
}


@end
