/*
SRSafariBookmarkShelf.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 "SRBookmarkProxy.h"
#import "SRAppControllerProxy.h"
#import "SRBrowserControllerProxy.h"

#import "SRBookmarkContextMenu.h"

#import "SRPrefDefaultKeys.h"

#import "SRSafariBookmarkShelf.h"

#import "SRConstants.h"

@implementation SRSafariBookmarkShelf

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

- (id)initWithBundle:(NSBundle*)bundle
{
    self = [super initWithBundle:bundle];
    if (!self) {
        return nil;
    }
    
    return self;
}

- (void)didLoad
{
    // Create buttons
    _tableButton = [[NSClassFromString(@"HMButton") alloc] initWithFrame:NSMakeRect(0, 0, 25, 19)];
    [_tableButton setButtonType:NSMomentaryChangeButton];
    [_tableButton setBezelStyle:NSRegularSquareBezelStyle];
    [_tableButton setBordered:NO];
    [_tableButton setImage:[NSImage imageNamed:@"shelfTableView"]];
    [_tableButton setSelectedImage:[NSImage imageNamed:@"shelfPressedBackground"]];
    [_tableButton setTarget:self];
    [_tableButton setAction:@selector(selectDisplayAction:)];
    
    _browserButton = [[NSClassFromString(@"HMButton") alloc] initWithFrame:NSMakeRect(0, 0, 25, 19)];
    [_browserButton setButtonType:NSMomentaryChangeButton];
    [_browserButton setBezelStyle:NSRegularSquareBezelStyle];
    [_browserButton setBordered:NO];
    [_browserButton setImage:[NSImage imageNamed:@"shelfNodeBrowser"]];
    [_browserButton setSelectedImage:[NSImage imageNamed:@"shelfPressedBackground"]];
    [_browserButton setTarget:self];
    [_browserButton setAction:@selector(selectDisplayAction:)];
    
    _dividerViewView = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 2, 19)];
    [_dividerViewView setImage:[NSImage imageNamed:@"shelfBarDivider"]];
    
    _contextButton = [[NSClassFromString(@"HMMenuButton") alloc] initWithFrame:NSMakeRect(0, 0, 31, 19)];
    [_contextButton setButtonType:NSMomentaryChangeButton];
    [_contextButton setBezelStyle:NSRegularSquareBezelStyle];
    [_contextButton setBordered:NO];
    [_contextButton setImage:[NSImage imageNamed:@"shelfContext"]];
    [_contextButton setSelectedImage:[NSImage imageNamed:@"shelfPressedBackgroundL"]];
    [_contextButton setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
    [_contextButton setTarget:self];
    [_contextButton setDelegate:self];
    
    // Configure outline
    [_outlineView setDoubleAction:@selector(openBookmarkAction:)];
    [_outlineView setTarget:self];
    //[_outlineView setAutosaveExpandedItems:YES];
    [_outlineView setAutoresizesOutlineColumn:NO];
    
    // Configure browser
    [_browser _setBorderType:NSNoBorder];
    [_browser setDoubleAction:@selector(openBookmarkAction:)];
    [_browser setTarget:self];
    
    // Set image text cell
    NSTableColumn*      column;
    NSCell*             oldCell;
    id                  cell;
    column = [_outlineView tableColumnWithIdentifier:@"title"];
    oldCell = [column dataCell];
    cell = [[NSClassFromString(@"HMImageTextFieldCell") alloc] init];
    if (cell) {
        [cell setFont:[oldCell font]];
        [column setDataCell:cell];
        [cell release];
    }
    
    // Configure split view
    [_splitView setSplitType:HMSplitWide];
    
    // Configure tree controller
    NSSortDescriptor*   sortDescriptor;
    sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"index" ascending:YES];
    [_bookmarkTreeController setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
    [sortDescriptor release];
    [_bookmarkTreeController setIndexKeyPath:@"index"];
    [_bookmarkTreeController setDelegate:self];
    
    // Prepare content
    [_bookmarkTreeController prepareContent];
    
    // Set display style
    [self setDisplayStyle:SRShelfOutlineStyle];
}

- (void)dealloc
{
    [_tableButton release], _tableButton = nil;
    [_browserButton release], _browserButton = nil;
    [_dividerViewView release], _dividerViewView = nil;
    [_contextButton release], _contextButton = nil;
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Bar buttons --
//--------------------------------------------------------------//

- (NSArray*)rightBarButtons
{
    return [NSArray arrayWithObjects:
            _tableButton, _browserButton, _dividerViewView, _contextButton, nil];
}

//--------------------------------------------------------------//
#pragma mark -- Bookmark --
//--------------------------------------------------------------//

- (NSArray*)selectedBookmarks
{
    // Get selected objects
    return [_bookmarkTreeController selectedObjects];
}

- (NSMenu*)_contextMenuForView:(id)view 
        event:(NSEvent*)event
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get modifier key flag
    unsigned int    modifierFlags;
    unsigned int    cmdFlag, optionFlag, shiftFlag;
    modifierFlags = [event modifierFlags];
    cmdFlag = modifierFlags & NSCommandKeyMask;
    optionFlag = modifierFlags & NSAlternateKeyMask;
    shiftFlag = modifierFlags & NSShiftKeyMask;
    
    // Create array for tags
    NSMutableArray* tags;
    tags = [NSMutableArray array];
    
    // Select bookmark under the cursor
    NSPoint point;
    int     rowUnderPoint;
    point = [view convertPoint:[event locationInWindow] 
            fromView:nil];
    rowUnderPoint = [view rowAtPoint:point];
    if (rowUnderPoint >=0) {
        if (![[view selectedRowIndexes] containsIndex:rowUnderPoint]) {
            [view selectRowIndexes:[NSIndexSet indexSetWithIndex:rowUnderPoint] 
                    byExtendingSelection:NO];
        }
    }
    
    // Get selected rows
    NSIndexSet* indexSet;
    indexSet = [view selectedRowIndexes];
    
    id<SRBookmark>  bookmark = nil;
    NSArray*        bookmarks = nil;
    
    // No bookmark is selected
    if ([indexSet count] == 0) {
    }
    else {
        // Just one bookmark is selected
        if ([indexSet count] == 1) {
            // Get bookmark
            bookmark = [[self selectedBookmarks] objectAtIndex:0];
        }
        else {
            // Get bookmarks
            bookmarks = [self selectedBookmarks];
        }
        
        // Check tab availability
        BOOL    selectNewTabs;
        selectNewTabs = [defaults boolForKey:SRTabSelectNewTabs];
        
        // Case of one HTML bookmark
        if (bookmark && ![[bookmark isFolder] boolValue]) {
            // Create tag array
            [tags addObject:[NSNumber numberWithInt:SROpenBookmarkTag]];
            
            if ((selectNewTabs && !shiftFlag) || 
                (!selectNewTabs && shiftFlag))
            {
                [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInNewWindowTag]];
                [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInNewTabTag]];
            }
            else {
                [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInNewBackgroundWindowTag]];
                [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInNewBackgroundTabTag]];
            }
            [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInNewWindowTag]];
        }
        // Case of one bookmark folder or multiple bookmarks
        if ((bookmark && [[bookmark isFolder] boolValue]) || bookmarks) {
            // Create tag array
            [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInTabsTag]];
        }
    }
    
    if ([tags count] > 0) {
        // Copy menu
        NSMenu* menu;
        menu = HMCopyMenuWithTags([SRBookmarkContextMenu contextMenu], tags, self);
        
        // Set represented object
        if (bookmark) {
            [[menu itemArray] makeObjectsPerformSelector:@selector(setRepresentedObject:) 
                    withObject:bookmark];
        }
        else {
            [[menu itemArray] makeObjectsPerformSelector:@selector(setRepresentedObject:) 
                    withObject:bookmarks];
        }
        
        return menu;
    }
    
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- Display style --
//--------------------------------------------------------------//

- (void)_updateDisplayStyle
{
    // Get view frame
    NSRect  frame;
    frame = [_tabView bounds];
    
    // Decide display style
    int displayStyle = SRShelfOutlineStyle;
    if ([_tableButton isSelected]) {
        displayStyle = SRShelfOutlineStyle;
    }
    else if ([_browserButton isSelected]) {
        displayStyle = SRShelfBrowserStyle;
    }
    
    // For outline
    if (displayStyle == SRShelfOutlineStyle) {
        [_tabView selectTabViewItemWithIdentifier:@"outline"];
        [_outlineView setFrame:frame];
    }
    // For browser
    if (displayStyle == SRShelfBrowserStyle) {
        [_tabView selectTabViewItemWithIdentifier:@"browser"];
        [_browser setFrame:frame];
    }
}

- (void)setDisplayStyle:(int)displayStyle
{
    // Select button
    switch (displayStyle) {
    case SRShelfOutlineStyle: {
        [_tableButton setSelected:YES];
        [_browserButton setSelected:NO];
        break;
    }
    case SRShelfBrowserStyle: {
        [_tableButton setSelected:NO];
        [_browserButton setSelected:YES];
        break;
    }
    }
    
    // Update display style
    [self _updateDisplayStyle];
}

- (void)_updateLowerView
{
    // Get content size
    NSSize  contentSize;
    contentSize = [_scrollView contentSize];
    
    // Get selected bookmarks
    NSArray*    bookmarks;
    bookmarks = [self selectedBookmarks];
    if ([bookmarks count] == 0) {
        // Clear document view
        [_scrollView setDocumentView:nil];
        return;
    }
    
    // Get old view
    NSView* oldView;
    oldView = [_scrollView documentView];
    
    // For multiple view
    if ([bookmarks count] > 1) {
        return;
    }
    
    // Get selected bookmark
    id<SRBookmark>  bookmark;
    NSString*       type;
    bookmark = [bookmarks objectAtIndex:0];
    type = [bookmark type];
    
    // For HTML
    id  newView = nil;
    if ([type isEqualToString:SRBookmarkTypeHTML]) {
        newView = _bookmarkView;
    }
    
    // For folder
    if ([type isEqualToString:SRBookmarkTypeFolder] || 
        [type isEqualToString:SRBookmarkTypeBookmarkBar] || 
        [type isEqualToString:SRBookmarkTypeBookmarkMenu])
    {
        newView = _folderView;
    }
    
    // For JavaScript
    if ([type isEqualToString:SRBookmarkTypeJavaScript]) {
        newView = _javaScriptView;
    }
    
    // For smart
    if ([type isEqualToString:SRBookmarkTypeSmart]) {
        newView = _smartView;
    }
    
    // Swap view
    if (newView != oldView) {
        NSRect  frame;
        frame = [newView frame];
        frame.origin.x = 0;
        frame.origin.y = 0;
        frame.size.width = contentSize.width;
        [newView setFrame:frame];
        
        [_scrollView setDocumentView:newView];
    }
}

//--------------------------------------------------------------//
#pragma mark -- Action --
//--------------------------------------------------------------//

- (void)openBookmarkAction:(id)sender
{
    // Get selected bookmarks
    NSArray*        bookmarks;
    id<SRBookmark>  bookmark;
    bookmarks = [self selectedBookmarks];
    if (!bookmarks || [bookmarks count] == 0) {
        return;
    }
    bookmark = [bookmarks objectAtIndex:0];
    
    // For folder
    if ([[bookmark isFolder] boolValue]) {
        // Get selected row
        int row;
        row = [_outlineView selectedRow];
        if (row == -1) {
            return;
        }
        
        // Get item
        id  item;
        item = [_outlineView itemAtRow:row];
        
        // Toogle expansion
        if ([_outlineView isItemExpanded:item]) {
            [_outlineView collapseItem:item];
        }
        else {
            [_outlineView expandItem:item];
        }
    }
    // Other types
    else {
        // Open bookmark
        [[self browserController] openInNewTabBookmark:bookmark];
    }
}

- (void)selectDisplayAction:(id)sender
{
    // Set display style
    if (sender == _tableButton) {
        [self setDisplayStyle:SRShelfOutlineStyle];
    }
    if (sender == _browserButton) {
        [self setDisplayStyle:SRShelfBrowserStyle];
    }
}

- (void)openBookmarkInNewWindowAction:(id)sender
{
    // Get selected bookmarks
    NSArray*        bookmarks;
    id<SRBookmark>  bookmark;
    bookmarks = [self selectedBookmarks];
    if (!bookmarks || [bookmarks count] == 0) {
        return;
    }
    bookmark = [bookmarks objectAtIndex:0];
    
    // For folder
    if ([[bookmark isFolder] boolValue]) {
        return;
    }
    
    // Open bookmark
    [[self browserController] openInNewWindowBookmark:bookmark];
}

- (void)openBookmarkInNewBackgroundTabAction:(id)sender
{
    // Get selected bookmarks
    NSArray*        bookmarks;
    id<SRBookmark>  bookmark;
    bookmarks = [self selectedBookmarks];
    if (!bookmarks || [bookmarks count] == 0) {
        return;
    }
    bookmark = [bookmarks objectAtIndex:0];
    
    // For folder
    if ([[bookmark isFolder] boolValue]) {
        return;
    }
    
    // Open bookmark
    [[self browserController] openInNewTabBookmark:bookmark select:NO];
}

- (void)openBookmarkInNewTabAction:(id)sender
{
    // Get selected bookmarks
    NSArray*        bookmarks;
    id<SRBookmark>  bookmark;
    bookmarks = [self selectedBookmarks];
    if (!bookmarks || [bookmarks count] == 0) {
        return;
    }
    bookmark = [bookmarks objectAtIndex:0];
    
    // For folder
    if ([[bookmark isFolder] boolValue]) {
        return;
    }
    
    // Open bookmark
    [[self browserController] openInNewTabBookmark:bookmark select:YES];
}

- (void)openBookmarkInTabsAction:(id)sender
{
    // Get selected bookmarks
    NSArray*    bookmarks;
    bookmarks = [self selectedBookmarks];
    if (!bookmarks || [bookmarks count] == 0) {
        return;
    }
    
    // Open bookmark
    [[self browserController] openInTabsBookmarks:bookmarks];
}

- (void)openBookmarkInNewBackgroundWindowAction:(id)sender
{
    // Get selected bookmarks
    NSArray*        bookmarks;
    id<SRBookmark>  bookmark;
    bookmarks = [self selectedBookmarks];
    if (!bookmarks || [bookmarks count] == 0) {
        return;
    }
    bookmark = [bookmarks objectAtIndex:0];
    
    // For folder
    if ([[bookmark isFolder] boolValue]) {
        return;
    }
    
    // Open bookmark
    [[self browserController] openInNewBackgroundWindowBookmark:bookmark];
}

//--------------------------------------------------------------//
#pragma mark -- NSOutlineView delegate --
//--------------------------------------------------------------//

- (void)outlineView:(NSOutlineView*)outlineView 
        willDisplayCell:(id)cell 
        forTableColumn:(NSTableColumn*)column 
        item:(id)item
{
    // Bookmark outline
    if (outlineView == _outlineView) {
        // Get column identifier
        id  identifier;
        identifier = [column identifier];
        
        // For title column
        if ([identifier isEqualToString:@"title"]) {
            // Set image
            NSImage*    image = nil;
            if ([item respondsToSelector:@selector(observedObject)]) {
                image = [[item performSelector:@selector(observedObject)] valueForKey:@"icon"];
            }
            if (image) {
                [cell setImage:image];
            }
            
            return;
        }
    }
}

- (void)outlineViewSelectionDidChange:(NSNotification*)notification
{
    // Update appearance
    [self _updateLowerView];
}

//--------------------------------------------------------------//
#pragma mark -- NSOutlineViewEx delegate --
//--------------------------------------------------------------//

- (NSMenu*)outlineView:(NSOutlineView*)outlineView menuForEvent:(NSEvent*)event
{
    return [self _contextMenuForView:outlineView event:event];
}

//--------------------------------------------------------------//
#pragma mark -- NSBrowser delegate --
//--------------------------------------------------------------//

- (int)browser:(NSBrowser*)browser numberOfRowsInColumn:(int)column
{
    // Browser data source is provided by Core Data
    return 0;
}

- (void)browser:(NSBrowser*)browser willDisplayCell:(id)cell atRow:(int)row column:(int)column
{
    // Get bookmark at row and column
    NSArray*    sortDescriptors;
    NSArray*    bookmarks;
    sortDescriptors = [_bookmarkTreeController sortDescriptors];
    bookmarks = [[_bookmarkTreeController content] sortedArrayUsingDescriptors:sortDescriptors];
    
    int i;
    for (i = 0; i < column; i++) {
        int selectedRow;
        selectedRow = [browser selectedRowInColumn:i];
        
        if (selectedRow > [bookmarks count]) {
            bookmarks = nil;
            break;
        }
        
        bookmarks = [[[bookmarks objectAtIndex:selectedRow] valueForKey:@"children"] allObjects];
        bookmarks = [bookmarks sortedArrayUsingDescriptors:sortDescriptors];
        if (!bookmarks) {
            break;
        }
    }
    
    if (!bookmarks || [bookmarks count] < row) {
        return;
    }
    
    id  bookmark;
    bookmark = [bookmarks objectAtIndex:row];
    
    // Set image
    [cell setImage:[bookmark valueForKey:@"icon"]];
}

//--------------------------------------------------------------//
#pragma mark -- HMMenuButton delegate --
//--------------------------------------------------------------//

- (NSMenu*)menuButton:(HMMenuButton*)menuButton menuForEvent:(NSEvent*)event
{
    return [self _contextMenuForView:_outlineView event:event];
}

//--------------------------------------------------------------//
#pragma mark -- HMSplitView delegate --
//--------------------------------------------------------------//

- (float)splitView:(NSSplitView*)splitView 
        constrainMaxCoordinate:(float)proposedMax ofSubviewAt:(int)offset
{
    return proposedMax;
}

//--------------------------------------------------------------//
#pragma mark -- HMTreeController delegate --
//--------------------------------------------------------------//

- (void)hmTreeController:(HMTreeController*)treeController 
        outlineView:(NSOutlineView*)outlineView 
        writeItems:(NSArray*)items 
        toPasteboard:(NSPasteboard*)pboard
{
    // Get URLs and titles
    NSMutableArray* URLs;
    NSMutableArray* titles;
    NSEnumerator*   enumerator;
    id              item;
    URLs = [NSMutableArray array];
    titles = [NSMutableArray array];
    enumerator = [items objectEnumerator];
    while (item = [enumerator nextObject]) {
        // Get bookmark
        id<SRBookmark>  bookmark;
        bookmark = [item observedObject];
        if (!bookmark) {
            continue;
        }
        
        // Get URL and title
        NSString*   URLString;
        NSURL*      URL = nil;
        NSString*   title;
        URLString = [bookmark urlString];
        if (URLString) {
            URL = [NSURL URLWithString:URLString];
        }
        title = [bookmark title];
        
        if (URL && title) {
            [URLs addObject:URL];
            [titles addObject:title];
        }
    }
    
    // Write URLs and titles
    if ([URLs count] > 0) {
        [pboard addTypes:[NSArray arrayWithObject:WebURLsWithTitlesPboardType] owner:nil];
        [WebURLsWithTitles writeURLs:URLs andTitles:titles toPasteboard:pboard];
    }
}

@end
