/* 
CodeBrowserController 
 
 Part of Code-Browser, simple front end to GNU source-highlight
 Copyright (C) 2006, Niko Matsakis
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License along
 with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 You can contact the author at niko@alum.mit.edu
 */

#import "CodeBrowserController.h"
#import "NSStringCategory.h"
#import "NSTreeControllerCategory.h"

@interface CodeBrowserController (Private)

// Colorizes the data from the file at the given path, returning nil on error.
- (NSData*) loadColoredDataForPath:(NSString*)selectedPath;

// Colorizes and loads the data from the file at path into the browser, returning NO
// if there was a failure.
- (BOOL) loadPathIntoBrowser:(NSString*)selectedPath;

@end

@implementation CodeBrowserController

- init
{
    if ((self = [super initWithWindowNibName:@"CodeBrowser"])) {
        [self setHistory:[NSArray array]];
    }
    return self;
}

- (void) dealloc
{
    [m_directoryStructure removeObserver:self forKeyPath:@"selectedObjects"];
    [self setHistory:nil];
    [super dealloc];
}

- (void) awakeFromNib
{
    [m_directoryStructure addObserver:self forKeyPath:@"selectedObjects" options:0 context:nil];
    //[m_webView setMaintainsBackForwardList:YES]; this seems to have no effect
}

- (NSString *)windowTitleForDocumentDisplayName:(NSString *)displayName
{
    return @"Code Browser";
}

- (IBAction) loadFile:sender
{
    NSArray *newValue = [m_directoryStructure selectedObjects];
    if ([newValue count] == 1) {
        NSString *selectedPath = [newValue objectAtIndex:0];
        if ([self loadPathIntoBrowser:selectedPath]) {
            if (![[self history] containsObject:selectedPath]) {
                [self setHistory:[[self history] arrayByAddingObject:selectedPath]];
            }
            else {
                [m_historyController setSelectionIndex:[[self history] indexOfObject:selectedPath]];
            }
        }
    }
}

- (IBAction) selectFromHistory:sender
{
    int sel = [m_historyController selectionIndex];
    if (sel >= 0) {
        NSString *selectedPath = [[self history] objectAtIndex:sel];
        [self loadPathIntoBrowser:selectedPath];
    }
}

- (NSArray*) rootFiles
{
    NSString *initialPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"NewWindowInitialPath"];
    return [NSArray arrayWithObject:[initialPath stringByExpandingTildeInPath]];
}

- (NSArray*) history
{
    return a_history;
}

- (void) setHistory:(NSArray*)array
{
    [a_history release];
    a_history = [array retain];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (object == m_directoryStructure) {
        [self loadFile:self];
    }
}

@end

@implementation CodeBrowserController (Private)

+ (void)initialize{    
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *appDefaults = [NSDictionary
    dictionaryWithObjectsAndKeys:
        @"/opt/local/bin/source-highlight", @"SourceHighlightExecutablePath",
        @"~", @"NewWindowInitialPath",
        nil];    
        
    // Annoyingly, it seems we must register these both places:
    [defaults registerDefaults:appDefaults];
    [[NSUserDefaultsController sharedUserDefaultsController] setInitialValues:appDefaults];
}

- (NSData*) loadColoredDataForPath:(NSString*)selectedPath
{
    // Check the user defaults to find the location of the binary
    NSString *executablePath = [[NSUserDefaults standardUserDefaults] stringForKey:@"SourceHighlightExecutablePath"];

    // Initialize the task
    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:executablePath];
    NSArray *arguments;
    if ([[selectedPath pathExtension] caseInsensitiveCompare:@"m"] == NSOrderedSame) 
        // special case ".m" since source-highlight doesn't handle it properly
        arguments = [NSArray arrayWithObjects:@"-o", @"STDOUT", @"-s", @"C", selectedPath, nil];
    else 
        arguments = [NSArray arrayWithObjects:@"-o", @"STDOUT", selectedPath, nil];
    [task setArguments:arguments];
    NSPipe *pipe = [NSPipe pipe];
    [task setStandardOutput: pipe];            
    NSFileHandle *file = [pipe fileHandleForReading];            
    
    // Launch it, read contents, and wait for termination
    [task launch];
    NSData *contents = [file readDataToEndOfFile];
    [task waitUntilExit];
    if ([task terminationStatus] == 0) {
        return contents; // if successful, return contents
    }
    return nil;
}

- (BOOL) loadPathIntoBrowser:(NSString*)selectedPath
{
    if ([selectedPath pathIsFile]) {
        NSData *coloredData = [self loadColoredDataForPath:selectedPath];
        if (coloredData != nil) {
            NSURL *url = [NSURL fileURLWithPath:selectedPath];
            [[m_webView mainFrame] loadData:coloredData
                                   MIMEType:@"text/html" 
                           textEncodingName:@"utf-8" // make customizable?
                                    baseURL:url];
            return YES;
        }
    }
    return NO;            
}

@end
