//
//  MPT_AUView.m
//  AUCoderDemo
//
//  Created by Andrew Salamon on 5/17/06.
//  Copyright (c) 2006 Machine Perception Laboratory 
//  University of California San Diego.//

#import "MPT_AUView.h"


@interface MPT_Number : NSNumber
{
	BOOL hasValue;
}
+ (MPT_Number *)numberWithNoValue;

- (BOOL)hasValue;
- (void)setHasValue:(BOOL)value;
@end

@interface NSNumber(NoValue)
- (BOOL)hasValue;
@end

@interface MPT_AUView(PrivateMethods)
- (NSDictionary *)attributesForScale:(int)sc;
- (void)calculateOffset;
- (void)calculateNewSize;
@end

@implementation MPT_AUView
+ (NSRect)defaultFrame
{
	static NSRect defaultAUFrame = { 468, 531, 200, 65 };
	return defaultAUFrame;
}

static float margin = 8.0;
+ (float)margin
{
	return margin;
}
+ (void)setMargin:(float)newMargin
{
	margin = newMargin;
}

static int maxScale = 2;
+ (int)maxScale
{
    return maxScale;
}
+ (void)setMaxScale:(int)value
{
    maxScale = value;
}


- (id)init
{
	return [self initWithFrame:[[self class] defaultFrame]];
}

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
	{
		scale = 1;
        AU = 0;
		AUDisplayString = AUString = @"NA";
		
		[self setDefaultMin:-0.1];
		[self setDefaultMax:0.1];
		
		[self resetDynamicRange];
		
		count = 100;
		noValue = YES;
		values = [[NSMutableArray alloc] initWithCapacity:count];
		clearValues = NO;
		
		[self setAUDisplayAttributes:[self attributesForScale:scale]];
		[self calculateOffset]; ///< Set baselineOffset and AUStringWidth.
    }
    return self;
}

- (void)dealloc
{
	[values release];
	[AUDisplayAttributes release];
	[super dealloc];
}

- (BOOL)isFlipped
{
	return YES;
}

- (void)buildDisplayString
{
	if( AU != 0 )
	{
		[AUDisplayString release];
		AUDisplayString = [[NSString stringWithFormat:@"%@ %d", AUString, AU] retain];
	}
	else
	{
		[AUDisplayString release];
		AUDisplayString = [AUString copy];
	}
	[self calculateOffset];
}

- (int)AU {
    return AU;
}

- (void)setAU:(int)value {
    if (AU != value)
	{
        AU = value;
		if( [AUString isEqualToString:@"NA"] )
		{
			[AUString release];
			AUString = [@"AU" retain];
		}
		[self buildDisplayString];
    }
}

- (NSString *)AUString
{
    return AUString;
}

- (void)setAUString:(NSString *)value
{
    if( AUString != value )
    {
        [AUString release];
        AUString = [value copy];
		[self buildDisplayString];
    }
}

- (float)defaultMin
{
    return defaultMin;
}

- (void)setDefaultMin:(float)value
{
    defaultMin = value;
}

- (float)defaultMax
{
    return defaultMax;
}

- (void)setDefaultMax:(float)value
{
    defaultMax = value;
}

- (float)min
{
    return min;
}

- (void)setMin:(float)value
{
    min = value;
}

- (float)max
{
    return max;
}

- (void)setMax:(float)value
{
    max = value;
}

- (void)resetDynamicRange
{
	clearValues = YES;
}

- (unsigned int)count
{
	return count;
}
- (void)setCount:(unsigned int)newCount
{
	count = newCount;
}

- (void)addValue:(float)value
{
	if( clearValues )
	{
		[self setMin:[self defaultMin]];
		[self setMax:[self defaultMax]];
		[values removeAllObjects];
		clearValues = NO;
	}

	if( [values count] > count )
	{
		[values removeObjectAtIndex:0];
		[values addObject:[NSNumber numberWithFloat:value]];
	}
	else
	{
		[values addObject:[NSNumber numberWithFloat:value]];
	}
	
	if( value < min )
		min = value;
	if( value > max )
		max = value;
	noValue = NO;
	[self setNeedsDisplay:YES];
}

- (void)addNoValue
{
	if( clearValues )
	{
		[self setMin:[self defaultMin]];
		[self setMax:[self defaultMax]];
		[values removeAllObjects];
		clearValues = NO;
	}

	if( [values count] > count )
	{
		[values removeObjectAtIndex:0];
		[values addObject:[MPT_Number numberWithNoValue]];
	}
	else
	{
		[values addObject:[MPT_Number numberWithNoValue]];
	}
	noValue = YES;
	[self setNeedsDisplay:YES];
}

- (BOOL)isEnabled
{
    return enabled;
}

- (void)setEnabled:(BOOL)value
{
    enabled = value;
}

- (int)scale
{
    return scale;
}
- (void)setScale:(int)value
{
	if( scale != value )
	{
		scale = value;
		[self calculateNewSize];
	}
}


- (NSString *)AUDisplayString
{
    return AUDisplayString;
}

- (NSDictionary *)AUDisplayAttributes
{
    return AUDisplayAttributes;
}
- (void)setAUDisplayAttributes:(NSDictionary *)value
{
    if( AUDisplayAttributes != value )
    {
        [AUDisplayAttributes release];
        AUDisplayAttributes = [value retain];
    }
}

- (void)clearValue
{
	noValue = YES;
	[self setNeedsDisplay:YES];
}

- (void)drawRect:(NSRect)rect
{
	NSSize mySize = [self frame].size;
	float baseline = mySize.height - baselineOffset;
	NSRect aRect = NSMakeRect( 0.0, baseline, mySize.width, 1.0 );
	NSPoint center = NSMakePoint( 4.0, baseline );
	[[NSColor blackColor] setFill];
	NSRectFill( aRect );
	[AUDisplayString drawAtPoint:center withAttributes:AUDisplayAttributes];
	
	NSFrameRect( NSMakeRect( 0.0, 0.0, mySize.width, mySize.height ) );
	
	float inc = mySize.width / count;
	NSRect bar = NSMakeRect( 0.0, baseline, inc, mySize.height - baseline );
	float x = 0.0;
	float barscale =  (mySize.height - baselineOffset) / (max - min);
	NSEnumerator *valueEnum = [values objectEnumerator];
	NSNumber *value;
	while( value = [valueEnum nextObject] )
	{
		if( [value hasValue] )
		{
			bar.size.height = ([value floatValue] - min) * barscale;
			bar.origin.x = x;
			bar.origin.y = baseline - bar.size.height;
			NSRectFill( bar );
		}
		x += inc;
	}
	
	if( [values count] && !noValue )
	{
		NSString *cur = [NSString stringWithFormat:@"%.4f", [[values lastObject] floatValue]];
		float auWidth = [cur sizeWithAttributes:AUDisplayAttributes].width;
		center.x = mySize.width - (auWidth + 5.0);
		[cur drawAtPoint:center withAttributes:AUDisplayAttributes];
	}
}

- (void)calculateNewSize
{
	NSSize size = [[self class] defaultFrame].size;
	size.width *= scale;
	size.width += [[self class] margin] * (scale - 1);
	size.height *= scale;
	size.height += [[self class] margin] * (scale - 1);
	[self setFrameSize:size];
	
	[self setAUDisplayAttributes:[self attributesForScale:scale]];
	[self calculateOffset];
	
	[self setNeedsDisplay:YES];
	if( [[self superview] respondsToSelector:@selector(tileViews)] )
		[[self superview] performSelector:@selector(tileViews)];
}


- (void)mouseDown:(NSEvent *)theEvent
{
	// We have to suppress the parent classes' implementation of this, otherwise mouseUp never get's called.
}

- (void)mouseUp:(NSEvent *)theEvent
{
	if( ![self mouse:[self convertPoint:[theEvent locationInWindow] fromView:nil] inRect:[self bounds]] )
		return;
	
	if( [theEvent modifierFlags] & NSAlternateKeyMask )
	{
		[[self superview] setNeedsDisplayInRect:[self frame]];
		--scale;
	}
	else
	{
		++scale;
	}
	if( scale > maxScale )
	{
		[[self superview] setNeedsDisplayInRect:[self frame]];
		scale = 1;
	}
	else if( scale < 1 )
	{
		scale = maxScale;
	}
	
}

- (NSDictionary *)attributesForScale:(int)sc
{
	float fontSize = [NSFont systemFontSize];
	switch( sc )
	{
		case 1: fontSize = 12.0; break;
		case 2: fontSize = 18.0; break;
		default: fontSize = 24.0; break;
	}
	NSFont *font = [NSFont systemFontOfSize:fontSize];
	font = [NSFont labelFontOfSize:fontSize];
	return [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil];	
}

- (void)calculateOffset
{
	NSSize sz = [AUDisplayString sizeWithAttributes:[self AUDisplayAttributes]];
	baselineOffset = sz.height;
	AUStringWidth = [@"-00.0000" sizeWithAttributes:[self AUDisplayAttributes]].width;
}
@end


@implementation MPT_Number
+ (MPT_Number *)numberWithNoValue
{
	MPT_Number *tmp = [[MPT_Number alloc] init];
	[tmp setHasValue:NO];
	return [tmp autorelease];
}

- (id)init
{
	if( self = [super init] )
	{
		[self setHasValue:YES];
	}
	return self;
}

- (BOOL)hasValue
{
    return hasValue;
}

- (void)setHasValue:(BOOL)value
{
	hasValue = value;
}
@end

@implementation NSNumber(NoValue)
- (BOOL)hasValue
{
	return YES;
}
@end
