//
//  ZoomTextView.m
//  ZoomCocoa
//
//  Created by Andrew Hunter on Thu Oct 09 2003.
//  Copyright (c) 2003 Andrew Hunter. All rights reserved.
//

#import "ZoomTextView.h"
#import "ZoomView.h"
#import "ZoomPreferences.h"

@implementation ZoomTextView

- (id) initWithFrame: (NSRect) frame {
    self = [super initWithFrame: frame];
    if (self) {
        pastedLines = [[NSMutableArray allocWithZone: [self zone]] init];
		pastedScaleFactor = 1.0;
    }
    return self;
}

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

// Key event handling

- (void) keyDown: (NSEvent*) event {
    NSView* superview = [self superview];

    while (![superview isKindOfClass: [ZoomView class]]) {
        superview = [superview superview];
        if (superview == nil) break;
    }

    if (![(ZoomView*)superview handleKeyDown: event]) {
        [super keyDown: event];
    }
}

// = Mouse event handling =

- (void) mouseDown: (NSEvent*) evt {
	if (!dragged) {
		NSView* superview = [self superview];
		
		while (![superview isKindOfClass: [ZoomView class]]) {
			superview = [superview superview];
			if (superview == nil) break;
		}
		
		[(ZoomView*)superview clickAtPointInWindow: [evt locationInWindow]
										 withCount: [evt clickCount]];
	}
	
	[super mouseDown: evt];
}

- (BOOL)accessibilityIsIgnored {
	if ([[ZoomPreferences globalPreferences] speakGameText]) return YES;
	return [super accessibilityIsIgnored];
}

// = Drawing =

// (Draw the overlays)
- (void) drawRect: (NSRect) r {
	// Perform standard text drawing actions (we save state so that we don't get the text view clip rectangle at the end)
	[[NSGraphicsContext currentContext] saveGraphicsState];	
    [super drawRect: r];
	[[NSGraphicsContext currentContext] restoreGraphicsState];

    NSRect ourBounds = [self bounds];
    NSRect superBounds = [[self superview] frame];
	
	superBounds = [self convertRect: superBounds
						   fromView: [self superview]];

    // The enclosing ZoomView
    NSView* superview = [self superview];

    while (![superview isKindOfClass: [ZoomView class]]) {
        superview = [superview superview];
        if (superview == nil) break;
    }
    ZoomView* zoomView = (ZoomView*) superview;

    double offset = [zoomView upperBufferHeight]/pastedScaleFactor;
    
    // Draw pasted lines
    NSEnumerator* lineEnum = [pastedLines objectEnumerator];
    NSArray* line;
	
	NSAffineTransform* invertTransform = [NSAffineTransform transform];
	[invertTransform scaleXBy: 1.0 
						  yBy: -1.0];

	NSAffineTransform* scaleTransform = [NSAffineTransform transform];
	[scaleTransform scaleXBy: pastedScaleFactor
						 yBy: pastedScaleFactor];
	
    while (line = [lineEnum nextObject]) {
        NSValue* rect = [line objectAtIndex: 0];
        NSRect   lineRect = [rect rectValue];

        lineRect.origin.y -= offset;
		NSRect nonScaledRect = lineRect;
		
		lineRect.origin.x *= pastedScaleFactor;
		lineRect.origin.y *= pastedScaleFactor;
		lineRect.size.width *= pastedScaleFactor;
		lineRect.size.height *= pastedScaleFactor;
		
		lineRect.origin.x = floorf(lineRect.origin.x+0.5);
		lineRect.origin.y = floorf(lineRect.origin.y+0.5);
		lineRect.size.width = floorf(lineRect.size.width+0.5);
		lineRect.size.height = floorf(lineRect.size.height+0.5);
		
        if (NSIntersectsRect(r, lineRect)) {
            NSAttributedString* str = [line objectAtIndex: 1];
			
            if (NSMaxY(lineRect) < NSMaxY(ourBounds)-superBounds.size.height) {
                // Draw it faded out (so text underneath becomes increasingly visible)
                NSImage* fadeImage;

                double fadeAmount = (NSMaxY(ourBounds)-superBounds.size.height) - NSMaxY(lineRect);
                fadeAmount /= 2048;
                fadeAmount += 0.25;
                if (fadeAmount > 0.75) fadeAmount = 0.75;
                fadeAmount = 1-fadeAmount;
                
                fadeImage = [[NSImage alloc] initWithSize: lineRect.size];

				[fadeImage setFlipped: NO];
                [fadeImage setSize: lineRect.size];
                [fadeImage lockFocus];

				[[NSGraphicsContext currentContext] saveGraphicsState];					

				// Because text views are drawn flipped, we need to draw the image upsidedown
				[scaleTransform concat];
				[invertTransform concat];
                
				[str drawAtPoint: NSMakePoint(0, -lineRect.size.height/pastedScaleFactor)];
				[[NSGraphicsContext currentContext] restoreGraphicsState];
                [fadeImage unlockFocus];

				[fadeImage drawInRect:NSMakeRect(lineRect.origin.x,
												 lineRect.origin.y,
												 lineRect.size.width,
												 lineRect.size.height)
							 fromRect:NSMakeRect(0,0,
												 lineRect.size.width,
												 lineRect.size.height)
							operation:NSCompositeSourceOver
							 fraction:fadeAmount];

                [fadeImage release];
            } else {
				[[NSGraphicsContext currentContext] saveGraphicsState];	
				[scaleTransform concat];
                [str drawAtPoint: nonScaledRect.origin];
				[[NSGraphicsContext currentContext] restoreGraphicsState];
            }
        }
    }
}

// = Pasting overlays =

// Things that are drawn in the upper window, but outside the point at which it has been split are
// overlaid into the text view.

- (void) clearPastedLines {
    [pastedLines removeAllObjects];
}

- (void) setPastedLineScaleFactor: (float) scaleFactor {
	pastedScaleFactor = scaleFactor;
}

- (void) pasteUpperWindowLinesFrom: (ZoomUpperWindow*) win {
    NSArray* lines = [win lines];
    BOOL changed;
    
    if ([lines count] < [win length]) {
        return; // Nothing to do
    }

    // Get some information about the view
    NSView* container = (NSView*)[self superview];

    // The container
    while (![container isKindOfClass: [NSClipView class]]) {
        container = [container superview];
        if (container == nil) break;
    }

    if (container == nil) container = self;

    // The enclosing ZoomView
    NSView* superview = [self superview];

    while (![superview isKindOfClass: [ZoomView class]]) {
        superview = [superview superview];
        if (superview == nil) break;
    }
    ZoomView* zoomView = (ZoomView*) superview;
    
    NSRect ourBounds = [self bounds];
    NSRect containerBounds = [container bounds];
	
	//ourBounds = [self convertRect: ourBounds toView: container];
	containerBounds = [self convertRect: containerBounds fromView: container];

    double offset = [zoomView upperBufferHeight];

    double topPoint = NSMaxY(ourBounds) - containerBounds.size.height;
    
    NSSize fixedSize = [@"M" sizeWithAttributes:
        [NSDictionary dictionaryWithObjectsAndKeys:
            [zoomView fontWithStyle:ZFixedStyle], NSFontAttributeName, nil]];
    
	NSFontManager* fm = [NSFontManager sharedFontManager];
	
    // Perform the pasting
    changed = NO;

    NSRect drawRect = NSZeroRect;
    
    int l;
    for (l=[win length]; l<[lines count]; l++) {
        NSRect r;
        NSAttributedString* str = [lines objectAtIndex: l];

        if ([str length] > 0) {
            r = NSMakeRect(0, topPoint+fixedSize.height*(l-[win length]), 0,0);
            r.origin.y += offset;
            r.size = [str size];
			
			// Scale down the rectangle by the scale factor
			if (pastedScaleFactor != 1.0) {
				r.origin.x /= pastedScaleFactor;
				r.origin.y /= pastedScaleFactor;
				r.size.width /= pastedScaleFactor;
				r.size.height /= pastedScaleFactor;

				// Scale down the font size by the scale factor
				NSMutableAttributedString* editedStr = [[str mutableCopy] autorelease];
				
				NSRange editRange;
				NSDictionary* currentAttributes = [editedStr attributesAtIndex: 0
																effectiveRange: &editRange];
				for (;;) {
					NSFont* oldFont = [currentAttributes objectForKey: NSFontAttributeName];
					
					if (oldFont != nil) {
						NSFont* newFont = [fm convertFont: oldFont
												   toSize: [oldFont pointSize] / pastedScaleFactor];
						[editedStr addAttribute: NSFontAttributeName
										  value: newFont
										  range: editRange];
					}
					
					int newPos = editRange.location + editRange.length;
					if (editRange.length == 0) newPos++;
					if (newPos >= [editedStr length]) break;
					
					currentAttributes = [editedStr attributesAtIndex: newPos
													  effectiveRange: &editRange];
				}
				
				str = editedStr;
			}
			
			// Add this line to the set of pasted lines
            [pastedLines addObject: [NSArray arrayWithObjects:
                [NSValue valueWithRect: r],
                str,
                nil]];

            r.origin.y -= offset;

            drawRect = NSUnionRect(drawRect, r);
            changed = YES;
        }
    }

    if (changed) {
        // Update the window
        [self setNeedsDisplayInRect: drawRect];
    }

    // Scrub the lines
    [win cutLines];
}

- (void) offsetPastedLines: (float) offset {
	if (offset == 0) return;
	offset /= pastedScaleFactor;
	
	// Subtract offset from all of the pasted lines, and remove any that have disappeared
	NSMutableArray* newLines = [[NSMutableArray alloc] init];
	
	NSEnumerator* pastedEnum = [pastedLines objectEnumerator];
	NSArray* line;
	while (line = [pastedEnum nextObject]) {
		// Work out the new position of this line
		NSRect lineRect = [[line objectAtIndex: 0] rectValue];;
		NSAttributedString* str = [line objectAtIndex: 1];
		
		lineRect.origin.y -= offset;

		// If it's still in the view, then add it to the modified array
		if (NSMaxY(lineRect) > 0) {
			[newLines addObject: [NSArray arrayWithObjects:
				[NSValue valueWithRect: lineRect],
				str,
				nil]];
		}
	}
	
	// Replace the pasted lines with the new set of lines
	[pastedLines autorelease];
	pastedLines = newLines;
}

- (id)accessibilityAttributeValue:(NSString *)attribute {
	if ([attribute isEqualToString: NSAccessibilityParentAttribute]) {
		NSView* parent = [self superview];
		while (parent != nil && ![parent isKindOfClass: [ZoomView class]]) {
			parent = [parent superview];
		}
		if (parent) return parent;
	}
	
	return [super accessibilityAttributeValue: attribute];
}

@end
