//
//  MazeView.m
//  Rat
//
//  Created by Curtis Jones on 2009.11.10.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "MazeView.h"

@interface MazeView (Private)
- (void)sizeToFit;
- (void)drawSolution:(ratlibpoint_t*)solution inContext:(CGContextRef)context inRect:(NSRect)rect;
@end

@implementation MazeView

@synthesize session = mSession;
@synthesize target = mEventCallbackTarget;
@synthesize selector = mEventCallbackSelector;

#pragma mark -
#pragma mark structors

/**
 *
 *
 */
- (id)initWithFrame:(NSRect)frame
{
	self = [super initWithFrame:frame];
	
	if (self) {
		mInTargetMode = FALSE;
		mIsDirty = FALSE;
		
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doEventMazeBeginPointSet:) name:@"MazeBeginPointSet" object:nil];
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doEventMazeEndPointSet:) name:@"MazeEndPointSet" object:nil];
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doEventMazePointAdded:) name:@"MazePointAdded" object:nil];
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doEventSolveRoundCompleted:) name:@"SolveRoundCompleted" object:nil];
	}
	
	return self;
}

/**
 *
 *
 */
- (void)dealloc
{
	if (mImage != NULL) {
		CGImageRelease(mImage);
		mImage = NULL;
	}
	
	[super dealloc];
}





#pragma mark -
#pragma mark accessors

/**
 *
 *
 */
- (CGImageRef)image
{
	return mImage;
}

/**
 *
 *
 */
- (void)setImage:(CGImageRef)anImage
{
	if (mImage != NULL) {
		CGImageRelease(mImage);
		mImage = NULL;
	}
	
	mImage = anImage;
	CGImageRetain(mImage);
	
	[self sizeToFit];
}

- (void)sizeToFit
{
	NSLog(@"%s..", __PRETTY_FUNCTION__);
	
	if (mImage == nil)
		return;
	
	[self setFrame:CGRectMake(0., 0., CGImageGetWidth(mImage), CGImageGetHeight(mImage))];
	[self setBounds:CGRectMake(0., 0., CGImageGetWidth(mImage), CGImageGetHeight(mImage))];
	[[self enclosingScrollView] setNeedsDisplay:TRUE];
}





#pragma mark -
#pragma mark other stuff

- (void)setTargetCursor:(BOOL)aBool
{
	if (mInTargetMode != aBool) {
		mInTargetMode = aBool;
		[self.window invalidateCursorRectsForView:self];
	}
}





#pragma mark -
#pragma mark NSView

/**
 *
 *
 */
- (void)drawRect:(NSRect)dirtyRect
{
	if (mImage == nil)
		return;
	
	[self drawRect:dirtyRect inContext:[[NSGraphicsContext currentContext] graphicsPort]];
	
	mIsDirty = FALSE;
}

/**
 *
 *
 */
- (void)drawRect:(NSRect)dirtyRect inContext:(CGContextRef)context
{
	CGContextDrawImage(context, self.bounds, mImage);
	
	[self drawVisitedInContext:context inRect:dirtyRect];
	
	if (mSession->solution != NULL)
		[self drawSolution:mSession->solution inContext:context inRect:dirtyRect];
	
	// draw the begin marker if it is set
	if (mSession->begpoint != NULL)
		[self drawTargetInContext:context point:mSession->begpoint colorR:0 colorG:1 colorB:0 colorA:.5];
	
	// draw the end marker if it is set
	if (mSession->endpoint != NULL)
		[self drawTargetInContext:context point:mSession->endpoint colorR:1. colorG:0. colorB:0. colorA:.5];
}

/**
 *
 *
 */
- (void)drawSolution:(ratlibpoint_t*)solution inContext:(CGContextRef)context inRect:(NSRect)rect
{
	ratlibpoint_t *point = solution;
	
	CGContextSetRGBFillColor(context, 1, 0, 0, 1);
	
	do {
		CGContextFillRect(context, [self rectFromPoint:point width:1 height:1]);
	}
	while (NULL != (point = point->prev));
}

/**
 *
 *
 */
- (void)drawVisitedInContext:(CGContextRef)context inRect:(NSRect)rect
{
	CGContextSetRGBFillColor(context, 0, 0, 1, 1);
	
	ratlibgrid_t *grid = &mSession->grid;
	ratlibpoint_t *point = ratlib_point(0, 0);
	uint32_t width=0, height=0;
	
	point->x = rect.origin.x;
	point->y = mSession->image->height - (rect.origin.y + rect.size.height);
	
	while (1)
	{
		if (ratlibgrid_isvisited(grid, point))
			CGContextFillRect(context, [self rectFromPoint:point width:1 height:1]);
		
		point->x++;
		
		if (++width >= rect.size.width) {
			point->y++;
			point->x = rect.origin.x;
			width = 0;
			
			if (++height >= rect.size.height)
				break;
		}
	}
	
	ratlibpoint_release(point);
}

/**
 *
 *
 */
- (void)drawTargetInContext:(CGContextRef)context point:(ratlibpoint_t*)point colorR:(float)r colorG:(float)g colorB:(float)b colorA:(float)a
{
	int32_t x = point->x;
	int32_t y = mSession->image->height - point->y - 1;
	
	CGContextSetRGBFillColor(context, r, g, b, a);
	CGContextFillRect(context, CGRectMake(x-10, y-2, 21, 5));
	CGContextFillRect(context, CGRectMake(x-2, y-10, 5, 21));
}

/**
 *
 *
 */
- (NSRect)rectFromPoint:(ratlibpoint_t*)point width:(NSUInteger)width height:(NSUInteger)height
{
	return NSMakeRect(point->x, mSession->image->height-point->y-1, width, height);
}

/**
 * Over-ridden to return TRUE so that we can receive mouse events.
 *
 */
- (BOOL)acceptsFirstResponder
{
	return TRUE;
}

/**
 *
 *
 */
- (void)resetCursorRects
{
	if (mInTargetMode)
		[self addCursorRect:CGRectMake(0., 0., self.frame.size.width, self.frame.size.height) cursor:[NSCursor crosshairCursor]];
	else
		[self removeCursorRect:CGRectMake(0., 0., self.frame.size.width, self.frame.size.height) cursor:[NSCursor crosshairCursor]];
}





#pragma mark -
#pragma mark NSResponder

/**
 *
 *
 */
- (void)mouseDown:(NSEvent *)theEvent
{
	if (mEventCallbackTarget != nil && mEventCallbackSelector != nil)
		[mEventCallbackTarget performSelector:mEventCallbackSelector withObject:theEvent];
	
	[super mouseDown:theEvent];
}





#pragma mark -
#pragma mark NSNotificationCenter

- (void)doEventSolveRoundCompleted:(NSNotification*)notification
{
	if (mIsDirty)
		return;
	
	mIsDirty = TRUE;
	[self setNeedsDisplay:TRUE];
}

- (void)doEventMazeBeginPointSet:(NSNotification*)notification
{
	if (mIsDirty)
		return;
	
	mIsDirty = TRUE;
	[self setNeedsDisplay:TRUE];
}

- (void)doEventMazeEndPointSet:(NSNotification*)notification
{
	if (mIsDirty)
		return;
	
	mIsDirty = TRUE;
	[self setNeedsDisplay:TRUE];
}

- (void)doEventMazePointAdded:(NSNotification*)notification
{
	if (mIsDirty)
		return;
	
	mIsDirty = TRUE;
	[self setNeedsDisplay:TRUE];
}

@end
