#import "ConsoleView.h"

#define MAX_LINES 200
#define LINE_WIDTH 500
#define LINE_HEIGHT 12

/*
 * This VScroller sends a scrollToEnd message to the console based on whether you hit page/line down or not
 */
@interface VScroller : NSScroller {
}
@end
@implementation VScroller
- (NSScrollerPart)hitPart {
    ConsoleView *view =  (ConsoleView *)[(NSScrollView*)[self superview] documentView];
    NSScrollerPart part = [super hitPart];
    [view scrollToEnd:((part == NSScrollerIncrementPage) || (part == NSScrollerIncrementLine))];
    return part;
}
@end


@implementation ConsoleView

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        array = [[[NSMutableArray alloc] init] retain];
        
        attr = [[NSDictionary dictionaryWithObjectsAndKeys:
            [NSFont userFontOfSize:(LINE_HEIGHT-2)], NSFontAttributeName, 
            [NSColor colorWithCalibratedRed:0.2 green:0.8 blue:0.2 alpha:1.0], NSForegroundColorAttributeName, 
            nil] retain];
    }
    return self;
}

- (void)awakeFromNib {
    [self scrollToEnd:YES];
    
    NSScroller *vscroll = [[VScroller alloc] init];
    [vscroll setControlSize:[[[self enclosingScrollView] verticalScroller] controlSize]];
    [[self enclosingScrollView] setVerticalScroller:vscroll];
}

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

- (BOOL)isFlipped { 
    return YES; 
}

- (void)drawRect:(NSRect)rect { 
    //draw the visible lines only
    int startLine = rect.origin.y/LINE_HEIGHT;
    int endLine = 1 + (rect.origin.y+rect.size.height)/LINE_HEIGHT;
    if(startLine < 0) startLine = 0;
    if(endLine > [array count]) endLine = [array count];
    int i;  
    for(i = startLine; i < endLine; i++) {
        NSString *str = [array objectAtIndex:i];
        [str drawAtPoint:NSMakePoint(2, i * LINE_HEIGHT) withAttributes:attr];
    }
}

- (void)scrollToEnd:(BOOL)enable {
    endScroll = enable;
}

- (void)appendLine:(NSString*)line {
    BOOL chop = [array count] > MAX_LINES;
    if(chop) {
        [array removeObjectAtIndex:0]; // limit the number of lines
    }
    [array addObject:line]; 
    int i = [array count];
    [self setFrame:NSMakeRect(0, 0, LINE_WIDTH, i*LINE_HEIGHT)]; // increase the frame size 
        
    NSRect rect = NSMakeRect(0, (i-1)*LINE_HEIGHT, LINE_WIDTH, LINE_HEIGHT);
    if(endScroll) {
        // Scroll to the line just added
        if([self scrollRectToVisible:rect]) return;
    } else {
        // Lock the scrolling on the first visible line
        i = [self visibleRect].origin.y/LINE_HEIGHT;
        if(!chop) i++;
        if(i < 0) i = 0; 
        if(i > [array count]) i = [array count];
        NSRect vrect = NSMakeRect(0, (i-1)*LINE_HEIGHT, LINE_WIDTH, LINE_HEIGHT);
        if([self scrollRectToVisible:vrect]) return;
    }
    if(chop)
        [self setNeedsDisplay:YES];
    else
        [self setNeedsDisplayInRect:rect];
}

- (void)appendText:(NSString*)text {
    NSArray *lines = [text componentsSeparatedByString:@"\n"]; //@TODO assumes we get given lines rather than fragments...
    int i;
    for(i = 0; i < [lines count]; i++) {
        NSString *line = [lines objectAtIndex:i];
        if([line length] == 0) continue; //skip empty
        [self appendLine:line];
    }
}

- (BOOL)acceptsFirstResponder { 
    return YES; 
}

- (IBAction)delete:(id)sender {
    [array removeAllObjects];
    [self setFrame:NSMakeRect(0,0,0,0)];
}

@end
