#import "CQButtonCell.h"

typedef enum {
	CQMouseStateDefault = 0,
	CQMouseStateHover,
	CQMouseStateClick
} CQMouseState;

NSString *CQDualButtonLeftDictionaryKey = @"CQDualButtonLeftDictionaryKey";
NSString *CQDualButtonRightDictionaryKey = @"CQDualButtonRightDictionaryKey";

NSString *CQMouseStateDefaultKey = @"CQMouseStateDefaultKey";
NSString *CQMouseStateHoverKey = @"CQMouseStateHoverKey";
NSString *CQMouseStateClickKey = @"CQMouseStateClickKey";

@interface NSButtonCell (Properties)
@property (nonatomic) NSButtonType buttonType;
@property (nonatomic) BOOL bordered;
@property (nonatomic) BOOL continuous;
@end

#pragma mark -

@implementation CQButtonCell
@synthesize rightButtonCell = _rightButtonCell;
@synthesize leftButtonCell = _leftButtonCell;
@synthesize hidesLeftButton = _hideLeftButton;
@synthesize mouseStates = _mouseStates;

#pragma mark -

+ (BOOL) prefersTrackingUntilMouseUp {
	return YES;
}

#pragma mark -

- (id) init {
	if (!(self = [super init]))
		return nil;

	_rightButtonCell = [[NSButtonCell alloc] init];
	_rightButtonCell.buttonType = NSSwitchButton;
	_rightButtonCell.bezelStyle = NSSmallSquareBezelStyle;
	_rightButtonCell.imagePosition = NSImageRight;
	_rightButtonCell.bordered = NO;
	_rightButtonCell.continuous = NO;
	[_rightButtonCell sendActionOn:NSLeftMouseUp];

	_leftButtonCell = [[NSButtonCell alloc] init];
	_leftButtonCell.buttonType = NSSwitchButton;
	_leftButtonCell.bezelStyle = NSSmallSquareBezelStyle;
	_leftButtonCell.imagePosition = NSImageRight;
	_leftButtonCell.bordered = NO;
	_leftButtonCell.continuous = NO;
	[_leftButtonCell sendActionOn:NSLeftMouseUp];

	return self;
}

- (id) copyWithZone:(NSZone *) zone {
	CQButtonCell *cell = (CQButtonCell *)[super copyWithZone:zone];
	cell->_rightButtonCell = [_rightButtonCell retain];
	cell->_leftButtonCell = [_leftButtonCell retain];
	cell->_hideLeftButton = _hideLeftButton;
	cell->_leftButtonMouseState = _leftButtonMouseState;
	cell->_rightButtonMouseState = _rightButtonMouseState;
	cell->_mouseStates = [_mouseStates retain];
	return cell;
}

- (void) dealloc {
	[_mouseStates release];
	[_rightButtonCell release];
	[_leftButtonCell release];

	[super dealloc];
}

#pragma mark -

- (void) setHidesLeftButton:(BOOL) hidesLeftButton {
	_hideLeftButton = hidesLeftButton;

	[(NSControl *)self.controlView updateCell:self];
}

- (NSRect) _rightButtonCellFrameFromRect:(NSRect) cellFrame {
#define RightButtonBorderMargin 9.
	NSRect rightFrame = NSMakeRect(0, 0, _rightButtonCell.image.size.width, _rightButtonCell.image.size.height);
	rightFrame.origin.x = (cellFrame.origin.x + cellFrame.size.width) - (rightFrame.size.width + RightButtonBorderMargin);
	rightFrame.origin.y = floorf(cellFrame.origin.y + ((cellFrame.size.height / 2) - (rightFrame.size.height / 2.)));
	return rightFrame;
}

- (NSRect) _leftButtonCellFrameFromRect:(NSRect) cellFrame {
#define BetweenButtonBorderMargin 7.
	NSRect rightFrame = [self _rightButtonCellFrameFromRect:cellFrame];
	NSRect leftFrame = NSMakeRect(0, 0, _leftButtonCell.image.size.width, _leftButtonCell.image.size.height);
	leftFrame.origin.x = rightFrame.origin.x - (leftFrame.size.width + BetweenButtonBorderMargin);
	leftFrame.origin.y = rightFrame.origin.y;
	return leftFrame;
}

#pragma mark -

- (NSImage *) _imageFromState:(CQMouseState) state inDictionary:(NSDictionary *) dictionary {
	if (!dictionary)
		return nil;

	switch (state) {
	case CQMouseStateDefault:
		return [dictionary objectForKey:CQMouseStateDefaultKey];
	case CQMouseStateHover:
		return [dictionary objectForKey:CQMouseStateHoverKey];
	case CQMouseStateClick:
		return [dictionary objectForKey:CQMouseStateClickKey];
	}

	return nil;
}

- (void) drawWithFrame:(NSRect) cellFrame inView:(NSView *) controlView {
	if (!_hideLeftButton) {
		NSImage *image = [self _imageFromState:_leftButtonMouseState inDictionary:[_mouseStates objectForKey:CQDualButtonLeftDictionaryKey]];
		if (image)
			_leftButtonCell.image = image;
		[_leftButtonCell drawWithFrame:[self _leftButtonCellFrameFromRect:cellFrame] inView:controlView];
	}

	NSImage *image = [self _imageFromState:_rightButtonMouseState inDictionary:[_mouseStates objectForKey:CQDualButtonRightDictionaryKey]];
	if (image)
		_rightButtonCell.image = image;
	[_rightButtonCell drawWithFrame:[self _rightButtonCellFrameFromRect:cellFrame] inView:controlView];
}

- (NSUInteger) hitTestForEvent:(NSEvent *) event inRect:(NSRect) cellFrame ofView:(NSView *) controlView {
	NSPoint point = [controlView convertPoint:event.locationInWindow fromView:nil];

	if (!_hideLeftButton && NSMouseInRect(point, [self _leftButtonCellFrameFromRect:cellFrame], [controlView isFlipped]))
		return (NSCellHitContentArea | NSCellHitTrackableArea);
	if (NSMouseInRect(point, [self _rightButtonCellFrameFromRect:cellFrame], [controlView isFlipped]))
		return (NSCellHitContentArea | NSCellHitTrackableArea);
	return [super hitTestForEvent:event inRect:cellFrame ofView:controlView];
}

#pragma mark -

- (void) _addTrackingAreasForView:(NSView *) controlView inRect:(NSRect) cellFrame withUserInfo:(NSDictionary *) userInfo mouseLocation:(NSPoint) mouseLocation forButtonFrame:(NSRect) buttonFrame {
	NSTrackingAreaOptions options = (NSTrackingEnabledDuringMouseDrag | NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways);

	if (NSMouseInRect(mouseLocation, buttonFrame, [controlView isFlipped])) {
		options |= NSTrackingAssumeInside;
		[controlView setNeedsDisplayInRect:cellFrame];
	}

	NSTrackingArea *buttonTrackingArea = [[NSTrackingArea alloc] initWithRect:buttonFrame options:options owner:controlView userInfo:userInfo];
	[controlView addTrackingArea:buttonTrackingArea];
	[buttonTrackingArea release];
}

- (void) addTrackingAreasForView:(NSView *) controlView inRect:(NSRect) cellFrame withUserInfo:(NSDictionary *) userInfo mouseLocation:(NSPoint) mouseLocation {
	NSMutableDictionary *modifiedUserInfo = [userInfo mutableCopy];
	[modifiedUserInfo setObject:@"Left" forKey:@"Position"];
	[self _addTrackingAreasForView:controlView inRect:cellFrame withUserInfo:modifiedUserInfo mouseLocation:mouseLocation forButtonFrame:[self _leftButtonCellFrameFromRect:cellFrame]];

	id old = modifiedUserInfo;
	modifiedUserInfo = [userInfo mutableCopy];
	[old release];
	[modifiedUserInfo setObject:@"Right" forKey:@"Position"];
	[self _addTrackingAreasForView:controlView inRect:cellFrame withUserInfo:modifiedUserInfo mouseLocation:mouseLocation forButtonFrame:[self _rightButtonCellFrameFromRect:cellFrame]];
	[modifiedUserInfo release];
}

#pragma mark -

- (BOOL) trackMouse:(NSEvent *) mouseEvent inRect:(NSRect) cellFrame ofView:(NSView *) controlView untilMouseUp:(BOOL) untilMouseUp {
	self.controlView = controlView;

	NSRect leftButtonRect = [self _leftButtonCellFrameFromRect:cellFrame];
	NSRect rightButtonRect = [self _rightButtonCellFrameFromRect:cellFrame];

	while (mouseEvent.type != NSLeftMouseUp) {
		NSPoint point = [controlView convertPoint:[mouseEvent locationInWindow] fromView:nil];
		if (NSMouseInRect(point, leftButtonRect, [controlView isFlipped]) && (_leftButtonMouseState != CQMouseStateClick)) {
			_leftButtonMouseState = CQMouseStateClick;
			[controlView setNeedsDisplayInRect:cellFrame];
		}

		if (NSMouseInRect(point, rightButtonRect, [controlView isFlipped]) && (_rightButtonMouseState != CQMouseStateClick)) {
			_rightButtonMouseState = CQMouseStateClick;
			[controlView setNeedsDisplayInRect:cellFrame];
		}

		if (mouseEvent.type == NSMouseEntered || mouseEvent.type == NSMouseExited)
			[NSApp sendEvent:mouseEvent];

		mouseEvent = [controlView.window nextEventMatchingMask:(NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSMouseEnteredMask | NSMouseExitedMask)];
	}

	if (_leftButtonMouseState == CQMouseStateClick) {
		[NSApp sendAction:_leftButtonCell.action to:_leftButtonCell.target from:controlView];
		_leftButtonMouseState = CQMouseStateHover;
		[controlView setNeedsDisplayInRect:cellFrame];
	}

	if (_rightButtonMouseState == CQMouseStateClick) {
		[NSApp sendAction:_rightButtonCell.action to:_rightButtonCell.target from:controlView];
		_rightButtonMouseState = CQMouseStateHover;
		[controlView setNeedsDisplayInRect:cellFrame];
	}

	return YES;
}

- (void) mouseEntered:(NSEvent *) event {
	if ([[event.trackingArea.userInfo objectForKey:@"Position"] isEqualToString:@"Left"])
		_leftButtonMouseState = CQMouseStateHover;
	else _rightButtonMouseState = CQMouseStateHover;

	[(NSControl *)self.controlView updateCell:self];
}

- (void) mouseExited:(NSEvent *) event {
	if ([[event.trackingArea.userInfo objectForKey:@"Position"] isEqualToString:@"Left"])
		_leftButtonMouseState = CQMouseStateDefault;
	else _rightButtonMouseState = CQMouseStateDefault;

	[(NSControl *)self.controlView updateCell:self];
}
@end
