/*
SRSBHistoryController.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 "SRContextMenu.h"

#import "SRAppDelegate.h"
#import "SRMainWindowController.h"
#import "SRSBHistoryController.h"

#import "SRImageTextCell.h"

#import "WebKitEx.h"

static NSString*   _SRHistoryTabIdentifier = @"history";
static NSString*   _SRFindResultsTabIdentifier = @"findResult";

@implementation SRSBHistoryController

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

- (id)initWithMainWindowController:(SRMainWindowController*)mainWindowController 
        sideBarController:(SRSideBarController*)sideBarController
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Initialize instance variables
    _mainWindowController = mainWindowController;
    _sideBarController = sideBarController;
    
    return self;
}

static int  _historyTabPaddingTop = -1;
static int  _historyTabPaddingBottom = -1;

- (void)awakeFromNib
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    NSTableColumn*      column;
    NSCell*             oldCell;
    SRImageTextCell*    cell;
    
    // Configur history outline
    column = [_historyOutline tableColumnWithIdentifier:@"history"];
    oldCell = [column dataCell];
    cell = [[SRImageTextCell alloc] init];
    [cell autorelease];
    [cell setFont:[oldCell font]];
    [column setDataCell:cell];
    
    [_historyOutline setTarget:self];
    [_historyOutline setAction:@selector(historySelectedAction:)];
    [_historyOutline setDoubleAction:@selector(openHistoryItemAction:)];
    
    // Configure history find table
    column = [_findTable tableColumnWithIdentifier:@"results"];
    oldCell = [column dataCell];
    cell = [[SRImageTextCell alloc] init];
    [cell autorelease];
    [cell setFont:[oldCell font]];
    [column setDataCell:cell];
    
    [_findTable setTarget:self];
    [_findTable setAction:@selector(historySelectedAction:)];
    [_findTable setDoubleAction:@selector(openHistoryItemAction:)];
    
    // Select tab item
    [_historyTab selectTabViewItemWithIdentifier:_SRHistoryTabIdentifier];
    
    // Reload history
    [self reloadHistory];
    
    [_historyOutline setAutosaveExpandedItems:YES];
    
    // Get paddings
    if (_historyTabPaddingTop < 0) {
        NSRect  historyTabFrame, historyFrame;
        historyTabFrame = [_historyTab frame];
        historyFrame = [_historyView frame];
        historyFrame.origin = NSZeroPoint;
        _historyTabPaddingTop = (historyFrame.origin.y + historyFrame.size.height) - 
                (historyTabFrame.origin.y + historyTabFrame.size.height);
        _historyTabPaddingBottom = historyTabFrame.origin.y - historyFrame.origin.y;
    }
    
    // Set frame change notifications
    [_historyView setPostsFrameChangedNotifications:YES];
    
    // Register notification
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center addObserver:self 
            selector:@selector(webHistoryItemsUpdated:) 
            name:WebHistoryItemsAddedNotification 
            object:nil];
    [center addObserver:self 
            selector:@selector(webHistoryItemsUpdated:) 
            name:WebHistoryItemsRemovedNotification 
            object:nil];
    [center addObserver:self 
            selector:@selector(webHistoryItemsUpdated:) 
            name:WebHistoryAllItemsRemovedNotification 
            object:nil];
    [center addObserver:self 
            selector:@selector(historyViewFrameDidChange:) 
            name:NSViewFrameDidChangeNotification 
            object:_historyView];
    
    // Register key value observation
    [defaults addObserver:self 
            forKeyPath:SRIconUseFavicon 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRIconUseFaviconSideBar 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
}

- (void)dealloc
{
    // Release outlets
    [_historyView release];
    _historyView = nil;
    [_findResults release];
    _findResults = nil;
    
    // Remove notification
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center removeObserver:self];
    
    // Remove observers
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    [defaults removeObserver:self forKeyPath:SRIconUseFavicon];
    [defaults removeObserver:self forKeyPath:SRIconUseFaviconSideBar];
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- First responder --
//--------------------------------------------------------------//

- (BOOL)acceptsFirstResponder
{
    return YES;
}

- (void)_updateNextResponder
{
    // Get selected table
    id  table;
    table = [self selectedHistoryView];
    
    if ([table nextResponder] != self) {
        [self setNextResponder:[table nextResponder]];
        [table setNextResponder:self];
    }
}

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

- (NSView*)view
{
    return _historyView;
}

- (id)selectedHistoryView
{
    // Get tab item identifier
    id  identifier;
    identifier = [[_historyTab selectedTabViewItem] identifier];
    
    // Return selected table
    if ([identifier isEqualToString:_SRHistoryTabIdentifier]) {
        return _historyOutline;
    }
    if ([identifier isEqualToString:_SRFindResultsTabIdentifier]) {
        return _findTable;
    }
    
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- Controller --
//--------------------------------------------------------------//

- (SRMainWindowController*)mainWindowController
{
    return _mainWindowController;
}

- (void)setMainWindowController:(SRMainWindowController*)mainWindowController
{
    _mainWindowController = mainWindowController;
}

- (SRSideBarController*)sideBarController
{
    return _sideBarController;
}

- (void)setSidebarController:(SRSideBarController*)sideBarController
{
    _sideBarController = sideBarController;
}

//--------------------------------------------------------------//
#pragma mark -- Hisotry --
//--------------------------------------------------------------//

- (NSArray*)selectedHistoryItems
{
    // Get tab item identifier
    id  identifier;
    identifier = [[_historyTab selectedTabViewItem] identifier];
    
    // Get selected history
    // For history outlie
    if ([identifier isEqualToString:_SRHistoryTabIdentifier]) {
        // Get selected rows
        NSIndexSet* indexSet;
        indexSet = [_historyOutline selectedRowIndexes];
        if (!indexSet || [indexSet count] == 0) {
            return nil;
        }
        
        // Get selected rows
        NSMutableArray* historyItems;
        unsigned int    index;
        historyItems = [NSMutableArray array];
        index = [indexSet firstIndex];
        do {
            // Get selected history item
            id  selectedObject;
            selectedObject = [_historyOutline itemAtRow:index];
            if (!selectedObject) {
                continue;
            }
            
            if ([selectedObject isKindOfClass:[WebHistoryItem class]]) {
                if (![historyItems containsObject:selectedObject]) {
                    [historyItems addObject:selectedObject];
                }
            }
            if ([selectedObject isKindOfClass:[NSCalendarDate class]]) {
                // Add all history items in this day
                int numberOfChildren;
                numberOfChildren = [self outlineView:_historyOutline 
                        numberOfChildrenOfItem:selectedObject];
                
                int i;
                for (i = 0; i < numberOfChildren; i++) {
                    id  child;
                    child = [self outlineView:_historyOutline 
                            child:i 
                            ofItem:selectedObject];
                    if (!child) {
                        continue;
                    }
                    
                    if (![historyItems containsObject:child]) {
                        [historyItems addObject:child];
                    }
                }
            }
        } while ((index = [indexSet indexGreaterThanIndex:index]) != NSNotFound);
        
        return historyItems;
    }
    // For find table
    if ([identifier isEqualToString:_SRFindResultsTabIdentifier]) {
        // Get selected rows
        NSIndexSet* indexSet;
        indexSet = [_findTable selectedRowIndexes];
        if (!indexSet || [indexSet count] == 0) {
            return nil;
        }
        
        // Get selected rows
        NSMutableArray* historyItems;
        unsigned int    index;
        historyItems = [NSMutableArray array];
        index = [indexSet firstIndex];
        do {
            // Get selected history item
            id  selectedObject;
            selectedObject = [_findResults objectAtIndex:index];
            [historyItems addObject:selectedObject];
        } while ((index = [indexSet indexGreaterThanIndex:index]) != NSNotFound);
        
        return historyItems;
    }
    
    return nil;
}

- (void)reloadHistory
{
    // Reload bookmark outline
    [_historyOutline reloadData];
}

- (void)reloadHistoryFindTable
{
    // Get find string
    NSString*   findString;
    findString = [_searchField stringValue];
    
    // Find history
    NSMutableArray* results;
    results = [NSMutableArray array];
    
    // Get web history and last visited days
    WebHistory* history;
    NSArray*    lastVisitedDays;
    history = [WebHistory optionalSharedHistory];
    lastVisitedDays = [history orderedLastVisitedDays];
    
    // Enumerate calendar date
    NSEnumerator*   enumerator;
    NSCalendarDate* calendarDate;
    enumerator = [lastVisitedDays objectEnumerator];
    while (calendarDate = [enumerator nextObject]) {
        // Get items
        NSArray*    items;
        items = [history orderedItemsLastVisitedOnDay:calendarDate];
        if (!items || [items count] == 0) {
            continue;
        }
        
        // Enumerate history itmes
        NSEnumerator*   itemEnum;
        WebHistoryItem* item;
        itemEnum = [items objectEnumerator];
        while (item = [itemEnum nextObject]) {
            // Check history item
            NSString*   title;
            NSString*   URLString;
            title = [item title];
            URLString = [item URLString];
            if (title && 
                [title rangeOfString:findString options:NSCaseInsensitiveSearch].location != NSNotFound)
            {
                [results addObject:item];
                continue;
            }
            if (URLString && 
                [URLString rangeOfString:findString options:NSCaseInsensitiveSearch].location != NSNotFound)
            {
                [results addObject:item];
                continue;
            }
        }
    }
    
    // Reload table
    [_findResults release];
    _findResults = [results retain];
    [_findTable reloadData];
}

- (void)copySelectedHistoryItems
{
    // Get selected history items
    NSArray*    historyItems;
    historyItems = [self selectedHistoryItems];
    if (!historyItems || [historyItems count] == 0) {
        return;
    }
    
    // Write history items to pasteboard
    NSPasteboard*   pboard;
    pboard = [NSPasteboard generalPasteboard];
    SRWriteWebHistoryItemsToPasteboard(historyItems, pboard);
}

- (void)deleteSelectedHistoryItems
{
    // Get selected history items
    NSArray*    historyItems;
    historyItems = [self selectedHistoryItems];
    if (!historyItems || [historyItems count] == 0) {
        return;
    }
    
    // Get row of first item
    int row = -1;
    id  table;
    table = [self selectedHistoryView];
    row = [[table selectedRowIndexes] firstIndex];
    
    // Delete history items
    [[WebHistory optionalSharedHistory] removeItems:historyItems];
    
    // Select previous one
    if (row > 0) {
        [table selectRowIndexes:[NSIndexSet indexSetWithIndex:row - 1] 
                byExtendingSelection:NO];
    }
}

- (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 history item under the cursor
    NSPoint point;
    int     rowUnderPoint;
    point = [view convertPoint:[event locationInWindow] 
            fromView:nil];
    rowUnderPoint = [view rowAtPoint:point];
    if (![[view selectedRowIndexes] containsIndex:rowUnderPoint]) {
        [view selectRowIndexes:[NSIndexSet indexSetWithIndex:rowUnderPoint] 
                byExtendingSelection:NO];
    }
    
    // Get selected rows
    NSIndexSet* indexSet;
    indexSet = [view selectedRowIndexes];
    
    WebHistoryItem* historyItem = nil;
    NSArray*        historyItems = nil;
    
    // No bookmark is selected
    if ([indexSet count] == 0) {
        // No menu
    }
    else {
        // Just one history item is selected
        if ([indexSet count] == 1) {
            // Get history item
            historyItem = [[self selectedHistoryItems] objectAtIndex:0];
        }
        else {
            // Get history items
            historyItems = [self selectedHistoryItems];
        }
        
        // Check tab availability
        BOOL    enableTabbedBrowsing, selectNewTabs;
        enableTabbedBrowsing = [defaults boolForKey:SRTabEnableTabbedBrowsing];
        selectNewTabs = [defaults boolForKey:SRTabSelectNewTabs];
        
        // Case of one history item
        if (historyItem && [historyItem isKindOfClass:[WebHistoryItem class]]) {
            // Create tag array
            [tags addObject:[NSNumber numberWithInt:SROpenHistoryTag]];
            
            if (enableTabbedBrowsing) {
                if ((selectNewTabs && !shiftFlag) || 
                    (!selectNewTabs && shiftFlag))
                {
                    [tags addObject:[NSNumber numberWithInt:SROpenHistoryInNewWindowTag]];
                    [tags addObject:[NSNumber numberWithInt:SROpenHistoryInNewTabTag]];
                }
                else {
                    [tags addObject:[NSNumber numberWithInt:SROpenHisotryInNewBackgroundWindowTag]];
                    [tags addObject:[NSNumber numberWithInt:SROpenHistoryInNewBackgroundTabTag]];
                }
            }
            else {
                [tags addObject:[NSNumber numberWithInt:SROpenHistoryInNewWindowTag]];
            }
            
            [tags addObject:[NSNumber numberWithInt:SRDeleteHistoryTag]];
        }
        // Case of one folder
        if (historyItem && [historyItem isKindOfClass:[NSCalendarDate class]]) {
            // Create tag array
            [tags addObject:[NSNumber numberWithInt:SRDeleteHistoryTag]];
        }
        // Case of multiple history items
        if (historyItems) {
            // Check existence of history item
            BOOL            isContainedHistoryItem = NO;
            NSEnumerator*   enumerator;
            WebHistoryItem* tmp;
            enumerator = [historyItems objectEnumerator];
            while (tmp = [enumerator nextObject]) {
                if ([tmp isKindOfClass:[WebHistoryItem class]]) {
                    isContainedHistoryItem = YES;
                    break;
                }
            }
            
            // Create tag array
            if (isContainedHistoryItem) {
                if (enableTabbedBrowsing) {
                    [tags addObject:[NSNumber numberWithInt:SROpenHistoryInTabsTag]];
                }
            }
            [tags addObject:[NSNumber numberWithInt:SRDeleteHistoryTag]];
        }
    }
    
    if ([tags count] > 0) {
        // Copy menu
        NSMenu* menu;
        menu = [SRContextMenu copyMenuFrom:[SRContextMenu historyContextMenu] 
                ofTags:tags 
//                target:_mainWindowController];
                target:self];
        
        // Set represented object
        if (historyItem) {
            [[menu itemArray] makeObjectsPerformSelector:@selector(setRepresentedObject:) 
                    withObject:historyItem];
        }
        if (historyItems) {
            [[menu itemArray] makeObjectsPerformSelector:@selector(setRepresentedObject:) 
                    withObject:historyItems];
        }
        
        return menu;
    }
    
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- Actions --
//--------------------------------------------------------------//

- (void)copy:(id)sender
{
    // Copy selected history items
    [self copySelectedHistoryItems];
}

- (void)delete:(id)sender
{
    // Delete selected history items
    [self deleteSelectedHistoryItems];
}

- (void)historySelectedAction:(id)sender
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // For single click opening
    if ([defaults integerForKey:SRSidebarClick] == SRSidebarSingleClick) {
        // Open history
        [self openHistoryItemAction:sender];
    }
}

- (void)openHistoryItemAction:(id)sender
{
    // Get selected history
    id  history;
    history = [[self selectedHistoryItems] objectAtIndex:0];
    
    // For NSCalnderDate
    if ([history isKindOfClass:[NSCalendarDate class]]) {
        // Get row
        int row;
        row = [_historyOutline rowForItem:history];
        if (row == -1) {
            return;
        }
        
        // Toogle expansion
        if ([_historyOutline isItemExpanded:history]) {
            [_historyOutline collapseItem:history];
        }
        else {
            [_historyOutline expandItem:history];
        }
    }
    // For WebHistory
    if ([history isKindOfClass:[WebHistoryItem class]]) {
        // Get open action
        SROpenActionType    openAction;
        openAction = SROpenActionTypeFromModifierFlags([[NSApp currentEvent] modifierFlags]);
        
        // Open history wiht open action
        [_mainWindowController openHistory:history withOpenAction:openAction];
    }
}

- (void)findHistoryAction:(id)sender
{
    // Get find string
    NSString*   findString;
    findString = [_searchField stringValue];
    
    // Switch tab item
    if ([findString length] == 0) {
        [_historyTab selectTabViewItemWithIdentifier:_SRHistoryTabIdentifier];
        return;
    }
    else {
        [_historyTab selectTabViewItemWithIdentifier:_SRFindResultsTabIdentifier];
    }
    
    // Reload table
    [self reloadHistoryFindTable];
}

//
// From context menu
//

- (void)openHistoryAction:(id)sender
{
    // Pass to main window controller
    [_mainWindowController openHistoryAction:sender];
}

- (void)openHistoryInNewWindowAction:(id)sender
{
    // Pass to main window controller
    [_mainWindowController openHistoryInNewWindowAction:sender];
}

- (void)openHistoryInNewTabAction:(id)sender
{
    // Pass to main window controller
    [_mainWindowController openHistoryInNewTabAction:sender];
}

- (void)openHistoryInTabsAction:(id)sender
{
    // Pass to main window controller
    [_mainWindowController openHistoryInTabsAction:sender];
}

- (void)openHistoryInNewBackgroundWindowAction:(id)sender
{
    // Pass to main window controller
    [_mainWindowController openHistoryInNewBackgroundWindowAction:sender];
}

- (void)openHistoryInNewBackgroundTabAction:(id)sender
{
    // Pass to main window controller
    [_mainWindowController openHistoryInNewBackgroundTabAction:sender];
}

- (void)deleteHistoryAction:(id)sender
{
    [self deleteSelectedHistoryItems];
}

- (void)addHistoryToBookmarkAction:(id)sender
{
}

- (void)bookmarkAllHistoryAction:(id)sender
{
}

//--------------------------------------------------------------//
#pragma mark -- NSTableDataSource --
//--------------------------------------------------------------//

- (int)numberOfRowsInTableView:(NSTableView*)tableView
{
    if (tableView == _findTable) {
        return [_findResults count];
    }
    
    return 0;
}

- (id)tableView:(NSTableView*)tableView 
        objectValueForTableColumn:(NSTableColumn*)tableColumn 
        row:(int)rowIndex
{
    // Get identifier
    id  identifier;
    identifier = [tableColumn identifier];
    
    if (tableView == _findTable) {
        if ([identifier isEqualToString:@"results"]) {
            return [[_findResults objectAtIndex:rowIndex] title];
        }
    }
    
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- NSTableView delegate --
//--------------------------------------------------------------//

- (void)tableView:(NSTableView*)tableView 
        willDisplayCell:(id)cell 
        forTableColumn:(NSTableColumn*)tableColumn 
        row:(int)rowIndex
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check favicon availability
    BOOL    isFaviconUsed;
    isFaviconUsed = [defaults boolForKey:SRIconUseFavicon] && [defaults boolForKey:SRIconUseFaviconSideBar];
    
    // Get column identifier
    id  identifier;
    identifier = [tableColumn identifier];
    
    if (tableView == _findTable) {
        // For bookmark column
        if ([identifier isEqualToString:@"results"]) {
            if (isFaviconUsed) {
                // Set image
                [cell setImage:[[_findResults objectAtIndex:rowIndex] icon]];
            }
            else {
                [cell setImage:nil];
            }
            return;
        }
    }
}

- (NSMenu*)tableView:(NSTableView*)tableView 
        menuForEvent:(NSEvent*)event
{
    // Find table
    if (tableView == _findTable) {
        return [self _contextMenuForView:tableView event:event];
    }
    
    return nil;
}

// SRTableView delegate
- (void)tableViewDeleteSelectedItem:(NSTableView*)tableView
{
    // Get selected history items
    NSArray*    historyItems;
    historyItems = [self selectedHistoryItems];
    if (!historyItems || [historyItems count] == 0) {
        return;
    }
    
    // Get fisrt history item
    WebHistoryItem* historyItem;
    historyItem = [historyItems objectAtIndex:0];
    if (!historyItem) {
        return;
    }
    
    // Get selected index
    int row;
    row = [[_findTable selectedRowIndexes] firstIndex];
    
    // Delete history items
    [[WebHistory optionalSharedHistory] removeItems:historyItems];
    
    // Select previous one
    if (row > 0) {
        [_findTable selectRowIndexes:[NSIndexSet indexSetWithIndex:row - 1] byExtendingSelection:NO];
    }
}

- (BOOL)tableView:(NSTableView*)tableView 
        writeRows:(NSArray*)rows 
        toPasteboard:(NSPasteboard*)pboard
{
    // Find table
    if (tableView == _findTable) {
        // Get specified history items
        NSMutableArray* historyItems;
        NSEnumerator*   enumerator;
        NSNumber*       rowNumber;
        historyItems = [NSMutableArray array];
        enumerator = [rows objectEnumerator];
        while (rowNumber = [enumerator nextObject]) {
            if ([rowNumber intValue] < [_findResults count]) {
                [historyItems addObject:[_findResults objectAtIndex:[rowNumber intValue]]];
            }
        }
        
        // Write history items to pasteboard
        SRWriteWebHistoryItemsToPasteboard(historyItems, pboard);
        
        return YES;
    }
    
    return NO;
}

//--------------------------------------------------------------//
#pragma mark -- NSOutlineViewDataSource --
//--------------------------------------------------------------//

- (id)outlineView:(NSOutlineView*)outlineView 
        child:(int)index 
        ofItem:(id)item
{
    // History outline
    if (outlineView == _historyOutline) {
        // Get web history
        WebHistory* history;
        history = [WebHistory optionalSharedHistory];
        
        // For root
        if (!item) {
            return [[history orderedLastVisitedDays] objectAtIndex:index];
        }
        // For NSCalenderDate
        if ([item isKindOfClass:[NSCalendarDate class]]) {
            NSArray*    items;
            items = [history orderedItemsLastVisitedOnDay:item];
            return [items objectAtIndex:index];
        }
        return nil;
    }
    
    return nil;
}

- (BOOL)outlineView:(NSOutlineView*)outlineView 
        isItemExpandable:(id)item
{
    // History outline
    if (outlineView == _historyOutline) {
        // For root
        if (!item) {
            return YES;
        }
        // For NSCalenderDate
        if ([item isKindOfClass:[NSCalendarDate class]]) {
            return YES;
        }
        return NO;
    }
    
    return NO;
}

- (int)outlineView:(NSOutlineView*)outlineView 
        numberOfChildrenOfItem:(id)item
{
    // History outline
    if (outlineView == _historyOutline) {
        // Get web history
        WebHistory* history;
        history = [WebHistory optionalSharedHistory];
        
        // For root
        if (!item) {
            return [[history orderedLastVisitedDays] count];
        }
        // For NSCalenderDate
        if ([item isKindOfClass:[NSCalendarDate class]]) {
            NSArray*    items;
            items = [history orderedItemsLastVisitedOnDay:item];
            return [items count];
        }
        return 0;
    }
    
    return 0;
}

- (id)outlineView:(NSOutlineView*)outlineView 
        objectValueForTableColumn:(NSTableColumn*)column 
        byItem:(id)item
{
    // History outline
    if (outlineView == _historyOutline) {
        // Get column identifier
        id  identifier;
        identifier = [column identifier];
        
        // For history column
        if ([identifier isEqualToString:@"history"]) {
            if ([item isKindOfClass:[NSCalendarDate class]]) {
                NSDictionary*   locale;
                locale = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
                return [item descriptionWithCalendarFormat:NSLocalizedString(@"%a, %B %e", nil) 
                        locale:locale];
            }
            if ([item isKindOfClass:[WebHistoryItem class]]) {
                //return [item title];
                return SRAlternateTitleOfWebHistoryItem(item);
            }
        }
    }
    
    return nil;
}

// - outlineView:setObjectValue:forTableColumn:byItem:
// - outlineView:acceptDrop:item:childIndex:
// - outlineView:validateDrop:proposedItem:proposedChildIndex:

- (id)outlineView:(NSOutlineView*)outlineView 
        itemForPersistentObject:(id)object
{
    // History outline
    if (outlineView == _historyOutline) {
        // Return unarchived history
        return [NSUnarchiver unarchiveObjectWithData:object];
    }
    
    return nil;
}

- (id)outlineView:(NSOutlineView*)outlineView 
        persistentObjectForItem:(id)item
{
    // History outline
    if (outlineView == _historyOutline) {
        // Return archived history
        return [NSArchiver archivedDataWithRootObject:item];
    }
    
    return nil;
}

- (BOOL)outlineView:(NSOutlineView*)outlineView 
        writeItems:(NSArray*)items 
        toPasteboard:(NSPasteboard*)pboard
{
    // History outline
    if (outlineView == _historyOutline) {
        // Write history items to pasteboard
        SRWriteWebHistoryItemsToPasteboard(items, pboard);
        
        return YES;
    }
    
    return NO;
}

// - outlineView:sortDescriptorsDidChange:

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

- (void)outlineView:(NSOutlineView*)outlineView 
        willDisplayCell:(id)cell 
        forTableColumn:(NSTableColumn*)column 
        item:(id)item
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check favicon availability
    BOOL    isFaviconUsed;
    isFaviconUsed = [defaults boolForKey:SRIconUseFavicon] && [defaults boolForKey:SRIconUseFaviconSideBar];
    
    // History outline
    if (outlineView == _historyOutline) {
        // Get column identifier
        id  identifier;
        identifier = [column identifier];
        
        // For history column
        if ([identifier isEqualToString:@"history"]) {
            // For NSCaledarDate
            if ([item isKindOfClass:[NSCalendarDate class]]) {
                if (isFaviconUsed) {
                    // Set image
                    [cell setImage:[NSImage imageNamed:@"shiiraFolder"]];
                }
                else {
                    [cell setImage:nil];
                }
                return;
            }
            if ([item isKindOfClass:[WebHistoryItem class]]) {
                if (isFaviconUsed) {
                    // Set image
                    [cell setImage:[[WebIconDatabase sharedIconDatabase] 
                            iconForURL:[item URLString] withSize:NSMakeSize(16, 16)]];
                }
                else {
                    [cell setImage:nil];
                }
                return;
            }
        }
    }
}

// Extention method
- (NSMenu*)outlineView:(NSOutlineView*)outlineView 
        menuForEvent:(NSEvent*)event
{
    // History outline
    if (outlineView == _historyOutline) {
        return [self _contextMenuForView:outlineView event:event];
    }
    
    return nil;
}

// SROutlineView delegate
- (void)outlineViewDeleteSelectedItem:(NSOutlineView*)outlineView
{
    // Delete selected history items
    [self deleteSelectedHistoryItems];
}

//--------------------------------------------------------------//
#pragma mark -- NSTabView delegate --
//--------------------------------------------------------------//

- (void)tabView:(NSTabView*)tabView 
        didSelectTabViewItem:(NSTabViewItem*)tabViewItem
{
    // Update next responder
    [self _updateNextResponder];
}

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

- (BOOL)validateMenuItem:(id<NSMenuItem>)menuItem
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get tag
    int tag;
    tag = [menuItem tag];
    
    switch (tag) {
    // Edit menu
    case SRCopyTag: {
        return [self selectedHistoryItems] != nil;
    }
    case SRDeleteTag: {
        return [self selectedHistoryItems] != nil;
    }
    }
    
    return YES;
}

//--------------------------------------------------------------//
#pragma mark -- NSView notification --
//--------------------------------------------------------------//

- (void)historyViewFrameDidChange:(NSNotification*)notification
{
    // Get history tab frame and check padding
    NSRect  historyTabFrame, historyFrame;
    int     historyTabPaddingTop, historyTabPaddingBottom;
    historyTabFrame = [_historyTab frame];
    historyFrame = [_historyView frame];
    historyFrame.origin = NSZeroPoint;
    _historyTabPaddingTop = (historyFrame.origin.y + historyFrame.size.height) - 
            (historyTabFrame.origin.y + historyTabFrame.size.height);
    _historyTabPaddingBottom = historyTabFrame.origin.y - historyFrame.origin.y;
    
    if (historyTabPaddingTop != _historyTabPaddingTop || 
        historyTabPaddingBottom != _historyTabPaddingBottom)
    {
        // Reset padding
        NSRect  newFrame;
        newFrame.origin.x = historyTabFrame.origin.x;
        newFrame.origin.y = historyFrame.origin.y + _historyTabPaddingBottom;
        newFrame.size.width = historyTabFrame.size.width;
        newFrame.size.height = historyFrame.size.height - _historyTabPaddingBottom - _historyTabPaddingTop;
        
        if (newFrame.origin.y > 0 && newFrame.size.height > 0) {
            [_historyTab setFrame:newFrame];
        }
    }
}

//--------------------------------------------------------------//
#pragma mark -- WebHistory notification --
//--------------------------------------------------------------//

- (void)webHistoryItemsUpdated:(NSNotification*)notification
{
    // Reload history
    [self reloadHistory];
    [self reloadHistoryFindTable];
}

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

- (void)observeValueForKeyPath:(NSString*)keyPath 
        ofObject:(id)object 
        change:(NSDictionary*)change 
        context:(void *)context
{
    if ([keyPath isEqualToString:SRIconUseFavicon] || 
        [keyPath isEqualToString:SRIconUseFaviconSideBar])
    {
        // Reload history
        [self reloadHistory];
        [self reloadHistoryFindTable];
    }
}

@end
