/*
 * SplatView.m
 * Description
 * 	subclass of BoardView specific to the game "Splat"
 * History
 * 	14-Feb-93  Erik Kay  Created
 *	15-Mar-93  Erik Kay  added sound
 */

#import "SplatView.h"
#import "SplatApp.h"
#import "SplatGame.h"
#import "SplatDefaults.h"

extern void animateMove (DPSTimedEntry te, double now, void *data);
extern void animateChange (DPSTimedEntry te, double now, void *data);

@implementation SplatView

- initFrame:(const NXRect *)r
{
    NXSize sz;
    [super initFrame:r];
    
    /* image initialization */
    obstructionImg = [[NXImage findImageNamed:"obstruction"] setScalable:YES];
    one = [[NXImage findImageNamed:"one"] setScalable:YES];
    two = [[NXImage findImageNamed:"two"] setScalable:YES];
    numAnimationFrames = 9;
    currentFrame = -1;

    [obstructionImg getSize:&sz];
    pieceSize = sz.width;
    squareSize = pieceSize + 4;
    
    /* sound initialization */
    soundManager = [NXApp soundManager];
    return self;
}

- drawHighlightedSquare:(NXRect *)rect at:(int)col :(int)row
{
    NXRect r;
    NX_WIDTH(rect) -= 4;
    NX_HEIGHT(rect) -= 4;
    NX_X(rect) += 2;
    NX_Y(rect) += 2;
    
    NXSetRect(&r,(currentFrame % 3) * pieceSize, 
    	((8-currentFrame) / 3) * pieceSize, pieceSize, pieceSize);
    if (currentPlayer == SQUARE_ONE)
	[one composite:NX_SOVER fromRect:&r toPoint:&rect->origin];
    else
	[two composite:NX_SOVER fromRect:&r toPoint:&rect->origin];
    currentFrame++;
    return self;
}

- drawObstruction:(NXRect *)r at:(int)col :(int)row
{
    NX_WIDTH(r) -= 4;
    NX_HEIGHT(r) -= 4;
    NX_X(r) += 2;
    NX_Y(r) += 2;
    [obstructionImg composite:NX_SOVER toPoint:&r->origin];
    return self;	
}

- drawPlayerOne:(NXRect *)r at:(int)col :(int)row
{
    NXRect rect;
    NX_WIDTH(r) -= 4;
    NX_HEIGHT(r) -= 4;
    NX_X(r) += 2;
    NX_Y(r) += 2;
    if ((moving && ((selectedCol == col) && (selectedRow == row))) ||
    	(changing))
    {
	NXSetRect(&rect,((8-currentFrame) % 3) * pieceSize, 
		((currentFrame) / 3) * pieceSize, pieceSize, pieceSize);
	[one composite:NX_SOVER fromRect:&rect toPoint:&r->origin];
    } else {
	if (!((col == currentMove.from.col) && (row == currentMove.from.row) &&
		(selectedCol == -1) && moving))
	{
	    NXSetRect(&rect,pieceSize * 2, 0, pieceSize, pieceSize);
	    [one composite:NX_SOVER fromRect:&rect toPoint:&r->origin];
	}
    }
    return self;	
}

- drawPlayerTwo:(NXRect *)r at:(int)col :(int)row
{
    NXRect rect;
    NX_WIDTH(r) -= 4;
    NX_HEIGHT(r) -= 4;
    NX_X(r) += 2;
    NX_Y(r) += 2;
    if ((moving && ((selectedCol == col) && (selectedRow == row))) ||
    	(changing))
    {
	NXSetRect(&rect,((8-currentFrame) % 3) * pieceSize, 
		((currentFrame) / 3) * pieceSize, pieceSize, pieceSize);
	[two composite:NX_SOVER fromRect:&rect toPoint:&r->origin];
    } else {
	if (!((col == currentMove.from.col) && (row == currentMove.from.row) &&
		(selectedCol == -1) && moving))
	{
	    NXSetRect(&rect, pieceSize * 2, 0, pieceSize, pieceSize);
	    [two composite:NX_SOVER fromRect:&rect toPoint:&r->origin];
	}
    }
    return self;	
}

- drawSelf:(const NXRect *)rects :(int)count
{
    int tmpFrame;
    if (resized) {
	tmpFrame = currentFrame;
	currentFrame = 0;
	[super drawSelf:rects :count];
	resized = NO;
	currentFrame = tmpFrame;
    } else
	[super drawSelf:rects :count];
    return self;
}

- doMoveAnimation:(move *)mv
{
    if (abortGame) {
    	if (moveEntry) {
	    DPSRemoveTimedEntry(moveEntry);
	    moveEntry = NULL;
	}
	abortGame = NO;
    }
    currentMove = *mv;
    board->currentMove = *mv;
    if ((abs(currentMove.from.col - currentMove.to.col) > 1) ||
	(abs(currentMove.from.row - currentMove.to.row) > 1))
	moving = YES;
    [self selectSquare:mv->from.col :mv->from.row];
    moveEntry = DPSAddTimedEntry(0.02,animateMove,(void *)self,NX_BASETHRESHOLD);
    currentFrame = 0;
    currentPlayer = [board pieceAt:mv->from.col :mv->from.row];
    [self display];
    [self playSound:MOVE_SOUND atCol:mv->to.col];
    return self;
}

- doAnimateMove
{
    if (highlightedCol == -1) {
	highlightedCol = currentMove.to.col;
	highlightedRow = currentMove.to.row;
    }
    [boardCopy setPiece:SQUARE_ERROR at:highlightedCol :highlightedRow];
    [self display];
    return self;
}

- finishChangeAnimation
{
    changing = NO;
    currentFrame = -1;
    [boardDiffs free];
    boardDiffs = nil;
    [board free];
    board = newBoard;
    newBoard = nil;
    [self display];
    [super finishChangeAnimation];
    return self;
}

- finishMoveAnimation
{
    currentFrame = -1;
    [super finishMoveAnimation];
    moving = NO;
    return self;
}

- doChangeAnimation:(Board *)orig
{
    move *mv;
    int r, c, diffcount;
    BOOL doit = NO;

    boardDiffs = [board trueCopy];
    [boardDiffs clearBoard];
    newBoard = board;
    board = [orig trueCopy];
    for (c = 0; c < cols; c++) {
    	for (r = 0; r < rows; r++) {
	    if (([board pieceAt:c :r] != [newBoard pieceAt:c :r]) &&
	    	([board pieceAt:c :r] != SQUARE_EMPTY) &&
		([newBoard pieceAt:c :r] != SQUARE_EMPTY)) {
		    doit = YES;
		    [boardDiffs setPiece:[newBoard pieceAt:c :r] at:c :r];
		}
	}
    }
    if (doit) {
	changeEntry = DPSAddTimedEntry(0.001,animateChange,(void *)self,NX_BASETHRESHOLD);
	currentFrame = 0;
	changing = YES;
	mv = [orig currentMove];
	currentPlayer = [board pieceAt:mv->from.col :mv->from.row];
	diffcount = [boardDiffs numberOfPiece:SQUARE_ONE] +
		    [boardDiffs numberOfPiece:SQUARE_TWO];
	if (diffcount >= 7)
	    [self playSound:WOWCHANGE_SOUND atCol:mv->to.col];
	else if (diffcount >= 4)
	    [self playSound:BIGCHANGE_SOUND atCol:mv->to.col];
	else
	    [self playSound:CHANGE_SOUND atCol:mv->to.col];
    }
    else
	[self finishChangeAnimation];
    return self;
}

- doAnimateChange
{
    int r, c;
    NXRect rect;
    [self lockFocus];
    for (c = 0; c < cols; c++)
    	for (r = 0; r < rows; r++) {
	    if ([boardDiffs pieceAt:c :r] == SQUARE_EMPTY)
		continue;
	    NXSetRect(&rect,c * squareSize, r * squareSize, 
	    		squareSize, squareSize);
	    [self drawEmptySquare:&rect at:c :r];
	    switch ([boardDiffs pieceAt:c :r]) {
		case SQUARE_ONE:
		    if (direction == -1)
			[self drawPlayerOne:&rect at:c :r];
		    else
			[self drawPlayerTwo:&rect at:c :r];
		    break;
		case SQUARE_TWO:
		    if (direction == -1)
			[self drawPlayerTwo:&rect at:c :r];
		    else
			[self drawPlayerOne:&rect at:c :r];
		    break;
		case SQUARE_BLOCKED:
		case SQUARE_EMPTY:
		default:
		    break;
	    }
	}
    [self unlockFocus];
    currentFrame += direction;
    [window flushWindow];
    NXPing();
    return self;
}

- scaleImages
{
    NXSize sz;
    int c, r;
    
    if (scalable) {
    	[obstructionImg getSize:&sz];
	if (sz.width == pieceSize)
	    return self;
	if (obstructionImg != [NXImage findImageNamed:"obstruction"]) {
	    [obstructionImg free];
	    [one free];
	    [two free];
	    obstructionImg = [NXImage findImageNamed:"obstruction"];
	    one = [NXImage findImageNamed:"one"];
	    two = [NXImage findImageNamed:"two"];
	}
	sz.width = sz.height = pieceSize;
	obstructionImg = [obstructionImg copy];
	one = [one copy];
	two = [two copy];
	[obstructionImg setSize:&sz];
	[one getSize:&sz];
	c = sz.width / 48.0;
	r = sz.height / 48.0;
	sz.width = c * pieceSize;
	sz.height = r * pieceSize;
	[one setSize:&sz];
	[two getSize:&sz];
	c = sz.width / 48.0;
	r = sz.height / 48.0;
	sz.width = c * pieceSize;
	sz.height = r * pieceSize;
	[two setSize:&sz];
    }
    return self;
}

- setBoard:(Board *)b
{
    [window disableFlushWindow];
    squareSize = (int)[[NXApp defaultsManager] squareSize];
    pieceSize = squareSize - 4;
    [super setBoard:b];
    moving = changing = NO;
    currentFrame = 0;
    if (squareSize != 52)
	scalable = YES;
    if (scalable)
	[self scaleImages];
    if (boardCopy) {
	[boardCopy free];
	boardCopy = nil;
    }
    [window display];
    [window reenableFlushWindow];
    [window flushWindow];
    return self;
}

- windowDidResize:sender
{
    scalable = YES;
    squareSize = NX_HEIGHT(&frame) / rows; // both width and height should work
    [[NXApp defaultsManager] setSquareSize:squareSize];
    pieceSize = squareSize - 4;
    [self scaleImages];
    [boardCopy free];
    boardCopy = nil;
    resized = YES;
    [self display];
    [window flushWindow];
    return self;
}

- windowWillResize:sender toSize:(NXSize *)newSize
{
    NXRect winR;
    NXSize curSize, minSize;
    float dx, dy, ratio;
    
    ratio = (float)rows / cols;
    [window getFrame:&winR];
    curSize = winR.size;
    dx = newSize->width - curSize.width;
    dy = newSize->height - curSize.height;
    if (abs(dx) > abs(dy)) {
	dy = dx * ratio;
    } else if (abs(dy) > abs(dx)) {
    	dx = dy / ratio;
    } else {
	if (rows > cols)
	    dx = dy / ratio;
	else if (rows < cols)
	    dy = dx / ratio;
    }
    newSize->width = curSize.width + dx;
    newSize->height = curSize.height + dy;
    [window getMinSize:&minSize];
    if (newSize->width < minSize.width) {
	dx = minSize.width - newSize->width;
	dy = dx * ratio;
	newSize->width = minSize.width;
	newSize->height += dy;
    }
    if (newSize->height < minSize.height) {
	dy = minSize.height - newSize->height;
	dx = dy / ratio;
	newSize->height = minSize.height;
	newSize->width += dx;
    }
    return self;
}

- windowWillClose:sender
{
    id ret = [game windowWillClose:sender];
    if (ret)
	[game perform:@selector(free) with:nil 
		afterDelay:100 cancelPrevious:NO];
    return ret;
}

- gameOver
{
    [super gameOver];
    return self;
}

- playSound:(int)which atCol:(int)c
{
    float l, r;
    
    if ([[NXApp defaultsManager] playSounds]) {
	l = 1 - ((float)(c + 1)/(float)cols);
	r = 1 - l;
	[soundManager playSound:which  withGainLeft:l right:r];
    }
    return self;
}

@end
