// ScoreBoard.m
// Project: Cribbage
// Stephan Wacker
// 93-09-21


#import "ScoreBoard.h"
#import "ScoreGoal.h"

#import <assert.h>

// Number of pegs in a row and in a group.
#define LEN		30	// number of pegs in a row
#define GRP		5	// number of pegs in a group
#define DIST		0.5	// additional distance between groups of pegs

#define TOTAL_LEN	(2 + LEN + (LEN/GRP)*DIST)	// == 35
	// width of board expressed in units of one peg field width

@implementation ScoreBoard


- (NXColor) color
{
    [self subclassResponsibility: _cmd];
    return NX_COLORCLEAR;
}


- (BOOL) topRowFirst
{
    [self subclassResponsibility: _cmd];
    return NO;
}


- makePegImage: (NXColor) color
//
// Create the peg image from a shape image, a color and a mask image.
//
{
    NXImage	*shape = [NXImage findImageNamed: "pegShape"];
    NXImage	*mask = [NXImage findImageNamed: "pegMask"];
    NXRect	rect;

    // Create a new image having the same size as the shape.
    [shape getSize: &rect.size];
    rect.origin.x = rect.origin.y = 0;
    if( !pegImage ) {
	pegImage = [[NXImage alloc] initSize: &rect.size];
	[pegImage useCacheWithDepth: NX_TwelveBitRGBDepth];
    }
    
    // Draw into the pegImage.
    [pegImage lockFocus];
    
    // Fill it with the given color.
    NXSetColor( color );
    NXRectFill( &rect );
    
    // Composite the shape over it; the shape's transparency will determine
    // where the color is visible.
    [shape composite: NX_SOVER toPoint: &rect.origin];
    
    // Composite the mask over it; the pegImage will take on the transparency
    // of the mask.
    [mask composite: NX_DIN toPoint: &rect.origin];
    
    [pegImage unlockFocus];
    
    return self;
}


- initFrame: (const NXRect *) frameRect
{
    [super initFrame: frameRect];
    [self setOpaque: NO];
    
    [self makePegImage: [self color]];
    
    games = -1;		// signals first game
    
    return self;
}


- changePegColor
{
    [self makePegImage: [self color]];
    [window disableFlushWindow];
    [self lockFocus];
    [self putPeg: prevScore];
    [self putPeg: score];
    [self unlockFocus];
    [window reenableFlushWindow];
    [window flushWindowIfNeeded];
    
    return self;
}


- clear
{
    [self lockFocus];
    
    if( games < 0 ) {
	games = 0;
	[gamesField setIntValue: games];
    } else {
	// There is a problem when after a long game the game limit is
	// reduced to a short game.  [self limit] will be less than score
	// and the pegs are not removed correctly.  The solution is to
	// make sure that all scores are less than [self limit].
	while( prevScore > 2*LEN ) prevScore -= 2*LEN;
	while(     score > 2*LEN )     score -= 2*LEN;
	
	[self removePeg: prevScore];
	[self removePeg: score];
	[self removePeg: [self limit]];		// winner's peg
    }
    
    score = 0, prevScore = -1;
    
    [self putPeg: prevScore];
    [self putPeg: score];
    
    [self unlockFocus];
    
    return self;
}


- drawBackground
{
    NXRect	rect = bounds;
    [self convertRect: &rect toView: backgroundView];
    
    // Display background behind the rect we will composite over.
    [backgroundView display: &rect : 1 : YES];
    return self;
}


- drawDecoration
{
    NXImage	*decoration = [NXImage findImageNamed: "boardDecoration"];
    NXRect	rect;
    
    [backgroundView getBounds: &rect];
    if( [backgroundView isFlipped] ) {
	rect.origin.y += rect.size.height;
    }
    
    [backgroundView lockFocus];
    // [decoration composite: NX_SOVER toPoint: &rect.origin];
    [decoration dissolve: 50 toPoint: &rect.origin];
    [backgroundView unlockFocus];
    
    return self;
}


- drawSelf: (const NXRect *) rects: (int) rectCount
//
// Draw the pegging board.
//
{
    int		i;
    
    [self drawBackground];
    // [self drawDecoration];
    
    for( i = -1; i <= 2*LEN; i++ ) {
	[self removePeg: i];
    }
    [self removePeg: [self limit]];
    
    [self putPeg: prevScore];
    [self putPeg: score];
    
    return self;
}


- (int) limit
{
    return [goalView limit];
}


- (int) score
{
    return score;
}


- peg: (int) points
//
// Return non-nil iff game is won.
//
{
    id		result = nil;
    
    if( points > 0 ) {
	[self lockFocus];
	[self removePeg: prevScore];
	prevScore = score;
	score += points;
	if( score >= [self limit] ) {
	    score = [self limit];
	    result = self;
	}
	[self putPeg: score];
	[self unlockFocus];
    }
    
    return result;
}


- (NXPoint) pegCoords: (int) pos
//
// Return center coordinates of peg number pos.
// All peg images should have the same size: 11x11.
//
{
    NXPoint	result;
    NXRect	rect = bounds;
    int		x, y;
    
    // Determine x and y coordinates of the peg.
    //
    x = pos, y = 0;
    while( x > 2*LEN ) {
	x -= 2*LEN;
    }
    if( x > LEN ) {
	x = (2*LEN+1)-x, y = 1;
    }
    if( [self topRowFirst] ) y = 1-y;
    assert( -1 <= x && x <= LEN );
    
    // Partition total width and set x coordinate of peg's rect.
    //
    rect.size.width /= TOTAL_LEN;
    rect.origin.x += (x+1 + ((x+GRP-1)/GRP) * DIST) * rect.size.width;
    
    // Put peg's rect at top or bottom of view and make it square.
    //
    if( y == 1 ) {
	rect.origin.y = rect.size.height - rect.size.width;
    }
    rect.size.height = rect.size.width;
    
    // Find center of rect.
    //
    result.x = NX_MIDX(&rect);
    result.y = NX_MIDY(&rect);
    
    return result;
}


- putImage: (NXImage *) image at: (int) pos
//
// All peg images should have the same size.
// Pegs are drawn as transparent images over an opaque background object.
// The last peg is drawn in the goalView.
//
{
    NXRect	rect, rect2;
    id		drawingView = self;
    
    if( pos >= [self limit] ) {
	[(drawingView = goalView) lockFocus];
	rect.origin = [goalView pegCoords];
    } else {
	rect.origin = [self pegCoords: pos];
    }
    
    
    [image getSize: &rect.size];
    rect.origin.x -= rect.size.width / 2;
    rect.origin.y -= rect.size.height / 2;
    
    // Convert to coordinate system of the background.
    rect2 = rect;
    [drawingView convertRect: &rect2 toView: backgroundView];
    
    // Display background behind the rect we will composite over.
    [backgroundView display: &rect2 : 1 : YES];
    
    // Display the peg image.
    [image composite: NX_SOVER toPoint: &rect.origin];

    
    if( pos >= [self limit] ) {
	[goalView unlockFocus];
    }
    
    
    return self;
}


- removePeg: (int) pos
{
    return [self putImage: [NXImage findImageNamed: "pegHole"] at: pos];
}


- putPeg: (int) pos
{
    [scoreField setIntValue: score];
    return [self putImage: pegImage at: pos];
}


- win: (int) gameValue
{
    games += gameValue;
    [gamesField setIntValue: games];
    return self;
}


@end
