//#import <AppKit/AppKit.h>

#import <AppKit/NSImage.h>
#import <AppKit/NSPrintInfo.h>
#import <AppKit/NSBitmapImageRep.h>
#import <AppKit/NSWindow.h>
#import <AppKit/NSEvent.h>
#import <AppKit/NSGraphics.h>		// NSBeep
#import <Foundation/NSString.h>
#import <Foundation/NSGeometry.h>	// NSZeroPoint
#import <Foundation/NSUtilities.h>	// MAX, ABS
#import <Foundation/NSException.h>

#include <math.h>

// own interface
#import "Board.h"

// messaging objects
#import "Square.h"
#import "Chess.h"			// NSApp

// portability layer
#import "gnuglue.h"			// floor_value

// square colors
#define BLACK_SQUARE_COLOR  (0.5)
#define WHITE_SQUARE_COLOR  (5.0 / 6.0)

// private functions

static NSString  *whitePiece( p )
short  p;
{
    switch( p ) {
	case PAWN:	return @"white_pawn";
	case ROOK:	return @"white_rook";
	case KNIGHT:	return @"white_knight";
	case BISHOP:	return @"white_bishop";
	case KING:	return @"white_king";
	case QUEEN:	return @"white_queen";
	default:	break;
    }
    return nil;
}

static NSString  *blackPiece( p )
short  p;
{
    switch( p ) {
	case PAWN:	return @"black_pawn";
	case ROOK:	return @"black_rook";
	case KNIGHT:	return @"black_knight";
	case BISHOP:	return @"black_bishop";
	case KING:	return @"black_king";
	case QUEEN:	return @"black_queen";
	default:	break;
    }
    return nil;
}

// Board implementations

@implementation Board

- (id)initWithFrame: (NSRect) f
{
    self = [super initWithFrame: f];
    if( self ) {
	int r, c;
	NSSize size;

	[self allocateGState];
	size.width  = f.size.width  / 8.0;
	size.height = f.size.height / 8.0;
	backBitmap = [[NSImage alloc] initWithSize: size];

	for( r = 0; r < 8; r++ ) {
            for( c = 0; c < 8; c++ ) {
		Square  *aSquare;
		BOOL  even;
		float  bk;
		aSquare = [[Square alloc] init];
		even = ( ! ((r + c) % 2) );
		bk = ( even ) ? BLACK_SQUARE_COLOR : WHITE_SQUARE_COLOR;
		[aSquare setBackground: bk];
		square[r][c] = aSquare;
	    }
	}
	[self setupPieces];
	return self;
    }
    return nil;
}

- (void) setupPieces
{
    short  *pieces = default_pieces();
    short  *colors = default_colors();
    [self layoutBoard: pieces color: colors];
    return;
}

- (void) layoutBoard: (short *)p color: (short *)c
{
    int  sq;
    for( sq = 0; sq < SQUARE_COUNT; sq++ ) {
	int  row = sq / 8;
	int  col = sq % 8;
	[self placePiece: p[sq] at: row: col color: c[sq]];
    }
    return;
}

- (void) placePiece:  (short)p at: (int)row : (int)col color: (short)c
{
    Square    *theSquare = square[row][col];
    NSString  *piece = ( c == WHITE ) ? whitePiece( p ) : blackPiece( p );
    NSImage   *image = ( piece ) ? [NSImage imageNamed: piece] : nil;
    [theSquare setImage: image];
    return;
}

- (void) slidePieceFrom: (int)row1 : (int)col1 to: (int)row2 : (int)col2
{
    Square  *theSquare;
    NSString *icon;
    int  controlGState;
    NSRect  pieceRect;
    NSPoint  backP, endP, winP, roundedBackP;
    float  incX, incY;
    int  i, increments;

    theSquare = square[row1][col1];
    //icon = [[theSquare image] name];
    icon = [theSquare imageName];
    if( [icon isEqual: @""] )
	icon = nil;		// ?
    if( ! icon )
	return;
    controlGState = [self gState];

    pieceRect.size.width  = (float)floor_value( (double)([self frame].size.width  / 8.0) );
    pieceRect.size.height = (float)floor_value( (double)([self frame].size.height / 8.0) );

    backP.x = ( (float)col1 * pieceRect.size.width  );
    backP.y = ( (float)row1 * pieceRect.size.height );
    endP.x  = ( (float)col2 * pieceRect.size.width  );
    endP.y  = ( (float)row2 * pieceRect.size.height );

    [self lockFocus];
    PSgsave();
    
    /* Draw over the piece we are moving. */
    pieceRect.origin.x = col1 * pieceRect.size.width;
    pieceRect.origin.y = row1 * pieceRect.size.height;
    [theSquare drawBackground: pieceRect inView: self];

    /* Save background */ 
    [backBitmap lockFocus];
    PSgsave();
	roundedBackP.x = floor(backP.x); 
	roundedBackP.y = floor(backP.y);
	winP = [[self superview] convertPoint: roundedBackP fromView: self];
    PScomposite( winP.x, winP.y, pieceRect.size.width, pieceRect.size.height,
	controlGState, (float)0.0, (float)0.0, NSCompositeCopy );
    PSgrestore();
    [backBitmap unlockFocus];

    incX = endP.x - backP.x;
    incY = endP.y - backP.y;
    increments = (int) MAX( ABS(incX), ABS(incY) ) / 7;
    incX = incX / increments;
    incY = incY / increments;

    for( i = 0; i < increments; i++ ){

	/* Restore old background */
	[self lockFocus];
	[backBitmap compositeToPoint: backP operation: NSCompositeCopy];
	[self unlockFocus];
	[[self window] flushWindow];

	/* Save new background */
	backP.x += incX;
	backP.y += incY;

	[backBitmap lockFocus];
	PSgsave();
	roundedBackP.x = floor(backP.x); 
	roundedBackP.y = floor(backP.y);
	pieceRect.origin = roundedBackP;
	winP = [[self superview] convertPoint: roundedBackP fromView: self];
	PScomposite( winP.x, winP.y, pieceRect.size.width,
			pieceRect.size.height, controlGState, (float)0.0,
			(float)0.0, NSCompositeCopy );
	PSgrestore();
	[backBitmap unlockFocus];

	/* Draw piece at new location. */
	[theSquare drawInteriorWithFrame: pieceRect inView: self];
	[[self window] flushWindow];
	PSsetgray( NSBlack );
	PSsetlinewidth( (float)2.0 );
	PSclippath();
	PSstroke();
	[[self window] flushWindow];
    }
 
    PSgrestore();
    [self unlockFocus];
    return;
}

- (int) pieceAt: (int)row : (int)col
{
    if( row >= 0 && col >= 0 ) {
	Square  *theSquare = square[row][col];
	return( [theSquare pieceType] );
    }
    return (int)NO_PIECE;
}

- (void) highlightSquareAt: (int)row : (int)col
{
    NSRect  cr;
    Square  *theSquare = square[row][col];

    [self lockFocus];
    cr.size.width  = [self frame].size.width  / 8.0;
    cr.size.height = [self frame].size.height / 8.0;
    cr.origin.x = col * cr.size.width;
    cr.origin.y = row * cr.size.height;

    [theSquare highlight: cr inView: self];

    PSsetgray( NSBlack );
    PSsetlinewidth( (float)2.0 );
    PSclippath();
    PSstroke();
    [[self window] flushWindow];

    [self unlockFocus];
    return;
}
    
- (void) unhighlightSquareAt: (int)row : (int)col
{
    return;
}
    
- (void) flashSquareAt: (int)row : (int)col
{
    return;
}

- (void) print: (id)sender
{
    NSPrintInfo	*pi = [NSPrintInfo sharedPrintInfo];
    NSSize ps = [pi paperSize];
    NSSize fs = [self frame].size;
    float hm = (ps.width  - fs.width)  / 2.0;
    float vm = (ps.height - fs.height) / 2.0;

    [pi setLeftMargin:   hm];
    [pi setRightMargin:  hm];
    [pi setTopMargin:    vm];
    [pi setBottomMargin: vm];

    [self lockFocus];
    printImage = [[NSBitmapImageRep alloc] initWithFocusedViewRect: [self bounds]];
    [self unlockFocus];

    [super print: sender];
    [printImage release];
    printImage = nil;
    return;
}

- (void) drawRect: (NSRect)f
{
    if( ! printImage ) {
	int r, c;
	NSRect cr;

	PSgsave();
	cr.size.width  = f.size.width  / 8.0;
	cr.size.height = f.size.height / 8.0;
	for( r = 0; r < 8; r++ ) {
	    cr.origin.y = r * cr.size.height;
	    for( c = 0; c < 8; c++ ) {
		Square  *theSquare = square[r][c];
		cr.origin.x = c * cr.size.width;
		[theSquare drawWithFrame: cr inView: self];
	    }
	}
	PSsetgray( NSBlack );
	PSsetlinewidth( (float)2.0 );
	PSclippath();
	PSstroke();
	PSgrestore();
    }
    else {
	[printImage draw];
    }
    return;
}

- (void) mouseDown: (NSEvent *)event 
{
    NSException	 *exception = nil;

    if ( [NSApp bothsides] ) {
	NSBeep();
    }
    else if( [NSApp finished] ) {
	[NSApp finishedAlert];
	[[self window] setAcceptsMouseMovedEvents: YES];

      NS_DURING
	while( [event type] != NSLeftMouseUp ) {
	    unsigned int mask = (NSLeftMouseUpMask | NSLeftMouseDraggedMask);
	    event = [[self window] nextEventMatchingMask: mask];
	}
      NS_HANDLER
	exception = localException;
      NS_ENDHANDLER
    }

    else {
	NSPoint  pickedP, backP, roundedBackP, winP;
	NSRect  pieceRect, backR;
	int  r, c;
	Square  *theSquare;
	int  controlGState;
	NSString  *icon;
        NSPoint  p;

	pickedP = [event locationInWindow];
	pickedP = [self convertPoint: pickedP fromView: nil];
	backP = pickedP;
	//pieceRect.origin = NSZeroPoint;
	pieceRect.size.width  = [self frame].size.width  / 8.0;
	pieceRect.size.height = [self frame].size.height / 8.0;
	r = floor_value( (double)(pickedP.y / pieceRect.size.height) ); 
	c = floor_value( (double)(pickedP.x / pieceRect.size.width)  );
	pieceRect.size.width =(float)floor_value((double)pieceRect.size.width);
	pieceRect.size.height=(float)floor_value((double)pieceRect.size.height); 
	backR.origin = NSZeroPoint;
	backR.size   = pieceRect.size;
	pickedP.x = pickedP.x - (((float)c) * pieceRect.size.width);
	pickedP.y = pickedP.y - (((float)r) * pieceRect.size.height);
	theSquare = square[r][c];
	//icon = [[theSquare image] name];
        icon = [theSquare imageName];
	if( [icon isEqual: @""] )
	    icon = nil;		// ?
	if( icon && [self isEnabled] ) {
	    controlGState = [self gState];
	    if( ! controlGState ) {
		[self allocateGState];
		controlGState = [self gState];
	    }
	    [self lockFocus];
	    PSgsave();

	    /* Draw over the piece we are moving. */
	    pieceRect.origin.x = c * pieceRect.size.width;
	    pieceRect.origin.y = r * pieceRect.size.height;
	    [theSquare drawBackground: pieceRect inView: self];

	    /* Save background */ 
	    [backBitmap lockFocus];
	    PSgsave();
		roundedBackP.x = floor(backP.x); 
		roundedBackP.y = floor(backP.y);
		winP = [[self superview] convertPoint: roundedBackP fromView: self];
	    PScomposite( winP.x, winP.y, backR.size.width, backR.size.height,
	      controlGState, (float)0.0, (float)0.0, NSCompositeCopy );
	    PSgrestore();
	    [backBitmap unlockFocus];
	} 
    
	[[self window] setAcceptsMouseMovedEvents: YES];

      NS_DURING
	while ([event type] != NSLeftMouseUp) {
	    unsigned int mask = (NSLeftMouseUpMask | NSLeftMouseDraggedMask);
	    event = [[self window] nextEventMatchingMask: mask];

	    p = [event locationInWindow];
	    p = [self convertPoint: p fromView: nil];

	    if( icon && [self isEnabled] ) {
		/* Restore old background */
		[self lockFocus];
		[backBitmap compositeToPoint:roundedBackP operation:NSCompositeCopy];
		[self unlockFocus];

		/* Save new background */
                backP.x = pieceRect.origin.x = p.x - pickedP.x;
		backP.y = pieceRect.origin.y = p.y - pickedP.y;
                
		[backBitmap lockFocus];
		PSgsave();
		roundedBackP.x = floor(backP.x); 
		roundedBackP.y = floor(backP.y);
		winP = [[self superview] convertPoint: roundedBackP fromView: self];
		PScomposite( winP.x, winP.y, backR.size.width,
					 backR.size.height, controlGState, (float)0.0,
					 (float)0.0, NSCompositeCopy );
		PSgrestore();
		[backBitmap unlockFocus];

		/* Draw piece at new location. */
		[theSquare drawInteriorWithFrame: pieceRect inView: self];

		PSsetgray( NSBlack );
		PSsetlinewidth( (float)2.0 );
		PSclippath();
		PSstroke();
		[[self window] flushWindow];
	    }
	}
      NS_HANDLER
	exception = localException;
      NS_ENDHANDLER

	if( icon && [self isEnabled] ) {
	    NSSize  frame = [self frame].size;
	    int  r2 = floor_value( (double)(p.y / (frame.height / 8.0)) ); 
	    int  c2 = floor_value( (double)(p.x / (frame.width  / 8.0)) );
	    if( r2 != r || c2 != c ) {
		if( ! [NSApp makeMoveFrom: r : c to: r2 : c2] ) {
		    PSWait();
		}
            }
            [self display]; 
	    PSgrestore();
	    [self unlockFocus];
	}
    }

    if( exception )
	[exception raise];
    return;
}

@end

// Local Variables:
// tab-width: 8
// End:
