/*
 * This is the view from which NextMatrix and TetMatrix inherit.
 *
 *  The major data structures are.
 *   id *iconMatrix;  Holds the id of the block that is in the current
 *                    (row, column).
 */
#import <appkit/NXImage.h>
#import <appkit/nextstd.h>
#import <dpsclient/psops.h>
#import "Minimatrix.h"

@implementation Minimatrix

- initFrame:(const NXRect *)frameRect
{
	return [self initFrame:frameRect bitmap:nil numRows:0 numCols:0];
}

- initFrame:(const NXRect *)frameRect
            numRows:(int)rowsHigh numCols:(int)colsWide
{
	return [self initFrame:frameRect
			bitmap:nil
			numRows:rowsHigh numCols:colsWide];
}

- initFrame:(const NXRect *)frameRect
       bitmap:theBitmap numRows
		:(int)rowsHigh numCols
		:(int)colsWide
{
	[super initFrame:frameRect];

	numRows = rowsHigh;
	numCols = colsWide;
	NX_MALLOC(iconMatrix, id, numRows * numCols);
	backgroundGray = NX_LTGRAY;
	inset.width = inset.height = 0.0;
	insetBounds = bounds;
	intercell = elementSize = inset;

	[self setBitmap:theBitmap];
	return self;
}

/*
 * Return the id of the bitmap at (row,column) if one exists, else nil.
 */
- bitmapAt:(int)row :(int)column
{
	return (row < 0 || column < 0 || row >= numRows || column >= numCols) ?
	  nil : *(iconMatrix + row * numCols + column);
}

- displayAt:(int)row :(int)column
{
	NXRect destRect;

	NXSetRect(&destRect,
				 inset.width + column * (intercell.width + elementSize.width),
				 inset.height + row * (intercell.width + elementSize.width),
				 elementSize.width + intercell.width,
				 elementSize.height + intercell.height);

	return [self display:&destRect :1];
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
	int rowc, colc;
	int rectc;
	NXRect destRect;
	id *iconMatrixPiece;
	const NXRect *rectp;
	static int bsides[] = {NX_YMIN, NX_XMAX, NX_YMAX, NX_XMIN,
								  NX_YMIN, NX_XMAX, NX_YMAX, NX_XMIN};

	static float bgrays[] = {NX_WHITE, NX_WHITE, NX_DKGRAY, NX_DKGRAY,
									 NX_LTGRAY, NX_WHITE, NX_DKGRAY, NX_DKGRAY};

	PSsetgray(backgroundGray);
	NXRectFillList(rects, rectCount); // Clear old areas

	destRect = bounds;

	// Draw the insetRect around the border of the game when a Piece
	// intersects with the border.

	if ((inset.width || inset.height) && !NXContainsRect(&insetBounds, rects)) {
		NXDrawTiledRects(&destRect, (NXRect *)0, bsides, bgrays, 8);
	}

	NXSetRect(&destRect, inset.width + intercell.width,
				 inset.height + intercell.height,
				 elementSize.width, elementSize.height);

	iconMatrixPiece = iconMatrix;

	// Redraw all of the Pieces that have changed
	for (rowc = 0; rowc < numRows; rowc++) {
    	for (colc = 0; colc < numCols; colc++) {
			if (*iconMatrixPiece) {
				rectc = rectCount;
				rectp = rects;
				while (rectc--) {
					if (NXIntersectsRect(&destRect, rectp++)) {
						// If we assume the piece contains no alpha then we could use
						// NX_COPY
						[*iconMatrixPiece composite:NX_SOVER toPoint:&destRect.origin];
						break;
					}
				}
			}
			// Add block's width + space b/w each block to get new X coord
			destRect.origin.x += elementSize.width + intercell.width;
			iconMatrixPiece++;	  // Do next column in iconMatrix
		}
		destRect.origin.x = inset.width + intercell.width;

		// Add the block's height + intercell.height
		destRect.origin.y += elementSize.height + intercell.height;
	}
	return self;
}

/*
 * Private method.
 */
- getIntercell:(NXSize *)aSize
{
	*aSize = intercell;
	return self;
}

- getRect:(NXRect *)theRect for:(int)row :(int)column
{
	theRect->size = elementSize; // Get block's image size
	return [self point:&theRect->origin for:row :column];
}

/*
 * Calculate the TetrisView coordinate given the Tetrix (row,column)
 */
- point:(NXPoint *)thePoint for:(int)row :(int)column
{
	thePoint->x = inset.width + (column + 1) * intercell.width +
	  column * elementSize.width;
	thePoint->y = inset.height + (row + 1) * intercell.height +
	  row * elementSize.height;
	return self;
}


- setBackgroundGray:(float)gray
{
	backgroundGray = gray;
	return self;
}

- setBitmap:theBitmap
{
	id *iconMatrixPiece;

#ifdef DEBUG
	printf("setBitmap\n");
#endif

	iconMatrixPiece = iconMatrix + numRows * numCols;
	while (iconMatrixPiece > iconMatrix)
	  *--iconMatrixPiece = theBitmap;
	
	if (theBitmap)
	  [theBitmap getSize:&elementSize];
	return self;
}
/*
 * Set the current (row, column) of the iconMatrix equal to the id of
 * the block that was just dropped.
 *
 * There is a bug in this program.  Occasionally, an invalid row
 * is generated.
 *
 * theBitmap row, col = (24, 6)
 * theBitmap row, col = (23, 6)
 * theBitmap row, col = (23, 7)
 * theBitmap row, col = (25, 3)
 * theBitmap row, col = (698590, 3)
 */
- setBitmap:theBitmap at:(int)row :(int)column
{
	if (row < numRows && row >= 0 && column < numCols && column >= 0) {
		*(iconMatrix + row * numCols + column) = theBitmap;
	} else {
		fprintf(stderr, "Invalid row %d in Minimatrix::setBitmap\n", row);
	}
	return self;
}


/*
 *  The size of the image (TIFF or PS) we are using to build the block.
 *  Currently 16x16 is the image size.
 */
- setElementSize:(const NXSize *)aSize
{
	elementSize = *aSize;
	return self;
}

/*
 * Set the number of pixels away from the border the pieces will be.
 */
- setInset:(const NXSize *)aSize
{
	inset = *aSize;
	insetBounds = bounds;
	NXInsetRect(&insetBounds, inset.width, inset.height);
	return self;
}

/*
 * Set the amount of space to leave between each cell.
 */
- setIntercell:(const NXSize *)aSize
{
	intercell = *aSize;
	return self;
}

- free
{
	NX_FREE(iconMatrix);
	return [super free];
}

@end

