// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view.h"

#include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h"
#include "chrome/browser/metrics/user_metrics.h"
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h"
#import "chrome/browser/ui/cocoa/themed_window.h"
#import "chrome/browser/ui/cocoa/view_id_util.h"
#import "chrome/browser/themes/theme_service.h"
#import "third_party/mozilla/NSPasteboard+Utils.h"

@interface BookmarkBarView (Private)
- (void)themeDidChangeNotification:(NSNotification*)aNotification;
- (void)updateTheme:(ui::ThemeProvider*)themeProvider;
@end

@implementation BookmarkBarView

@synthesize dropIndicatorShown = dropIndicatorShown_;
@synthesize dropIndicatorPosition = dropIndicatorPosition_;
@synthesize noItemContainer = noItemContainer_;


- (void)dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  // This probably isn't strictly necessary, but can't hurt.
  [self unregisterDraggedTypes];
  [super dealloc];

  // To be clear, our controller_ is an IBOutlet and owns us, so we
  // don't deallocate it explicitly.  It is owned by the browser
  // window controller, so gets deleted with a browser window is
  // closed.
}

- (void)awakeFromNib {
  NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
  [defaultCenter addObserver:self
                    selector:@selector(themeDidChangeNotification:)
                        name:kBrowserThemeDidChangeNotification
                      object:nil];

  DCHECK(controller_) << "Expected this to be hooked up via Interface Builder";
  NSArray* types = [NSArray arrayWithObjects:
                    NSStringPboardType,
                    NSHTMLPboardType,
                    NSURLPboardType,
                    kBookmarkButtonDragType,
                    kBookmarkDictionaryListPboardType,
                    nil];
  [self registerForDraggedTypes:types];
}

// We need the theme to color the bookmark buttons properly.  But our
// controller desn't have access to it until it's placed in the view
// hierarchy.  This is the spot where we close the loop.
- (void)viewWillMoveToWindow:(NSWindow*)window {
  ui::ThemeProvider* themeProvider = [window themeProvider];
  [self updateTheme:themeProvider];
  [controller_ updateTheme:themeProvider];
}

- (void)viewDidMoveToWindow {
  [controller_ viewDidMoveToWindow];
}

// Called after the current theme has changed.
- (void)themeDidChangeNotification:(NSNotification*)aNotification {
  ui::ThemeProvider* themeProvider =
      static_cast<ThemeService*>([[aNotification object] pointerValue]);
  [self updateTheme:themeProvider];
}

// Adapt appearance to the current theme. Called after theme changes and before
// this is shown for the first time.
- (void)updateTheme:(ui::ThemeProvider*)themeProvider {
  if (!themeProvider)
    return;

  NSColor* color =
      themeProvider->GetNSColor(ThemeService::COLOR_BOOKMARK_TEXT,
                                true);
  [noItemTextfield_ setTextColor:color];
}

// Mouse down events on the bookmark bar should not allow dragging the parent
// window around.
- (BOOL)mouseDownCanMoveWindow {
  return NO;
}

-(NSTextField*)noItemTextfield {
  return noItemTextfield_;
}

-(NSButton*)importBookmarksButton {
  return importBookmarksButton_;
}

- (BookmarkBarController*)controller {
  return controller_;
}

// Internal method, needs to be called whenever a change has been made to
// dropIndicatorShown_ or dropIndicatorPosition_ so it can get the controller
// to reflect the change by moving buttons around.
-(void)dropIndicatorChanged {
  if (dropIndicatorShown_)
    [controller_ setDropInsertionPos:dropIndicatorPosition_];
  else
    [controller_ clearDropInsertionPos];
}

-(void)drawRect:(NSRect)dirtyRect {
  [super drawRect:dirtyRect];
}

// Shim function to assist in unit testing.
- (BOOL)dragClipboardContainsBookmarks {
  return bookmark_pasteboard_helper_mac::DragClipboardContainsBookmarks();
}

// NSDraggingDestination methods

- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
  if (![controller_ draggingAllowed:info])
    return NSDragOperationNone;
  if ([[info draggingPasteboard] dataForType:kBookmarkButtonDragType] ||
      [self dragClipboardContainsBookmarks] ||
      [[info draggingPasteboard] containsURLData]) {
    // We only show the drop indicator if we're not in a position to
    // perform a hover-open since it doesn't make sense to do both.
    BOOL showIt = [controller_ shouldShowIndicatorShownForPoint:
                   [info draggingLocation]];
    if (!showIt) {
      if (dropIndicatorShown_) {
        dropIndicatorShown_ = NO;
        [self dropIndicatorChanged];
      }
    } else {
      CGFloat x =
      [controller_ indicatorPosForDragToPoint:[info draggingLocation]];
      // Need an update if the indicator wasn't previously shown or if it has
      // moved.
      if (!dropIndicatorShown_ || dropIndicatorPosition_ != x) {
        dropIndicatorShown_ = YES;
        dropIndicatorPosition_ = x;
        [self dropIndicatorChanged];
      }
    }

    [controller_ draggingEntered:info];  // allow hover-open to work.
    return [info draggingSource] ? NSDragOperationMove : NSDragOperationCopy;
  }
  return NSDragOperationNone;
}

- (void)draggingExited:(id<NSDraggingInfo>)info {
  // Regardless of the type of dragging which ended, we need to get rid of the
  // drop indicator if one was shown.
  if (dropIndicatorShown_) {
    dropIndicatorShown_ = NO;
    [self dropIndicatorChanged];
  }
}

- (void)draggingEnded:(id<NSDraggingInfo>)info {
  [[BookmarkButton draggedButton] setHidden:NO];
  if (dropIndicatorShown_) {
    dropIndicatorShown_ = NO;
    [self dropIndicatorChanged];
  }
  [controller_ draggingEnded:info];
}

- (BOOL)wantsPeriodicDraggingUpdates {
  return YES;
}

- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info {
  // For now it's the same as draggingEntered:.
  return [self draggingEntered:info];
}

- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info {
  return YES;
}

// Implement NSDraggingDestination protocol method
// performDragOperation: for URLs.
- (BOOL)performDragOperationForURL:(id<NSDraggingInfo>)info {
  NSPasteboard* pboard = [info draggingPasteboard];
  DCHECK([pboard containsURLData]);

  NSArray* urls = nil;
  NSArray* titles = nil;
  [pboard getURLs:&urls andTitles:&titles convertingFilenames:YES];

  return [controller_ addURLs:urls
                   withTitles:titles
                           at:[info draggingLocation]];
}

// Implement NSDraggingDestination protocol method
// performDragOperation: for bookmark buttons.
- (BOOL)performDragOperationForBookmarkButton:(id<NSDraggingInfo>)info {
  BOOL rtn = NO;
  NSData* data = [[info draggingPasteboard]
                  dataForType:kBookmarkButtonDragType];
  // [info draggingSource] is nil if not the same application.
  if (data && [info draggingSource]) {
    BookmarkButton* button = nil;
    [data getBytes:&button length:sizeof(button)];
    BOOL copy = !([info draggingSourceOperationMask] & NSDragOperationMove);
    rtn = [controller_ dragButton:button
                               to:[info draggingLocation]
                             copy:copy];
    UserMetrics::RecordAction(UserMetricsAction("BookmarkBar_DragEnd"));
  }
  return rtn;
}

- (BOOL)performDragOperation:(id<NSDraggingInfo>)info {
  if ([controller_ dragBookmarkData:info])
    return YES;
  NSPasteboard* pboard = [info draggingPasteboard];
  if ([pboard dataForType:kBookmarkButtonDragType]) {
    if ([self performDragOperationForBookmarkButton:info])
      return YES;
    // Fall through....
  }
  if ([pboard containsURLData]) {
    if ([self performDragOperationForURL:info])
      return YES;
  }
  return NO;
}

- (void)setController:(id)controller {
  controller_ = controller;
}

- (ViewID)viewID {
  return VIEW_ID_BOOKMARK_BAR;
}

@end  // @implementation BookmarkBarView