/*
SRBrowserDocument.m

Author: Makoto Kinoshita

Copyright 2004-2006 The Shiira Project. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions 
  and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of 
  conditions and the following disclaimer in the documentation and/or other materials provided 
  with the distribution.

THIS SOFTWARE IS PROVIDED BY THE SHIIRA PROJECT ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE SHIIRA PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/

#import "SRBrowserDocument.h"
#import "SRBrowserController.h"
#import "SRDefaultKeys.h"

#import "SRMenu.h"

#import "SRPreference.h"
#import "SRPrefDefaultKeys.h"

// Document types
NSString*   SRCSSDocumentType = @"CSS style sheet";
NSString*   SRGIFDocumentType = @"GIF image";
NSString*   SRHTMLDocumentType = @"HTML document";
NSString*   SRHTMLNoExtensionDocumentType = @"HTML document (no extension)";
NSString*   SRJavaScriptDocumentType = @"JavaScript script";
NSString*   SRJPEGDocumentType = @"JPEG image";
NSString*   SRJPEG2000DocumentType = @"JPEG 2000 image";
NSString*   SRTextDocumentType = @"Plain text document";
NSString*   SRPNGDocumentType = @"PNG image";
NSString*   SRRTFDocumentType = @"Rich Text Format (RTF) document";
NSString*   SRTIFFDocumentType = @"TIFF image";
NSString*   SRURLDocumentType = @"Web site location";
NSString*   SRWeblocDocumentType = @"Web location";
NSString*   SRWebArchiveDocumentType = @"Web archive";
NSString*   SRICODocumentType = @"Windows icon image";
NSString*   SRXHTMLDocumentType = @"XHTML document";
NSString*   SRXMLDocumentType = @"XML document";
NSString*   SRRSSDocumentType = @"RSS document";
NSString*   SRRDFDocumentType = @"RDF document";
NSString*   SRPDFDocumentType = @"PDF document";

@implementation SRBrowserDocument

//--------------------------------------------------------------//
#pragma -- Initialize --
//--------------------------------------------------------------//

- (void)_init
{
    // Load nib
    if (![NSBundle loadNibNamed:@"BrowserDocument" owner:self]) {
        // Error
        NSLog(@"Could not load BrowserDocument.nib");
    }
    
    // Register key value observation
    [_content addObserver:self forKeyPath:SRBrowserDocumentPrintBackground 
            options:NSKeyValueObservingOptionNew context:NULL];
}

- (id)init
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Initialize
    [self _init];
    
    return self;
}

- (id)initWithContentsOfFile:(NSString*)fileName ofType:(NSString*)docType
{
    self = [super initWithContentsOfFile:fileName ofType:docType];
    if (!self) {
        return nil;
    }
    
    // Initialize
    [self _init];
    if (![[fileName pathExtension] isEqualToString:@"webloc"]) {
        _initialFileName = [fileName retain];
    }
    
    return self;
}

- (id)initWithContentsOfURL:(NSURL*)URL ofType:(NSString*)docType
{
    self = [super initWithContentsOfURL:URL ofType:docType];
    if (!self) {
        return nil;
    }
    
    // Initialize
    [self _init];
    
    return self;
}

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

//--------------------------------------------------------------//
#pragma -- Window controller --
//--------------------------------------------------------------//

- (void)makeWindowControllers
{
    [self makeWindowControllersWithFrameName:nil groupName:nil];
}

- (void)makeWindowControllersWithFrameName:(NSString*)frameName 
        groupName:(NSString*)groupName
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Create browser controller
    _browserController = [[SRBrowserController alloc] init];
    [_browserController addNewTabWithLabel:NSLocalizedString(@"Untitled", nil) 
            frameName:frameName groupName:groupName select:[defaults boolForKey:SRTabSelectNewTabs]];
    [self addWindowController:_browserController];
    
#if 0
    // Check file name
#if 1
    if (_initialFileName) {
        // Open file
        [mainWindowController openURL:[NSURL fileURLWithPath:_initialFileName]];
    }
    else if (_initialURLString) {
        // Open URL
        [mainWindowController openURLString:_initialURLString];
    }
#else
    NSString*   fileName;
    fileName = [self fileName];
    if (fileName) {
        // Open file
        [mainWindowController openURL:[NSURL fileURLWithPath:fileName]];
    }
#endif
#endif
}

- (SRBrowserController*)browserController
{
    return _browserController;
}

//--------------------------------------------------------------//
#pragma mark -- Opening document --
//--------------------------------------------------------------//

- (BOOL)_readWeblocFile:(NSString*)fileName
        inNewTab:(BOOL)newTab 
        select:(BOOL)select
{
    if ([[fileName pathExtension] isEqualToString:@"webloc"]) {
        // Get text and URL of webloc
        NSArray*    textAndURL;
        textAndURL = SRTextAndURLOfWeblocFile(fileName);
        if (!textAndURL || [textAndURL count] != 2) {
            return NO;
        }
        
        // Open URL
        if (_browserController) {
            if (newTab) {
                [_browserController 
                        openInNewTabURLString:[textAndURL objectAtIndex:1] select:select];
            }
            else {
                [_browserController openURLString:[textAndURL objectAtIndex:1]];
            }
        }
        else {
            _initialURLString = [[textAndURL objectAtIndex:1] retain];
        }
        
        return YES;
    }
    
    return NO;
}

- (BOOL)readFromFile:(NSString*)fileName 
        ofType:(NSString*)docType
{
    return [self readFromFile:fileName ofType:docType inNewTab:NO select:NO];
}

- (BOOL)readFromFile:(NSString*)fileName 
        ofType:(NSString*)docType 
        inNewTab:(BOOL)newTab 
        select:(BOOL)select
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // For webloc
    if ([[fileName pathExtension] isEqualToString:@"webloc"]) {
        return [self _readWeblocFile:fileName inNewTab:newTab select:select];
    }
    
    // For history
#if 0
    if ([docType isEqualToString:SRHistoryType]) {
        return YES;
    }
#endif
    
    // For other
    if ([docType isEqualToString:SRCSSDocumentType] || 
        [docType isEqualToString:SRGIFDocumentType] || 
        [docType isEqualToString:SRHTMLDocumentType] || 
        [docType isEqualToString:SRHTMLNoExtensionDocumentType] || 
        [docType isEqualToString:SRJavaScriptDocumentType] || 
        [docType isEqualToString:SRJPEGDocumentType] || 
        [docType isEqualToString:SRJPEG2000DocumentType] || 
        [docType isEqualToString:SRTextDocumentType] || 
        [docType isEqualToString:SRPNGDocumentType] || 
        [docType isEqualToString:SRRTFDocumentType] || 
        [docType isEqualToString:SRTIFFDocumentType] || 
        [docType isEqualToString:SRWebArchiveDocumentType] || 
        [docType isEqualToString:SRICODocumentType] || 
        [docType isEqualToString:SRXHTMLDocumentType] || 
        [docType isEqualToString:SRXMLDocumentType] || 
        [docType isEqualToString:SRRSSDocumentType] || 
        [docType isEqualToString:SRRDFDocumentType] || 
        [docType isEqualToString:SRPDFDocumentType])
    {
        // Open URL
        if (newTab) {
            [_browserController openInNewTabURL:[NSURL fileURLWithPath:fileName] select:select];
        }
        else {
            [_browserController openURL:[NSURL fileURLWithPath:fileName]];
        }
        
        return YES;
    }
    
    return NO;
}

- (BOOL)readFromURL:(NSURL*)absoluteURL 
		ofType:(NSString*)docType 
{
    return [self readFromURL:absoluteURL ofType:docType inNewTab:NO select:NO];
}

- (BOOL)readFromURL:(NSURL*)absoluteURL 
		ofType:(NSString*)docType 
        inNewTab:(BOOL)newTab 
        select:(BOOL)select
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // For webloc
    if ([[absoluteURL scheme] isEqualToString:@"file"]) {
        NSString*   fileName;
        fileName = [absoluteURL path];
        if ([[fileName pathExtension] isEqualToString:@"webloc"]) {
            return [self _readWeblocFile:fileName inNewTab:newTab select:select];
        }
        
#if 0
        // For history
        if ([[fileName pathExtension] isEqualToString:@"history"]) {
            // Get URL
            NSURL*  historyURL;
            historyURL = [[SRSpotlightController sharedInstance] URLWithHistoryFile:[absoluteURL path]];
            if (!historyURL) {
                return NO;
            }
            
            // Open URL
            if (newTab) {
                [[self mainWindowController] openInNewTabURL:historyURL select:select];
            }
            else {
                [[self mainWindowController] openURL:historyURL];
            }
            
            return YES;
        }
#endif
    }
    
    // For other
    if ([docType isEqualToString:SRCSSDocumentType] || 
        [docType isEqualToString:SRGIFDocumentType] || 
        [docType isEqualToString:SRHTMLDocumentType] || 
        [docType isEqualToString:SRHTMLNoExtensionDocumentType] || 
        [docType isEqualToString:SRJavaScriptDocumentType] || 
        [docType isEqualToString:SRJPEGDocumentType] || 
        [docType isEqualToString:SRJPEG2000DocumentType] || 
        [docType isEqualToString:SRTextDocumentType] || 
        [docType isEqualToString:SRPNGDocumentType] || 
        [docType isEqualToString:SRRTFDocumentType] || 
        [docType isEqualToString:SRTIFFDocumentType] || 
        [docType isEqualToString:SRWebArchiveDocumentType] || 
        [docType isEqualToString:SRICODocumentType] || 
        [docType isEqualToString:SRXHTMLDocumentType] || 
        [docType isEqualToString:SRXMLDocumentType] || 
        [docType isEqualToString:SRRSSDocumentType] || 
        [docType isEqualToString:SRRDFDocumentType] || 
        [docType isEqualToString:SRPDFDocumentType])
    {
        // Open URL
        if (newTab) {
            [_browserController openInNewTabURL:absoluteURL select:select];
        }
        else {
            [_browserController openURL:absoluteURL];
        }
        
		return YES;
	}
	return NO;
}

//--------------------------------------------------------------//
#pragma mark -- Saving document --
//--------------------------------------------------------------//

- (NSData*)dataRepresentationOfType:(NSString*)docType
{
    // Set default file type
    if ([docType isEqualToString:SRHTMLDocumentType] || 
        [docType isEqualToString:SRHTMLNoExtensionDocumentType] || 
        [docType isEqualToString:SRWebArchiveDocumentType])
    {
        NSUserDefaults* defaults;
        defaults = [NSUserDefaults standardUserDefaults];
        
        if ([docType isEqualToString:SRWebArchiveDocumentType]) {
            [defaults setObject:SRWebArchiveDocumentType forKey:SRSaveFileFormat];
        }
        else {
            [defaults setObject:SRWebArchiveDocumentType forKey:SRHTMLDocumentType];
        }
    }
    
    // Get web view
    WebView*    webView;
    webView = [[_browserController selectedPageController] webView];
    if (!webView) {
        return nil;
    }
    
    // For Web archive
    if ([docType isEqualToString:SRWebArchiveDocumentType]) {
        // Get web archive data
        WebArchive* archive;
        archive = [[[webView mainFrame] dataSource] webArchive];
        return [archive data];
    }
    
    // Other
    // Get data
    return [[[webView mainFrame] dataSource] data];
}

- (BOOL)writeToURL:(NSURL*)absoluteURL 
        ofType:(NSString*)docType
{
    return [self writeToURL:absoluteURL ofType:docType error:NULL];
}

- (BOOL)writeToURL:(NSURL*)absoluteURL 
        ofType:(NSString*)docType 
        error:(NSError**)error
{
    // Get data
    NSData* data;
    data = [self dataRepresentationOfType:docType];
    if (!data) {
        return NO;
    }
    
    // Set default file type
    if ([docType isEqualToString:SRHTMLDocumentType] || 
        [docType isEqualToString:SRHTMLNoExtensionDocumentType] || 
        [docType isEqualToString:SRWebArchiveDocumentType])
    {
        NSUserDefaults* defaults;
        defaults = [NSUserDefaults standardUserDefaults];
        
        if ([docType isEqualToString:SRWebArchiveDocumentType]) {
            [defaults setObject:SRWebArchiveDocumentType forKey:SRSaveFileFormat];
        }
        else {
            [defaults setObject:SRWebArchiveDocumentType forKey:SRHTMLDocumentType];
        }
    }
    
    // Write it to file
    return [data writeToURL:absoluteURL atomically:YES];
}

//--------------------------------------------------------------//
#pragma mark -- Document management --
//--------------------------------------------------------------//

- (NSString*)fileName
{
    // Check data source of web view
    if ([[[[_browserController selectedPageController] webView] mainFrame] dataSource]) {
        return [[_browserController window] title];
    }
    
    return @"";
}

- (NSString*)displayName
{
    // Check data source of web view
    if ([[[[_browserController selectedPageController] webView] mainFrame] dataSource]) {
        return [[_browserController window] title];
    }
    
    return [super displayName];
}

- (NSString*)_decideToUseHTMLorWebArchive:(NSString*)fileType;
{
    if (![fileType isEqualToString:SRHTMLDocumentType] && 
        ![fileType isEqualToString:SRHTMLNoExtensionDocumentType] && 
        ![fileType isEqualToString:SRWebArchiveDocumentType])
    {
        return fileType;
    }
    
    // Get default file type
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    NSString*   defaultFileType;
    defaultFileType = [defaults stringForKey:SRSaveFileFormat];
    if (!defaultFileType) {
        return fileType;
    }
    
    // Use default
    if ([defaultFileType isEqualToString:SRWebArchiveDocumentType]) {
        return SRWebArchiveDocumentType;
    }
    else {
        return SRHTMLDocumentType;
    }
}

- (NSString*)fileType
{
    // Get selected web view
    WebView*    webView;
    webView = [[_browserController selectedPageController] webView];
    if (!webView) {
        return nil;
    }
    
    // Get file extension
    NSString*   fileName;
    NSString*   fileExtension;
    fileName = [[[[[[webView mainFrame] dataSource] request] URL] path] lastPathComponent];
    fileExtension = [fileName pathExtension];
    
    // Get file type from extension
    NSString*   fileType;
    if (fileExtension && [fileExtension length] > 0) {
        fileType = [[NSDocumentController sharedDocumentController] 
                typeFromFileExtension:fileExtension];
        if (fileType) {
            return [self _decideToUseHTMLorWebArchive:fileType];
        }
    }
    
    // Get MIME type
    NSString*   mimeType;
    mimeType = [[[[webView mainFrame] dataSource] response] MIMEType];
    
    // Get file type from MIME type
    if (mimeType && [mimeType length] > 0) {
        fileType = [[NSDocumentController sharedDocumentController] typeFromMIMEType:mimeType];
        if (fileType) {
            return [self _decideToUseHTMLorWebArchive:fileType];
        }
    }
    
    // Return MIME type as file type
    return mimeType;
}

- (NSArray*)writableTypesForSaveOperation:(NSSaveOperationType)saveOperation
{
    // Get file type
    NSString*   fileType;
    fileType = [self fileType];
    if (!fileType) {
        return nil;
    }
    
    // For HTML type
    if ([fileType isEqualToString:SRHTMLDocumentType] || 
        [fileType isEqualToString:SRWebArchiveDocumentType])
    {
        // Add Web archive
        return [NSArray arrayWithObjects:SRHTMLDocumentType, SRWebArchiveDocumentType, nil];
    }
    
    return [NSArray arrayWithObject:fileType];
}

- (BOOL)isDocumentEdited
{
    // Never edited
    return NO;
}

- (void)updateChangeCount:(NSDocumentChangeType)changeType
{
    // Do nothing
}

//--------------------------------------------------------------//
#pragma mark -- Menu validation --
//--------------------------------------------------------------//

- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
{
    // Get tag
    int tag;
    tag = [menuItem tag];
    
    switch (tag) {
    // File menu
    case SRSaveAsTag: {
        // Get selected web view
        WebView*    webView;
        webView = [[_browserController selectedPageController] webView];
        if (!webView) {
            return NO;
        }
        
        // Get data source
        WebDataSource*  dataSource;
        dataSource = [[webView mainFrame] dataSource];
        if (!dataSource) {
            return NO;
        }
        
        return [dataSource data] != nil;
    }
    }
    
    return [super validateMenuItem:menuItem];
}

//--------------------------------------------------------------//
#pragma mark -- View --
//--------------------------------------------------------------//

- (NSView*)printAccessoryView
{
    return _printView;
}

//--------------------------------------------------------------//
#pragma mark -- Printing --
//--------------------------------------------------------------//

- (void)printShowingPrintPanel:(BOOL)flag
{
    NSPrintOperation*   printOperation;
    
    // Get selected web view
    WebView*    webView;
    id          documentView;
    webView = [[_browserController selectedPageController] webView];
    if (!webView) {
        return;
    }
    documentView = [[[webView mainFrame] frameView] documentView];
    [documentView retain];
    
    // Get print info
    NSPrintInfo*    printInfo;
    printInfo = [self printInfo];
    
    [printInfo setTopMargin:15.0];
    [printInfo setBottomMargin:15.0];
    [printInfo setLeftMargin:15.0];
    [printInfo setRightMargin:15.0];
    [printInfo setHorizontallyCentered:NO];
    [printInfo setVerticallyCentered:NO];
    
    // Set properties of print panel
    WebPreferences* preferences;
    preferences = [SRPreference webPreferences];
    [self setValue:[NSNumber numberWithBool:[preferences shouldPrintBackgrounds]] 
            forKey:SRBrowserDocumentPrintBackground];
    
    // Create print operation
    printOperation = [NSPrintOperation printOperationWithView:documentView printInfo:printInfo];
    [printOperation setShowPanels:flag];
    [printOperation setCanSpawnSeparateThread:NO];
    [printOperation setAccessoryView:_printView];
    
    // Run print operation
    [printOperation runOperationModalForWindow:[_browserController window] 
            delegate:self 
            didRunSelector:@selector(_printOperationDidRun:success:contextInfo:) 
            contextInfo:documentView];
}

- (void)_printOperationDidRun:(NSPrintOperation*)printOperation 
        success:(BOOL)success 
        contextInfo:(void*)contextInfo
{
    // contextInfo is document view
    [(id)contextInfo release];
}

//--------------------------------------------------------------//
#pragma mark -- Accessors --
//--------------------------------------------------------------//

- (void)setPrintBackground:(BOOL)isPrintBackground
{
    // Set web preferences
    WebPreferences* preferences;
    preferences = [SRPreference webPreferences];
    [preferences setShouldPrintBackgrounds:isPrintBackground];
}

//--------------------------------------------------------------//
#pragma mark -- Key value observation --
//--------------------------------------------------------------//

- (void)observeValueForKeyPath:(NSString*)keyPath 
        ofObject:(id)object 
        change:(NSDictionary*)change 
        context:(void *)context
{
    // For print controller
    if (object == _content) {
        // Print background
        if ([keyPath isEqualToString:SRBrowserDocumentPrintBackground]) {
            [self setPrintBackground:[[change objectForKey:@"new"] boolValue]];
            return;
        }
    }
}

@end

#pragma mark -

//--------------------------------------------------------------//
#pragma mark -- Utility --
//--------------------------------------------------------------//

NSArray* SRTextAndURLOfWeblocFile(
        NSString* weblocFile)
{
    OSErr       err;
    OSStatus    status;
    
    // Create FSSpec
    Boolean isDir;
    FSRef   fsRef;
    FSSpec  fsSpec;
    status = FSPathMakeRef((const UInt8*)[weblocFile fileSystemRepresentation], &fsRef, &isDir);
    if (status != noErr || isDir) {
        return nil;
    }
    err = FSGetCatalogInfo(&fsRef, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL);
    if (err != noErr) {
        return nil;
    }
    
    // Open resource file
    short   fileRef;
    fileRef = FSpOpenResFile(&fsSpec, 0);
    
    short   resCount;
    Handle  handle;
    
    // Get 'TEXT' resource
    NSString*   text = nil;
    resCount = Count1Resources('TEXT');
    if (resCount == 1) {
        handle = Get1IndResource('TEXT', 1);
        if (handle) {
            // Create NSString
            text = [NSString stringWithCString:*handle length:GetHandleSize(handle)];
        }
    }
    
    // Get 'url ' resource
    NSString*   URLString = nil;
    resCount = Count1Resources('url ');
    if (resCount == 1) {
        handle = Get1IndResource('url ', 1);
        if (handle) {
            // Create NSString
            URLString = [NSString stringWithCString:*handle length:GetHandleSize(handle)];
        }
    }
    
    // Close resource file
    CloseResFile(fileRef);
    
    if (text && URLString) {
        return [NSArray arrayWithObjects:text, URLString, nil];
    }
    
    return nil;
}
