/*
SRMainDocument.m

Author: Makoto Kinoshita

Copyright 2004 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 "SRDefaultsKey.h"

#import "SRAppDelegate.h"
#import "SRDocumentController.h"
#import "SRMainDocument.h"
#import "SRMainWindowController.h"
#import "SRPReferencesController.h"

#import "WebKitEx.h"

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";

@implementation SRMainDocument

//--------------------------------------------------------------//
// Initialize
//--------------------------------------------------------------//

- (void)_init
{
    // Load nib
    if (![NSBundle loadNibNamed:@"MainDocument" owner:self]) {
        // Fatal
        SR_FATAL(@"Could not load MainDocument.nib");
    }
    
    // Register key value observation
    [_content addObserver:self 
            forKeyPath:SRMainDocumentPrintBackground 
            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
{
    [_initialFileName release];
    _initialFileName = nil;
    [_initialURLString release];
    _initialURLString = nil;
    
    // Remove observers
    [_content removeObserver:self forKeyPath:SRMainDocumentPrintBackground];
    
    [super dealloc];
}

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

- (void)makeWindowControllersWithInitialFrameName:(NSString*)frameName 
        groupName:(NSString*)groupName
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Create main window controller
    SRMainWindowController* mainWindowController;
    mainWindowController = [[SRMainWindowController alloc] 
            initWithWindowNibName:@"MainWindow" initialFrameName:frameName initialGroupName:groupName];
    [self addWindowController:mainWindowController];
    [mainWindowController release];
    
    // 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
}

- (SRMainWindowController*)mainWindowController
{
    // Get window controllers
    NSArray*    windowControllers;
    windowControllers = [self windowControllers];
    if (!windowControllers || [windowControllers count] < 1) {
        return nil;
    }
    
    // Get first window controller
    id  windowController;
    windowController = [windowControllers objectAtIndex:0];
    if (![windowController isKindOfClass:[SRMainWindowController class]]) {
        return nil;
    }
    
    return windowController;
}

- (BOOL)_readWeblocFile:(NSString*)fileName
{
    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 ([self mainWindowController]) {
            [[self mainWindowController] openURLString:[textAndURL objectAtIndex:1]];
        }
        else {
            _initialURLString = [[textAndURL objectAtIndex:1] retain];
        }
        
        return YES;
    }
    
    return NO;
}

- (BOOL)readFromFile:(NSString*)fileName 
        ofType:(NSString*)docType
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // For webloc
    if ([[fileName pathExtension] isEqualToString:@"webloc"]) {
        return [self _readWeblocFile:fileName];
    }
    
    // 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])
    {
        // Open URL
        [[self mainWindowController] openURL:[NSURL fileURLWithPath:fileName]];
        
        return YES;
    }
    
    return NO;
}

- (BOOL)readFromURL:(NSURL*)absoluteURL 
		ofType:(NSString*)docType 
{
    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];
        }
    }
    
    // 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])
    {
        // Open URL
        [[self mainWindowController] openURL:absoluteURL];
        
		return YES;
	}
	return NO;
}

- (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 = [[self mainWindowController] selectedWebView];
    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
{
    // 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];
}

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

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

- (NSString*)displayName
{
    // Check data source of web view
    if ([[[[self mainWindowController] selectedWebView] mainFrame] dataSource]) {
        return [[[self mainWindowController] 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 = [[self mainWindowController] selectedWebView];
    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];
    
    // 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 -- Accessors --
//--------------------------------------------------------------//

- (NSView*)printAccessoryView
{
    return _printView;
}

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

- (void)printShowingPrintPanel:(BOOL)flag
{
    NSPrintOperation*   printOperation;
    
    // Get selected web view
    WebView*    webView;
    id          documentView;
    webView = [[self mainWindowController] selectedWebView];
    if (!webView) {
        return;
    }
    documentView = [[[webView mainFrame] frameView] documentView];
    
    // 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 = [SRPreferencesController defaultWebPreferences];
    [self setValue:[NSNumber numberWithBool:[preferences shouldPrintBackgrounds]] 
            forKey:SRMainDocumentPrintBackground];
    
    // Create print operation
    printOperation = [NSPrintOperation printOperationWithView:documentView printInfo:printInfo];
    [printOperation setShowPanels:flag];
    [printOperation setCanSpawnSeparateThread:YES];
    [printOperation setAccessoryView:_printView];
    
    // Run print operation
    [printOperation runOperationModalForWindow:[[self mainWindowController] window] 
            delegate:self 
            didRunSelector:@selector(_printOperationDidRun:success:contextInfo:) 
            contextInfo:NULL];
}

- (void)_printOperationDidRun:(NSPrintOperation*)printOperation 
        success:(BOOL)success 
        contextInfo:(void*)contextInfo
{
}

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

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

//--------------------------------------------------------------//
#pragma mark -- AppleScript accessor methods --
//--------------------------------------------------------------//

- (NSString*)source
{
    // Get selected web view
    WebView*    webView;
    webView = [[self mainWindowController] selectedWebView];
    if (!webView) {
        return @"";
    }
    
    // Get source
    NSString*   source;
    source = [[[[webView mainFrame] dataSource] representation] documentSource];
    if (!source) {
        return @"";
    }
    
    return source;
}

- (NSString*)text
{
    // Get selected web view
    WebView*    webView;
    webView = [[self mainWindowController] selectedWebView];
    if (!webView) {
        return @"";
    }
    
    // Get text
    id  documentView;
    documentView = [[[webView mainFrame] frameView] documentView];
    if (![documentView respondsToSelector:@selector(string)]) {
        return @"";
    }
    
    return [documentView string];
}

- (NSString*)URLString
{
    // Get selected web view
    WebView*    webView;
    webView = [[self mainWindowController] selectedWebView];
    if (!webView) {
        return @"";
    }
    
    // Get URL string
    NSString*   URLString;
    URLString = [[[[[webView mainFrame] dataSource] request] URL] _web_userVisibleString];
    if (!URLString) {
        return @"";
    }
    
    return URLString;
}

- (void)setURLString:(NSString*)URLString
{
    // Open URL string
    [[self mainWindowController] openURLString:URLString];
}

- (NSData*)PDF
{
    // Get selected web view and document view
    WebView*    webView;
    NSView*     documentView;
    webView = [[self mainWindowController] selectedWebView];
    documentView = [[[webView mainFrame] frameView] documentView];
    if (!documentView) {
        return nil;
    }
    
    // Get PDF data
    return [documentView dataWithPDFInsideRect:[documentView frame]];
}

//--------------------------------------------------------------//
#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 = [[self mainWindowController] selectedWebView];
        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 -- 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:SRMainDocumentPrintBackground]) {
            [self setPrintBackground:[[change objectForKey:@"new"] boolValue]];
            return;
        }
    }
}

@end

#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;
}
