#import "X24View.h"

// font sizes for pager sizes 0,1,2
static int fontSizes[3] = {
	12, 24, 48
};

// the width/height ratio of the separator images
#define SEPARATORL_ASPECT_RATIO 2.3833333333

// the width of a single image
#define SINGLE_IMAGE_WIDTH 10000.0

@implementation X24View

- (id) initWithFrame: (NSRect) frameRect
{
	self = [super initWithFrame: frameRect];
	if (self)
	{
		messages = nil;
		preparedImages = nil;
		animationTimer = nil;
		[self readPreferences];
		currentX = 0.0;
		totalWidth = 0.0;
		// load the separator background images
		separatorNormal = [[NSImage imageNamed:@"blanksep_blue"] retain];
		separatorUrgent = [[NSImage imageNamed:@"blanksep_red"] retain];
		separatorFont = [[NSFont fontWithName: @"Lucida Grande Bold" size: 24] retain];
		
		firstVisibleImageIndex = -1;
		
		// register for notifications
		[[NSNotificationCenter defaultCenter] 
			addObserver: self
			   selector: @selector(preferencesChangedNotificationHandler:) 
				   name: @"X24PreferencesChanged" 
				 object: nil];
		[[NSNotificationCenter defaultCenter] 
			addObserver: self
			   selector: @selector(messagesChangedNotificationHandler:) 
				   name: @"X24MessagesChanged" 
				 object: nil];
		[[NSNotificationCenter defaultCenter] 
			addObserver: self
			   selector: @selector(stopAnimationNotificationHandler:)
				   name: @"X24StopAnimation"
				 object: nil];
		[[NSNotificationCenter defaultCenter] 
			addObserver: self
			   selector: @selector(startAnimationNotificationHandler:)
				   name: @"X24StartAnimation"
				 object: nil];
		[[NSNotificationCenter defaultCenter] 
			addObserver: self
			   selector: @selector(pagerSizeChangedNotificationHandler:)
				   name: @"X24PagerSizeChanged"
				 object: nil];
	}
	return self;
}

- (void) dealloc
{
	// not wanting notifications any more
	[[NSNotificationCenter defaultCenter] removeObserver: self];
	
	if (messages)
		[messages release];
	if (preparedImages)
		[preparedImages release];
	if (separatorNormal)
		[separatorNormal release];
	if (separatorUrgent)
		[separatorUrgent release];
	if (separatorFont)
		[separatorFont release];
	[super dealloc];
}

- (void) readPreferences
{
	deltaX = [[NSUserDefaults standardUserDefaults] floatForKey: @"scrollingSpeed"];
	NS_DURING
	textColor = [[NSUnarchiver unarchiveObjectWithData:
		[[NSUserDefaults standardUserDefaults] objectForKey: @"textColor"]] retain];
	NS_HANDLER
		textColor = [[NSColor whiteColor] retain];
	NS_ENDHANDLER
	pagerSize = [[NSUserDefaults standardUserDefaults] integerForKey: @"pagerSize"];
}


- (void) preferencesChangedNotificationHandler: (NSNotification*) n
{
	[self readPreferences];
//	[self processMessages];
}

- (void) messagesChangedNotificationHandler: (NSNotification*) n
{
	if (messages)
		[messages release];
	messages = [[[n userInfo] objectForKey: @"messages"] retain];
	[self processMessages];
}

- (void) stopAnimationNotificationHandler: (NSNotification*) n
{
	if (animationTimer)
	{
		[animationTimer invalidate];
		[animationTimer release];
		animationTimer = nil;
	}
}

- (void) startAnimationNotificationHandler: (NSNotification*) n
{
	//TODO: set the timer to match the screen refresh
	animationTimer = [[NSTimer scheduledTimerWithTimeInterval: 1.0 / 30.0
													  target: self
													selector: @selector(animationStep:)
													userInfo: nil
													 repeats: YES] retain];
}

- (void) pagerSizeChangedNotificationHandler: (NSNotification*) n
{
	pagerSize = [[NSUserDefaults standardUserDefaults] integerForKey: @"pagerSize"];
	[self processMessages];
}

- (NSImage*) makeSeparator:(NSString*) separatorText urgent:(BOOL)isUrgent
{
	NSImage* separator;
	NSImage* background = (isUrgent ? separatorUrgent : separatorNormal);
	NSAttributedString* formattedSep;
	NSSize sepSize;
	NSSize sepTextSize;
	float baseline, sepTextX;
	
	// separator is the image to be drawn on
	separator = [background copy];
	sepSize = [separator size];
	
	[separator lockFocus];
	NSGraphicsContext* ctx = [NSGraphicsContext currentContext];
	[ctx saveGraphicsState];
	// formatted separator text
	formattedSep = [[NSAttributedString alloc] initWithString: separatorText
												   attributes: [NSDictionary dictionaryWithObjectsAndKeys: separatorFont, NSFontAttributeName, [NSColor whiteColor], NSForegroundColorAttributeName, nil]];
	sepTextSize = [formattedSep size];
	// center vertically
	baseline = (sepSize.height - sepTextSize.height) / 2.0;
	// center horizontally
	sepTextX = (sepSize.width - sepTextSize.width) / 2.0;
	
	// draw the separator text
	[formattedSep drawAtPoint:NSMakePoint(sepTextX, baseline)];
	
	// clean up
	[formattedSep release];
	[ctx restoreGraphicsState];
	[separator unlockFocus];
	return [separator autorelease];
}


- (void) processMessages
{
	NSRect myFrame = [self frame];
	int messageCount = [messages count];
	NSImage* separator = nil;
	NSSize separatorSize;
	NSSize scaledSeparatorSize;
	NSMutableArray* newPreparedImages;
	
	//NSLog(@"processMessages for pagerSize: %d",pagerSize);
	
	if (messages == nil)
		return;

	// stop the current animation
	[self stopAnimationNotificationHandler: nil];
			
	// set parameters
	if (pagerSize < 0)
		pagerSize = 0;
	else if (pagerSize > 2)
		pagerSize = 2;
	NSFont* msgFont = [[NSFont fontWithName: @"Lucida Grande Bold" size: fontSizes[pagerSize]] retain];
	
	// all separators have the same size, so why not use separatorNormal for measurement
	separatorSize = [separatorNormal size];

	// scaled separator's size depends on the pager size
	scaledSeparatorSize.height = myFrame.size.height - 4;
	scaledSeparatorSize.width = scaledSeparatorSize.height * SEPARATORL_ASPECT_RATIO;
	
	// calculate the total width of the pager image
	// start with empty space twice as wide as the pager view plus some offsets
	// for the first step of animation
	totalWidth = myFrame.size.width * 2.0 + deltaX * 2.0 + 2.0;
	NSSize singleMessageSize;
	NSSize msgImageSize;
	NSAttributedString* formattedMessage;
	float baseLine = 0.0;
	NSRect sr = NSMakeRect(0,0,separatorSize.width, separatorSize.height);
	NSRect r;
	NSPoint p;
	float currX = myFrame.size.width + deltaX + 1;
	float imX;
	int i;
	BOOL isUrgent = NO;
	NSDictionary* currentMessage;
	
	// init the images array
	newPreparedImages = [[NSMutableArray alloc] initWithCapacity: messageCount];
	
	// now calculate the size of the messages, remembering their attributed
	// versions in formattedMessages
	for (i = 0; i < messageCount; i++)
	{
		currentMessage = [messages objectAtIndex:i];
		formattedMessage = [[NSAttributedString alloc] initWithString: [currentMessage objectForKey: @"TEXT"]
														   attributes: [NSDictionary dictionaryWithObjectsAndKeys: msgFont, NSFontAttributeName, textColor, NSForegroundColorAttributeName, nil]];
		singleMessageSize = [formattedMessage size];
		
		isUrgent = [((NSString*) [currentMessage objectForKey:@"CATHEGORY"]) isEqualToString:@"PILNE"];
		separator = [self makeSeparator:[currentMessage objectForKey:@"CATHEGORY"] urgent:isUrgent];
		
		if (! separator)
			separator = separatorNormal;

		msgImageSize = NSMakeSize(singleMessageSize.width + myFrame.size.height * 2 + scaledSeparatorSize.width, myFrame.size.height);
		NSImage* msgImage = [[NSImage alloc] initWithSize: msgImageSize];
		baseLine = (myFrame.size.height - singleMessageSize.height) / 2.0;
		imX = currX;
				
		// draw the separator and the message
		[msgImage lockFocus];
		
		NSGraphicsContext* ctx = [NSGraphicsContext currentContext];
		[ctx saveGraphicsState];
		[textColor setFill];
		[textColor set];

		// draw the separator
		r.origin.x = 0;
		r.origin.y = 2;
		r.size = scaledSeparatorSize;
		[separator drawInRect: r
					 fromRect: sr
					operation: NSCompositeSourceOver
					 fraction: 1.0];
		currX += scaledSeparatorSize.width + myFrame.size.height; // separator + gap before text
		
		// draw the message
		p = NSMakePoint(scaledSeparatorSize.width + myFrame.size.height, baseLine);
		[formattedMessage drawAtPoint: p];
		currX += singleMessageSize.width + myFrame.size.height; // message + gap after text
		
		[ctx restoreGraphicsState];
		[msgImage unlockFocus];
		
		totalWidth += singleMessageSize.width + scaledSeparatorSize.width + 2.0 * myFrame.size.height;
		
		[newPreparedImages addObject: [NSDictionary dictionaryWithObjectsAndKeys:
			msgImage, @"image",
			[NSNumber numberWithFloat: imX], @"offset",
			[NSNumber numberWithFloat: msgImageSize.width], @"width",
			nil]];
				
		[formattedMessage release];
		[msgImage release];
	}
	
	[preparedImages autorelease];
	preparedImages = newPreparedImages;
	firstVisibleImageIndex = -1;
	
	currentX = 0.0;

	// post the start animation notification
	[[NSNotificationCenter defaultCenter] postNotificationName: @"X24StartAnimation"
														object: self];
}

- (void) animationStep: (NSTimer*) t
{
	if (!preparedImages)
	{
		[self stopAnimationNotificationHandler: nil];
		return;
	}
	NSRect myFrame = [self frame];
	if (myFrame.size.width + currentX >= totalWidth - 1)
	{
		[self stopAnimationNotificationHandler: nil];
		[[NSNotificationCenter defaultCenter] postNotificationName: @"X24AllMessagesDisplayed"
															object: self];
		return;
	}
	else
	{
		currentX += deltaX;
		[self setNeedsDisplay: YES];		
	}	
}

- (void) drawRect: (NSRect*) r
{
	if (! preparedImages)
		return;
	int msgIndex = firstVisibleImageIndex;
	int msgCount = [preparedImages count];
	float msgOffset;
	float msgWidth;
	NSRect myFrame = [self frame];
	NSRect imgSrcFrame;
	NSRect imgDstFrame;
	NSImage* msgImage;
	
	// omit all messages to the left
	do 
	{
		msgIndex++;
		msgOffset = [[[preparedImages objectAtIndex: msgIndex] objectForKey: @"offset"] floatValue];
		msgWidth = [[[preparedImages objectAtIndex: msgIndex] objectForKey: @"width"] floatValue];
	} while ((msgOffset + msgWidth < currentX) && (msgIndex < msgCount-1));
	
	if (msgOffset + msgWidth < currentX)
		return;
			
	// draw all images, of which at least parts are visible
	msgIndex--;
	firstVisibleImageIndex = msgIndex;
	do
	{
		msgIndex++;		
		msgOffset = [[[preparedImages objectAtIndex: msgIndex] objectForKey: @"offset"] floatValue];
		msgWidth = [[[preparedImages objectAtIndex: msgIndex] objectForKey: @"width"] floatValue];
		msgImage = [[preparedImages objectAtIndex: msgIndex] objectForKey: @"image"];
		imgDstFrame = NSMakeRect(msgOffset - currentX, 0, msgWidth, myFrame.size.height);
		imgSrcFrame = NSMakeRect(0,0,msgWidth,myFrame.size.height);
		[msgImage drawInRect: imgDstFrame
					fromRect: imgSrcFrame 
				   operation: NSCompositeSourceOver 
					fraction: 1.0];
	} while ((msgOffset <= currentX + myFrame.size.width) && (msgIndex < msgCount-1));
	
	// omit the rest of the images
}



@end
