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

#import "SRBookmarkButton.h"
#import "SRBookmarkButtonCell.h"

#import "SRUtil.h"

@implementation SRBookmarkButton

+ (Class)cellClass
{
    return [SRBookmarkButtonCell class];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    [super dealloc];
}

- (SRBookmark*)bookmark
{
    return _bookmark;
}

- (void)setBookmark:(SRBookmark*)bookmark
{
    if (bookmark != _bookmark) {
        [_bookmark release];
        _bookmark = [bookmark retain];
        
        // Set title and image
        [self setTitle:[_bookmark title]];
        [self setImage:[_bookmark icon]];
        
        // Update appearance
        [self update];
    }
}

- (BOOL)isClipMenu
{
    return _isClipMenu;
}

- (void)setClipMenu:(BOOL)isClipMenu
{
    if (_isClipMenu != isClipMenu) {
        _isClipMenu = isClipMenu;
    }
}

- (void)setClipedBookmarks:(NSArray*)clipedBookmarks
{
    if (_clipedBookmarks != clipedBookmarks) {
        [_clipedBookmarks release];
        _clipedBookmarks = [clipedBookmarks retain];
    }
}

- (void)update
{
    // Update bookmark
    [_bookmark update];
    
    // Set title and image
    [self setTitle:[_bookmark title]];
    [self setImage:[_bookmark icon]];
    
    [self setNeedsDisplay:YES];
}

- (void)_addOpenInTabsForMenu:(NSMenu*)menu ofBookmark:(SRBookmark*)bookmark
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check tab availability
    BOOL    isTabAvailable;
    isTabAvailable = [defaults boolForKey:SRTabEnableTabbedBrowsing];
    if (!isTabAvailable) {
        return;
    }
    
    // Check number of items
    if ([menu numberOfItems] == 0) {
        return;
    }
    
    // Create 'Open in Tabs' menu item
    id<NSMenuItem>  openInTabsItem;
    NSMenuItem*     copiedItem;
    openInTabsItem = [[SRContextMenu bookmarkContextMenu] 
            itemWithTag:SROpenBookmarkInTabsTag];
    copiedItem = [[NSMenuItem alloc] initWithTitle:[openInTabsItem title] 
            action:[openInTabsItem action] 
            keyEquivalent:[openInTabsItem keyEquivalent]];
    [copiedItem autorelease];
    [copiedItem setTag:[openInTabsItem tag]];
    [copiedItem setRepresentedObject:bookmark];
    
    [menu addItem:[NSMenuItem separatorItem]];
    [menu addItem:copiedItem];
    
    // Check submenu
    NSEnumerator*   enumerator;
    id<NSMenuItem>  item;
    enumerator = [[menu itemArray] objectEnumerator];
    while (item = [enumerator nextObject]) {
        if ([item hasSubmenu]) {
            // Add 'Open in Tabs' item in submenu
            [self _addOpenInTabsForMenu:[item submenu] 
                    ofBookmark:[item representedObject]];
        }
    }
}

- (void)mouseDown:(NSEvent*)event
{
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    BOOL   dropDownClick=NO; 
    
    // Get mouse location
    _clickPoint = [self convertPoint:[event locationInWindow] fromView:nil];
    
    // Check clicked point is in cell frame
    if (!NSPointInRect(_clickPoint, [self convertRect:[self frame] fromView:[self superview]])) {
        return;
    }
    
    // Set highlight
    [[self cell] setHighlighted:YES];
    
    // Check mouseDown in right image of FolderType
    if ([_bookmark isFolderType] && ![_bookmark isAutoTab]) {
        //12 is rough !!
        if([self frame].size.width-12 < _clickPoint.x){
            dropDownClick=YES;
        }
    }
    
    // Wait next event a moment
    BOOL    showContextMenu = NO;
    _willStartDragging = NO;
    if (!dropDownClick && ![self isClipMenu]) {
        NSEvent*		waitingEvent;
 		NSRect			offsetRect;
 		NSDate*			expireDate = [NSDate dateWithTimeIntervalSinceNow:0.3];
 		NSPoint			currentMouseLoc;
 		offsetRect = NSMakeRect(_clickPoint.x-2.0, _clickPoint.y-2.0, 5.0, 5.0);
 		while (1) {
 			waitingEvent = [NSApp nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask) 
 					untilDate:expireDate  
 					inMode:NSEventTrackingRunLoopMode  
 					dequeue:YES];
            // If timer is expired, stop tracking
            if (!waitingEvent) {
                showContextMenu = YES;
                break;
            }
 			// If mouse up is detected, show menu immediately
 			if ([waitingEvent type] == NSLeftMouseUp) {
 				[NSApp postEvent:waitingEvent atStart:YES];
                break;
 			}
 			// Permit mouse movement up to 3 pixels
 			else if ([waitingEvent type] == NSLeftMouseDragged) {
 				currentMouseLoc = [self convertPoint:[waitingEvent locationInWindow] fromView:nil];
 				if (!NSPointInRect(currentMouseLoc, offsetRect)) {
 					_willStartDragging = YES;
 					return;
 				}
 			}
 		}
    }
 	
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check favicon availability
    BOOL    isFaviconUsed, isFaviconUsedBookmarkBar;
    isFaviconUsed = [defaults boolForKey:SRIconUseFavicon];
    isFaviconUsedBookmarkBar = [defaults boolForKey:SRIconUseFaviconBookmarkBar];
    
    // Show clip menu
    if ([self isClipMenu]) {
        // Create menu
        NSMenu* menu;
        menu = SRCreateBookmarkMenuFromArray(_clipedBookmarks, YES, isFaviconUsed && isFaviconUsedBookmarkBar);
        
        // Check item num
        if ([menu numberOfItems] > 0) {
            // Move event location
            NSPoint     popupPoint;
            NSEvent*    popupEvent;
            popupPoint = [self convertPoint:NSMakePoint([self frame].size.width, 0) 
                    toView:nil];
            popupEvent = [NSEvent mouseEventWithType:[event type] 
                    location:popupPoint 
                    modifierFlags:[event modifierFlags] 
                    timestamp:[event timestamp] 
                    windowNumber:[event windowNumber] 
                    context:[event context] 
                    eventNumber:[event eventNumber] 
                    clickCount:[event clickCount] 
                    pressure:[event pressure]];
            
            // Register notification
            [center addObserver:self 
                    selector:@selector(_menuDidEndTracking:) 
                    name:NSMenuDidEndTrackingNotification 
                    object:menu];
            
            // Popup context menu
            [NSMenu popUpContextMenu:menu withEvent:popupEvent forView:self];
            
            return;
        }
    }
    
    // If bookmark is folder and not auto tab, show menu
    else if ([_bookmark isFolderType] && ![_bookmark isAutoTab]) {
        // Create menu
        NSMenu* menu;
        menu = SRCreateBookmarkMenu(_bookmark, YES, isFaviconUsed && isFaviconUsedBookmarkBar);
        
        // Check item num
        if ([menu numberOfItems] > 0) {
            // Add 'Open in tabs'
            [self _addOpenInTabsForMenu:menu ofBookmark:_bookmark];
            
            // Move event location
            NSPoint     popupPoint;
            NSEvent*    popupEvent;
            popupPoint = [self convertPoint:NSMakePoint(0, [self frame].size.height + 3) 
                    toView:nil];
            popupEvent = [NSEvent mouseEventWithType:[event type] 
                    location:popupPoint 
                    modifierFlags:[event modifierFlags] 
                    timestamp:[event timestamp] 
                    windowNumber:[event windowNumber] 
                    context:[event context] 
                    eventNumber:[event eventNumber] 
                    clickCount:[event clickCount] 
                    pressure:[event pressure]];
            
            // Register notification
            [center addObserver:self 
                    selector:@selector(_menuDidEndTracking:) 
                    name:NSMenuDidEndTrackingNotification 
                    object:menu];
            
            // Popup context menu
            [NSMenu popUpContextMenu:menu withEvent:popupEvent forView:self];
            
            return;
        }
    }
    
    // Show context menu
    else if (showContextMenu) {
        // Make NSRightMouseDown event
        NSEvent*    rightMouseEvent;
        rightMouseEvent = [NSEvent mouseEventWithType:NSRightMouseDown 
                location:[event locationInWindow] 
                modifierFlags:[event modifierFlags] 
                timestamp:[event timestamp] 
                windowNumber:[event windowNumber] 
                context:[event context] 
                eventNumber:[event eventNumber] 
                clickCount:[event clickCount] 
                pressure:[event pressure]];
        
        // Show context menu
        [self rightMouseDown:rightMouseEvent];
        
        return;
    }
    
    _willStartDragging = NO;
    [super mouseDown:event];
}

- (void)rightMouseDown:(NSEvent*)event
{
	// Set highlight
	[[self cell] setHighlighted:YES];
	
	[super rightMouseDown:event];
	
	// Clear highlight
	[[self cell] setHighlighted:NO];
}

- (void)_menuDidEndTracking:(NSNotification*)notification
{
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    
    // Remove notification
    [center removeObserver:self 
            name:NSMenuDidEndTrackingNotification 
            object:[notification object]];
    
    // Clear highlight
    [[self cell] setHighlighted:NO];
}

- (void)mouseDragged:(NSEvent*)event
{
    if (!_willStartDragging) {
        [super mouseDragged:event];
        return;
    }
    // Clear flag
    _willStartDragging = NO;
    
    //
    // Start dragging
    //
    
    // Get cell and its size
    NSCell*     cell;
    NSSize      cellSize;
    cell = [self cell];
    cellSize = [cell cellSize];
    
    // Create drag image
    NSImage*    dragImage;
    dragImage = [[NSImage alloc] initWithSize:cellSize];
    [dragImage autorelease];
    [dragImage setFlipped:YES];
    [dragImage lockFocus];
    [cell drawInteriorWithFrame:NSMakeRect(0, 0, cellSize.width, cellSize.height) 
            inView:self];
    [dragImage unlockFocus];
    [dragImage setFlipped:NO];
    
    NSImage*    dissolvedImage;
    dissolvedImage = [[NSImage alloc] initWithSize:cellSize];
    [dissolvedImage autorelease];
    [dissolvedImage lockFocus];
    [dragImage dissolveToPoint:NSZeroPoint fraction:0.7];
    [dissolvedImage unlockFocus];
    
    // Write bookmark to pasteboard
    NSPasteboard*   pboard;
    pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
    SRWriteBookmarksToPasteboard([NSArray arrayWithObject:_bookmark], pboard);
    
    // Retain itself to avoid deallocaation till drag end
    [self retain];
    
    // Start dragging
    NSPoint startAt;
    startAt = [self convertPoint:[event locationInWindow] fromView:nil];
    startAt.x -= _clickPoint.x;
    startAt.y += _clickPoint.y;
#if 0
    if ([self isFlipped]) {
        startAt.y = [self frame].size.height - startAt.y;
    }
#endif
    
    [self dragImage:dissolvedImage 
            at:startAt 
            offset:NSZeroSize 
            event:event 
            pasteboard:pboard 
            source:self 
            slideBack:YES];
 	
    // Clear highlight
 	[[self cell] setHighlighted:NO];
    
    // Release itself
    [self release];
}

- (NSMenu*)menuForEvent:(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;
    
    // Check tab availability
    BOOL    enableTabbedBrowsing, selectNewTabs;
    enableTabbedBrowsing = [defaults boolForKey:SRTabEnableTabbedBrowsing];
    selectNewTabs = [defaults boolForKey:SRTabSelectNewTabs];
    
    // Create tag array
    NSMutableArray* tags;
    tags = [NSMutableArray array];
    
    // Case of HTML bookmark
    if (![_bookmark isFolderType]) {
        // Create tag array
        [tags addObject:[NSNumber numberWithInt:SROpenBookmarkTag]];
        
        if (enableTabbedBrowsing) {
            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]];
            }
        }
        else {
            [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInNewWindowTag]];
        }
        
        if ([_bookmark isMutable]) {
            [tags addObject:[NSNumber numberWithInt:SRDeleteBookmarkTag]];
        }
    }
    // Case of bookmark folder
    else {
        // Create tag array
        [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInTabsTag]];
        if ([_bookmark isMutable]) {
            [tags addObject:[NSNumber numberWithInt:SRDeleteBookmarkTag]];
        }
    }
    
    // Create menu
    NSMenu* menu = nil;
    if ([tags count] > 0) {
        // Make context menu
        menu = [SRContextMenu copyMenuFrom:[SRContextMenu bookmarkContextMenu] 
                ofTags:tags 
                target:nil];
        
        // Set represented object
        [[menu itemArray] makeObjectsPerformSelector:@selector(setRepresentedObject:) 
                withObject:self];
    }
    
    return menu;
}

#pragma mark -
//--------------------------------------------------------------//
// NSDraggingSource
//--------------------------------------------------------------//

- (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)isLocal
{
    return NSDragOperationMove | NSDragOperationCopy | NSDragOperationDelete;
}

- (void)draggedImage:(NSImage*)image 
        endedAt:(NSPoint)aPoint 
        operation:(NSDragOperation)operation
{
    // For moving and deletion
    if (operation == NSDragOperationMove || operation == NSDragOperationDelete) {
        // Get parent
        SRBookmark* parent;
        parent = [[SRBookmarkStorage sharedInstance] parentOfBookmark:_bookmark];
        if (!parent || ![parent isMutable]) {
            return;
        }
        
        // Poof Animation
        if (operation == NSDragOperationDelete) {
            NSPoint centerLocation;
            centerLocation = [[self window] convertBaseToScreen:
                    [self convertPoint:[self bounds].origin toView:nil]];
            centerLocation.x += 16;
            NSShowAnimationEffect(NSAnimationEffectPoof,centerLocation, NSMakeSize(48, 48), nil, nil, nil);
        }
        
        // Remove bookmark
        [parent removeChild:_bookmark];
    }
}

@end
