/*
SRShelfController.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 "SRBookmark.h"
#import "SRWebHistory.h"

#import "SRAppController.h"
#import "SRDefaultKeys.h"

#import "SRPrefDefaultKeys.h"

#import "SRConstants.h"

#import "SRShelf.h"
#import "SRShelfController.h"
#import "SRShelfBar.h"

static NSDictionary*    _defaultShelfIndexes = nil;

@interface SRShelfController (private)
// Shelf management
- (void)_loadShelfs;
- (void)_updateShelfArray;
- (void)_updateShelf;
@end

@implementation SRShelfController

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

- (id)init
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Initialize instance variables
    _isInitialized = NO;
    _shelfInfos = [[NSMutableArray array] retain];
    
    if (!_defaultShelfIndexes) {
        _defaultShelfIndexes = [[NSDictionary dictionaryWithObjectsAndKeys:
                [NSNumber numberWithInt:0], @"jp.hmdt.shiira.bookmarkshelf", 
                [NSNumber numberWithInt:1], @"jp.hmdt.shiira.safaribookmarkshelf", 
                [NSNumber numberWithInt:2], @"jp.hmdt.shiira.firefoxbookmarkshelf", 
                
                [NSNumber numberWithInt:11], @"jp.hmdt.shiira.addressbookshelf", 
                [NSNumber numberWithInt:12], @"jp.hmdt.shiira.bonjourshelf", 
                
                [NSNumber numberWithInt:21], @"jp.hmdt.shiira.rssshelf", 
                
                [NSNumber numberWithInt:31], @"jp.hmdt.shiira.historyshelf", 
                [NSNumber numberWithInt:32], @"jp.hmdt.shiira.downloadshelf", 
                
                [NSNumber numberWithInt:41], @"jp.hmdt.shiira.searchengineshelf", 
                nil] retain];
    }
    
    // Load nib file
    if (![NSBundle loadNibNamed:@"Shelf" owner:self]) {
        // Error
        NSLog(@"Can't load Shelf.nib");
    }
    
    // Register key value observation
    [defaults addObserver:self forKeyPath:SRBookmarkMenuUsageFlags 
            options:NSKeyValueObservingOptionNew context:NULL];
    
    return self;
}

- (void)didLoad
{
    // Configure table view
    [_tableView setWantsLiveResizeToUseCachedImage:NO];
    
    // Set image text cell
    NSTableColumn*          column;
    NSCell*                 oldCell;
    HMImageTextFieldCell*   cell;
    column = [_tableView tableColumnWithIdentifier:@"shelf"];
    oldCell = [column dataCell];
    cell = [[HMImageTextFieldCell alloc] init];
    [cell setFont:[oldCell font]];
    [cell setUseBoldForHighlight:YES];
    [column setDataCell:cell];
    [cell release];
    
    // Set header cell
    NSCell*             oldHeaderCell;
    HMTableHeaderCell*  headerCell;
    oldHeaderCell = [column headerCell];
    headerCell = [[HMTableHeaderCell alloc] init];
    [headerCell setObjectValue:[oldHeaderCell objectValue]];
    [headerCell setAlignment:[oldHeaderCell alignment]];
    [headerCell setFont:[oldHeaderCell font]];
    [column setHeaderCell:headerCell];
    [headerCell release];
    
    // Set header view
    NSTableHeaderView*  oldHeaderView;
    HMTableHeaderView*  headerView;
    oldHeaderView = [_tableView headerView];
    headerView = [[HMTableHeaderView alloc] initWithFrame:[oldHeaderView frame]];
    [headerView setSplitView:_splitView];
    [_tableView setHeaderView:headerView];
    
    // Configure shlef bar
    [_shelfBar setTitle:@"Shelf"];
    [_shelfBar setTitleWidth:[_tableView frame].size.width];
    
    // Configure split view
    [_splitView setSplitType:HMSplitLine];
    
    float   shelfTableWidth;
    shelfTableWidth = [[NSUserDefaults standardUserDefaults] floatForKey:SRShelfTableWidth];
    if ([[_splitView subviews] count] > 0 && shelfTableWidth > 0) {
        // Set subview width
        NSView* subview;
        NSRect  frame;
        float   delta;
        
        subview = [[_splitView subviews] objectAtIndex:0];
        frame = [subview frame];
        delta = frame.size.width - shelfTableWidth;
        frame.size.width = shelfTableWidth;
        [subview setFrame:frame];
        
        subview = [[_splitView subviews] objectAtIndex:1];
        frame = [subview frame];
        frame.size.width += delta;
        [subview setFrame:frame];
        
        [_splitView adjustSubviews];
        
        // Set shelf bar title width
        [_shelfBar setTitleWidth:[_tableView frame].size.width];
    }
    
    // Configure array controller
    NSSortDescriptor*   sortDescriptor;
    sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"index" ascending:YES];
    [_shelfArrayController setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
    [sortDescriptor release];
    
    _isInitialized = YES;

    // Load shelfs
    [self _loadShelfs];
    [self _updateShelfArray];
    [_shelfArrayController setSelectionIndex:0];
    
    // Update shelf
    [self _updateShelf];
}

- (void)viewDidMoveToHostWindow
{
}

- (void)viewWillMoveToHostWindow:(NSWindow*)hostWindow
{
}

- (void)dealloc
{
    // Remove observer
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    [defaults removeObserver:self forKeyPath:SRBookmarkMenuUsageFlags];
    
    [_shelfInfos release], _shelfInfos = nil;
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Shelf management --
//--------------------------------------------------------------//

- (id)selectedShelf
{
    return nil;
    //return _selectedShelf;
}

- (void)_loadShelfs
{
    NSMutableArray* shelfBundleIds;
    NSMutableArray* shelfIcons;
    NSMutableArray* shelfTitles;
    shelfBundleIds = [NSMutableArray array];
    shelfIcons = [NSMutableArray array];
    shelfTitles = [NSMutableArray array];
    
    // Load build-in shelfs
    NSString*   shelfPath;
    shelfPath = [[[NSBundle mainBundle] builtInPlugInsPath] 
            stringByAppendingPathComponent:@"Shelfs"];
    
    // Load shelf bundles
    int             index = 100;
    NSArray*        dirContents;
    NSEnumerator*   enumerator;
    NSString*       dirContent;
    dirContents = [[NSFileManager defaultManager] directoryContentsAtPath:shelfPath];
    enumerator = [dirContents objectEnumerator];
    while (dirContent = [enumerator nextObject]) {
        // Check extention
        if (![dirContent hasSuffix:@".shelf"]) {
            continue;
        }
        
        // Create bundle path
        NSString*   bundlePath;
        bundlePath = [shelfPath stringByAppendingPathComponent:dirContent];
        
        // Create bundle
        NSBundle*       bundle;
        NSDictionary*   infoPlist;
        bundle = [NSBundle bundleWithPath:bundlePath];
        infoPlist = [bundle infoDictionary];
        if (!bundle || !infoPlist) {
            continue;
        }
        
        // Get bundle ID
        NSString*   bundleId;
        bundleId = [bundle bundleIdentifier];
        if (!bundleId) {
            continue;
        }
        if ([shelfBundleIds containsObject:bundleId]) {
            // Error
            NSLog(@"Duplicated bundle ID, %@", bundleId);
            continue;
        }
        
        // Get title
        NSString*   title;
        title = [infoPlist objectForKey:@"SRShelfTitle"];
        if (!title) {
            continue;
        }
        
        // Get icon
        NSString*   iconPath;
        NSImage*    icon = nil;
        iconPath = [infoPlist objectForKey:@"SRShelfIconFile"];
        if (iconPath) {
            iconPath = [[bundle resourcePath] stringByAppendingPathComponent:iconPath];
            icon = [[NSImage alloc] initWithContentsOfFile:iconPath];
            if (icon) {
                [icon setSize:NSMakeSize(16.0f, 16.0f)];
                [icon autorelease];
            }
        }
        
        // Get index
        NSNumber*   indexNumber;
        indexNumber = [_defaultShelfIndexes objectForKey:bundleId];
        if (!indexNumber) {
            indexNumber = [NSNumber numberWithInt:index++];
        }
        
        // Add shelf info
        NSMutableDictionary*    shelfInfo;
        shelfInfo = [NSMutableDictionary dictionary];
        [shelfInfo setObject:bundle forKey:@"bundle"];
        [shelfInfo setObject:bundleId forKey:@"bundleId"];
        [shelfInfo setObject:title forKey:@"title"];
        if (icon) {
            [shelfInfo setObject:icon forKey:@"icon"];
        }
        [shelfInfo setObject:indexNumber forKey:@"index"];
        
        [_shelfInfos addObject:shelfInfo];
    }
    
    // Add separator
    NSMutableDictionary*    separator;
    separator = [NSMutableDictionary dictionary];
    [separator setObject:[NSNumber numberWithBool:YES] forKey:@"isSeparator"];
    [separator setObject:[NSNumber numberWithInt:10] forKey:@"index"];
    [_shelfInfos addObject:separator];
    
    separator = [NSMutableDictionary dictionary];
    [separator setObject:[NSNumber numberWithBool:YES] forKey:@"isSeparator"];
    [separator setObject:[NSNumber numberWithInt:20] forKey:@"index"];
    [_shelfInfos addObject:separator];
    
    separator = [NSMutableDictionary dictionary];
    [separator setObject:[NSNumber numberWithBool:YES] forKey:@"isSeparator"];
    [separator setObject:[NSNumber numberWithInt:30] forKey:@"index"];
    [_shelfInfos addObject:separator];
    
    separator = [NSMutableDictionary dictionary];
    [separator setObject:[NSNumber numberWithBool:YES] forKey:@"isSeparator"];
    [separator setObject:[NSNumber numberWithInt:40] forKey:@"index"];
    [_shelfInfos addObject:separator];
}

- (void)_updateShelfArray
{
    // Get usage flags
    NSDictionary*   usageFlags;
    usageFlags = [[NSUserDefaults standardUserDefaults] objectForKey:SRBookmarkMenuUsageFlags];
    
    // Remove old shelf info
    [_shelfArrayController removeObjects:[_shelfArrayController arrangedObjects]];
    
    // Set shelf info
    NSEnumerator*   enumerator;
    NSDictionary*   shelfInfo;
    NSNumber*       flag;
    enumerator = [_shelfInfos objectEnumerator];
    while (shelfInfo = [enumerator nextObject]) {
        // For Safari bookmark shelf
        if ([[shelfInfo objectForKey:@"bundleId"] isEqualToString:@"jp.hmdt.shiira.safaribookmarkshelf"]) {
            flag = [usageFlags objectForKey:SRBrowserSafari];
            if (flag && ![flag boolValue]) {
                continue;
            }
        }
        
        // For Firefox bookmark shelf
        if ([[shelfInfo objectForKey:@"bundleId"] isEqualToString:@"jp.hmdt.shiira.firefoxbookmarkshelf"]) {
            flag = [usageFlags objectForKey:SRBrowserFirefox];
            if (flag && ![flag boolValue]) {
                continue;
            }
        }
        
        // Add shelf info
        [_shelfArrayController addObject:shelfInfo];
    }
    
    // Rearrange objects
    [_shelfArrayController rearrangeObjects];
}

- (void)_updateShelf
{
    // Get selected shelf info
    NSArray*                shelfInfos;
    NSMutableDictionary*    shelfInfo;
    shelfInfos = [_shelfArrayController selectedObjects];
    if (!shelfInfos || [shelfInfos count] == 0) {
        return;
    }
    shelfInfo = [shelfInfos objectAtIndex:0];
    
    // Get bundle ID
    NSString*   bundleId;
    bundleId = [shelfInfo objectForKey:@"bundleId"];
    
    // Get tab view item
    NSTabViewItem*  tabViewItem = nil;
    int             index;
    index = [_tabView indexOfTabViewItemWithIdentifier:bundleId];
    if (index != NSNotFound) {
        tabViewItem = [_tabView tabViewItemAtIndex:index];
    }
    if (!tabViewItem) {
        // Create tab view item
        tabViewItem = [[NSTabViewItem alloc] initWithIdentifier:bundleId];
        [_tabView addTabViewItem:tabViewItem];
    }
    
    // Select tab view item
    [_tabView selectTabViewItem:tabViewItem];
    
    // Get shelf instance
    SRShelf*    shelf;
    shelf = [shelfInfo objectForKey:@"shelf"];
    if (!shelf) {
        // Get bundle
        NSBundle*   bundle;
        bundle = [shelfInfo objectForKey:@"bundle"];
        if (!bundle) {
            return;
        }
        
        // Load bundle
        if (![bundle isLoaded]) {
            if (![bundle load]) {
                // Error
                NSLog(@"Failed to load the bundle, %@", bundleId);
                return;
            }
        }
        
        // Create shelf instance
        Class   principalClass;
        principalClass = [bundle principalClass];
        if (!principalClass) {
            // Error
            NSLog(@"principalClass is nil");
            return;
        }
        shelf = [[principalClass alloc] initWithBundle:bundle];
        if (!shelf) {
            // Error
            NSLog(@"Failed to create shelf instance, %@", bundleId);
            return;
        }
        
        // Set controllers
        [shelf setAppController:[SRAppController sharedInstance]];
        
        id  browserController;
        browserController = [[_view window] windowController];
        if ([browserController conformsToProtocol:@protocol(SRBrowserController)]) {
            [shelf setBrowserController:browserController];
        }
        //[shelf setPageController:[]];
        
        id  context, store;
        context = [[SRAppController sharedInstance] managedObjectContext];
        store = [[SRAppController sharedInstance] persistentStore];
        id  bookmarkFactory;
        bookmarkFactory = [SRBookmarkFactory 
                sharedInstanceWithManagedObjectContext:context persistentStore:store];
        [shelf setBookmarkFactory:bookmarkFactory];
        
        [shelf setWebHistory:[SRWebHistory sharedInstance]];
        
        [shelf loadMainNib];
        [shelf didLoad];
        
        // Set shelf
        [shelfInfo setObject:shelf forKey:@"shelf"];
        [shelf release];
    }
    
    // Check subviews
    if ([[[tabViewItem view] subviews] count] == 0) {
        // Get main view
        NSView* mainView;
        mainView = [shelf mainView];
        if (!mainView) {
            return;
        }
        
        // Set view
        [tabViewItem setView:mainView];
    }
    
    // Set bar buttons
    [_shelfBar setLeftButtons:[shelf leftBarButtons]];
    [_shelfBar setRightButtons:[shelf rightBarButtons]];
    
    // Select tab view item
    [_tabView selectTabViewItem:tabViewItem];
}

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

- (NSView*)view
{
    return _view;
}

//--------------------------------------------------------------//
#pragma mark -- Shelf selection --
//--------------------------------------------------------------//

- (NSString*)selectedShelfId
{
    return nil;
}

- (void)setSelectedShelfId:(NSString*)shelfId
{
    // Find shelf with ID
    NSArray*    objects;
    objects = [_shelfArrayController arrangedObjects];
    
    int i;
    for (i = 0; i < [objects count]; i++) {
        NSDictionary*   shelfInfo;
        shelfInfo = [objects objectAtIndex:i];
        
        if ([[shelfInfo objectForKey:@"bundleId"] isEqualToString:shelfId]) {
            break;
        }
    }
    if (i == [objects count]) {
        return;
    }
    
    // Select index
    [_shelfArrayController setSelectionIndex:i];
    
    // Update shelf
    [self _updateShelf];
}

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

- (BOOL)tableView:(NSTableView*)tableView shouldSelectRow:(int)rowIndex
{
    // Get shelf info
    NSArray*        shelfInfos;
    NSDictionary*   shelfInfo;
    shelfInfos = [_shelfArrayController arrangedObjects];
    if ([shelfInfos count] < rowIndex) {
        return NO;
    }
    shelfInfo = [shelfInfos objectAtIndex:rowIndex];
    
    // For separator
    NSNumber*   isSeparator;
    isSeparator = [shelfInfo objectForKey:@"isSeparator"];
    if (isSeparator && [isSeparator boolValue]) {
        return NO;
    }
    
    return YES;
}

- (void)tableViewSelectionDidChange:(NSNotification*)notification
{
    // Update shelf
    [self _updateShelf];
}

- (void)tableView:(NSTableView*)tableView 
        willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(int)rowIndex
{
    // Get identifier
    id  identifier;
    identifier = [tableColumn identifier];
    
    // For shelf
    if ([identifier isEqualToString:@"shelf"]) {
        // Get shelf info
        NSArray*        shelfInfos;
        NSDictionary*   shelfInfo;
        shelfInfos = [_shelfArrayController arrangedObjects];
        if ([shelfInfos count] < rowIndex) {
            return;
        }
        shelfInfo = [shelfInfos objectAtIndex:rowIndex];
        
        // Get icon
        NSImage*    icon;
        icon = [shelfInfo objectForKey:@"icon"];
        if (icon) {
            [cell setImage:icon];
        }
        
        // For separator
        NSNumber*   isSeparator;
        isSeparator = [shelfInfo objectForKey:@"isSeparator"];
        if (isSeparator && [isSeparator boolValue]) {
            [cell setSeparator:YES];
        }
        else {
            [cell setSeparator:NO];
        }
    }
}

- (float)tableView:(NSTableView*)tableView heightOfRow:(int)rowIndex
{
    // Get row height
    float   height;
    height = [tableView rowHeight];
    
    // Get shelf info
    NSArray*        shelfInfos;
    NSDictionary*   shelfInfo;
    shelfInfos = [_shelfArrayController arrangedObjects];
    if ([shelfInfos count] < rowIndex) {
        return height;
    }
    shelfInfo = [shelfInfos objectAtIndex:rowIndex];
    
    // For separator
    NSNumber*   isSeparator;
    isSeparator = [shelfInfo objectForKey:@"isSeparator"];
    if (isSeparator && [isSeparator boolValue]) {
        return 8.0f;
    }
    
    return height;
}

//--------------------------------------------------------------//
#pragma mark -- NSSplitView delegate --
//--------------------------------------------------------------//

- (float)splitView:(NSSplitView*)splitView 
        constrainMinCoordinate:(float)proposedMin ofSubviewAt:(int)offset
{
    return 100.0f;
}

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

- (void)splitViewDidResizeSubviews:(NSNotification*)notification
{
    // Set shlef bar title width
    [_shelfBar setTitleWidth:[_tableView frame].size.width];
    
    // Set shelf table width
    if (_isInitialized) {
        [[NSUserDefaults standardUserDefaults] 
                setFloat:[_tableView frame].size.width forKey:SRShelfTableWidth];
    }
}

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

- (void)observeValueForKeyPath:(NSString*)keyPath 
        ofObject:(id)object 
        change:(NSDictionary*)change 
        context:(void *)context
{
    // For user defaults
    if (object == [NSUserDefaults standardUserDefaults]) {
        // Bookmark pref
        if ([keyPath isEqualToString:SRBookmarkMenuUsageFlags]) {
            [self _updateShelfArray];
        }
    }
}

@end
